subform combobox rowsource update - how update drop down list - ms-access

I have a treeview control on an MS Access form. A subform combo box control's list of values depends on the node selected in the treeview on the main form.
I am trying to achieve refresh of the combo box drop down list contents on the subform as follows:
Public Sub TreeView1_nodeClick(ByVal node As Object)
subForm.Controls("Bid").RowSource = "... newquery depending on tree node values ..."
subForm.Controls("Bid").Requery
End Sub
But oddly enough, this does not update the list of values.
A second click on the same node does update the list to the expected values; when another node is clicked the list again is wrong (it contains the list related to the next to last assigned rowsource, instead of related to the last one assigned).
Is there some delay in activating the combo box' rowsource?
What is the solution for this obnoxious problem?

OK found a solution for this myself - I don't particularly like it (*), but it does work, so I regard it as a temp workaround, until somebody else provides a solution using the royal route (setting rowsource and somehow make this have the desired effect).
I checked msdn site and found that it is also possible to use a self-defined function for the combobox list population. The easy part of this is the specification of the fact that Access will have to use that function: just enter the function name in the row Source Type property of the combo box, just the plain function name, without any = in front or () behind it.
Now for the less easy part - MS requires a specific format for the function and a specific content. Mine looks as follows:
Private Function customFuncName(fld As Control, id As Variant, row As Variant, col As Variant, code As Variant) As Variant
Dim rs As Recordset
Set rs = CurrentDb.OpenRecordset(... new query ...)
Select Case code
Case acLBInitialize
customFuncName = True
Case acLBOpen
customFuncName = 1
Case acLBGetRowCount
customFuncName = rs.RecordCount
Case acLBGetColumnWidth
customFuncName = -1
Case acLBGetValue
customFuncName = rs.GetRows(rs.RecordCount)(col, row)
End Select
End Function
See msdn.microsoft.com rowSourceType property and the link specific function code arguments on it.
(*) because of the obvious recordset overhead causing performance to drop - but since the list I show is max 40 rows it works fine for me

Related

Access VBA - Turn off aggregation when opening form/sub-form

What I'm trying to do is, whenever a user opens a form (and the sub-form that opens by default), I want to search through all the columns (controls?) on the form, check to see if they are currently set to aggregate (sum, count etc.) with Access' built-in Totals row, and if so, set them to not aggregate.
The reason for this is there are several millions records that are stored, so when someone queries it down to 3-4 and turns on Sum, then closes it, when the next person opens it, it tries to sum millions of numbers and freezes up. The form displays the queried results from a table which is populated via SQL (I think, if that sentence makes sense). Here's what I have so far:
Private Sub Form_Load()
'this form_load is in the UserApxSub sub-form, for reference
Call De_Aggregate
End Sub
Private Sub De_Aggregate()
Dim frm As Form, con As Control
Set frm = Forms!UserAPX!UserApxSub.Form!
For Each con In frm.Controls
If con.ControlType = acTextBox Then
If con.Properties("AggregateType").Value <> -1 Then
'crashes on following line
con.Properties("AggregateType").Value = -1
End If
End If
Next con
End Sub
I have not so much experience in Access VBA (usually work in Excel VBA) so please forgive me if I'm entirely off the mark here. The command con.Properties("AggregateType").Value = -1 doesn't throw an error, but Access just straight-up crashes when reaching that line specifically.
I've tried a number of variations in the syntax with no success, and I've also tried looping through other elements of the file (tabledefs, querydefs, recordsets, etc.) as well to see if I'm trying to change the wrong value, but the controls on this subform are the only things in the entire .mdb file that results when I search for elements with the AggregateType property.
I switched out the line that errors with Debug.Print con.Name & " - " & con.Properties("AggregateType").Value and I can check, have nothing return anything other than -1, turn on aggregation in some column manually, and have it return the correct result (0 for sum for example), so I think I'm looking in the right place, just missing some key factor.
I've been working on this for a couple weeks with no success. Any way to fix what I have or point me toward the right direction would be greatly appreciated!
This is not necessarily the answer but I don't have enough reputation
to give a "comment"...
I tried your scenario and verified can change the property value as you are however I did not iterate through all controls and simply used an onDoubleClick event on a column to simulate.
I would suggest trying to fire your sub with Form_Open or Form_Current to see if the property is getting reset after your code has been called for some reason.
UPDATE:
You are referencing the "Subform" Object of your main Form:
Set frm = Forms!UserAPX!UserApxSub.Form!
Try referencing the actual UserApxSub FORM explicitly.
Something like Set frm = Forms!UserApxSub! (assuming UserApxSub is the name of the form)
then stick in the Form_Open of your main form:
Private Sub Form_Open(Cancel As Integer)
'// the following would set a single control only. You can add your loop through all controls
Me!{your control name}.Properties("AggregateType").Value = -1 '// set control in your main form
Form_UserApxSub!{your control name}.Properties("AggregateType").Value = -1 '// set control in your "sub" form
End Sub

VB.NET WPF How to get the column value from datagrid?

So I am not quite familiar with WPF yet and in WinForms, you can have CellDoubleClick event which I can get the value like
row.Cells("ID").Value.ToString()
But in WPF, row is not a member of datagrid. So how can I get the value of the selected ID if user just double click on the row?
Here is a picture to clarify my question. The record shown is from a datatable. When multiple row of records is shown, and the user double click on any of the row, how do I get the value of the selected row ID? To get the selectedIndex is easy but I am lost trying to figure out how do I get the value of the ID.
-------UPDATE-------
So in WinForms, I would obtain the ID like this but not sure how in WPF.
Private Sub DataGridView1_CellDoubleClick(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.CellDoubleClick
If e.RowIndex >= 0 Then
Dim row As New DataGridViewRow
row = Me.DataGridView1.Rows(e.RowIndex)
THE_ID = row.Cells("ID").Value.ToString() 'The ID is saved
End If
End Sub
In WPF data grids, the grid itself should never be used to acquire data. But instead, use the information you DO know about the grid. The selected row index, or "SelectedIndex" is what you should use to cross-reference your bound data source.
For example, let's assume that you had some DataTable and bound it's view to the DataGrid, you might have something that looks like this:
Dim View As DataView = TryCast(DataGrid1.ItemsSource, DataView)
If View IsNot Nothing Then
Dim ViewRow As DataRowView = View.Item(DataGrid1.SelectedIndex)
Dim ColumnValue As Object = ViewRow.Item("ID") 'or ViewRow.Item(0) for positional value.
'do something with ColumnValue here.
End If
That should work in the event that you are binding a DataTable's view to the grid, but remember the grid can take any IEnumerable. So your question really depends heavily on what object is your ItemsSource, and how that object implements IEnumerable.
hope that helps!

Requery only one record in a datasheet view form (MS Access)

I have a rather simple looking problem but it turned out to be more complicated than I thought.
I have a field (column) in my subForm which is a ComboBox.
I have a field (column) in another subForm by which I would like to filter this comboBox.
Basically, the comboBox before filtering has some 600 records, too many to scroll by casual user. I created a simple subForm whose field is linked to a mainForm and this works perfectly (ie. the selected record-field-ID is displayed on mainForm).
Now what I want is that this comboBox is filtered by this record (ie. only showing relevant fields). The problem is that if I simply Requery it with this given filter, the other fields show up blank.
I want this filter to apply only to NEW RECORDS comboBox, not the whole datasheet view.
What I did is:
Private Sub Sekacie_Operacie_GotFocus()
Dim SQL As String
SQL = CurrentDb.QueryDefs("qrySekacie_Operacie").SQL
Me.Sekacie_Operacie.RowSource = Replace(SQL, ";", "") & " WHERE Sekacie_Operacie.Operacia_ID = " & Me.Parent.Form!txtOP_ID
Me.Sekacie_Operacie.Requery
'works kinda as intended
End Sub
Private Sub Form_AfterInsert()
Me.Sekacie_Operacie.RowSource = CurrentDb.QueryDefs("qrySekacie_Operacie").SQL
Me.Refresh
End Sub
And when I select the record in my filter subForm:
Private Sub Form_Current()
Dim SQL As String
SQL = CurrentDb.QueryDefs("qrySekacie_Operacie").SQL
With Me.Parent.Form.subSekacie_Operacie_Modelu
.Form!Sekacie_Operacie.RowSource = SQL
.Form.Refresh
End With
End Sub
However, this workaround still shows "blank" records sometimes (I have to refresh the form by clicking different record) and I find it strange I had to go all the way to do this. Is there no simpler way of accomplishing this?
A couple of immediate things:
No need to refresh the whole sub form, just refresh the control.
Private Sub Form_AfterInsert()
' Me.Refresh ' replace this line with the following line
Me.Sekacie_Operacie.Refresh ' or is it Requery I can't remember
End Sub
The following code can be improved
Private Sub Form_Current()
Dim SQL As String
SQL = CurrentDb.QueryDefs("qrySekacie_Operacie").SQL
With Me.Parent.Form.subSekacie_Operacie_Modelu
.Form!Sekacie_Operacie.RowSource = SQL
.Form.Refresh
End With
End Sub
' by using code like this
Private Sub Form_Current()
Dim SQL As String
SQL = CurrentDb.QueryDefs("qrySekacie_Operacie").SQL
me.Sekacie_Operacie.RowSource = SQL
' I think the above line will cause the combo to refresh
' anyway (in some version of accees it might not,
' but, if needed add the following line
' me.Sekacie_Operacie.refresh
End Sub
The following line has the potential to produce and error
(perhaps when the main form is a new record)
WHERE Sekacie_Operacie.Operacia_ID = " & Me.Parent.Form!txtOP_ID
replace it with
WHERE Sekacie_Operacie.Operacia_ID = " & nz(Me.Parent.Form!txtOP_ID,0)
Note that adding such code will stop the subform from working independently of the main form and will require it to have a parent form.
You could get around this by using:
if not me.parent is nothing then
WHERE Sekacie_Operacie.Operacia_ID = " & nz(Me.Parent.Form!txtOP_ID,0)
end if
About your approach.
I find it a bit weird what you are doing. Consider this:
Say the query qrySekacie_Operacie returns 100 rows when run without any WHERE clause. When you are inserting a record into the subform you want to limit the combo by adding a WHERE clause that uses a value in the parent formMe.Parent.Form!txtOP_ID) , this might limit the number of rows displayed to 10.
It is worth noting that if any of the other rows in the subform contain value other than one of these 10 the combo will display a blank value. I think this is what you are experiencing.
My first question was why not use the on current event of the parent form to change the subform combo box SQL. It appears this is not what you need.
It appears that other values can exist in the dataset of the subform and that you only want certain value to be enterable in this subform but you want the subform to display any values that have been entered.
So that gets interesting. Here's what I would do:
Create a hidden combo control (with no control source) that is only visible when a new record is being entered. When this new combo is shown the other one is hidden.
Set up this new combo with the required SQL
use a before insert event to copy the value form the new combo to he old one.
Easy peezy?
Let me know ho you get on.
One other thing
I like to Ensure a subform encapsultes code that operates on it "private" controls.
As I general rule of thumb, where possible (which is 99% of the time), I like to try and keep subforms so that they will work as forms in their own right. They can be though of as an object, so if they work independently, it is an indication that their code has been encapsulated within their own code module.
So if the form has me.form.parent = nothing then the code behaves differently as it is a independent form.
So rather than the parent form code module having code that sets the SQL of the subforms combo, I would write a public sub in the subforms module (called SetComboSQL) that can be called (with the appropriate parameters. ie OPid ) to set the combo SQL.
To call the procedure in the parent form I would add
Private Sub Form_Current()
Me.MySubformControlName.Form.SetComboSQL(OPid:=Me.txtOP_ID)
End Sub
' note that intellisense won't pick up this proeprty BUT it will work. Use debugger to check it out.
I hope you find this tip useful (as it can be a massive headache saver. THE MOST BASIC OO PRINCIPAL is encapsulation. An object should own all the code that effects it's properties (and controls).

Access: Refer to text box from detail section of subreport from main report

I can successfully refer to the value in a text box in the subreport footer using [subreportName].[Report].[textBoxName]. However, I would like to refer to a text box in the detail section. Using the above just gives the last value it contained - I would like to refer to a specific one (by field, say).
Is this possible, or is there some workaround?
Update: This is what I have so far. fieldA is the name of the text box that I want to use to pick out the correct entry (the correct entry will have "keyString" in this text box), and fieldB contains the value I want to actually store.
In the OnFormat event in the subreport detail section:
Dim varToStore As Double
Private Sub Detail_Format(Cancel As Integer, FormatCount As Integer)
If Me![fieldA] = "keyString" Then
varToStore = Me![fieldB]
End If
End Sub
In a new Module:
Function getVariable(Name As String)
If Name = "varToStore" Then
getVariable = varToStore
Else
getVariable = -1
End If
End Function
In the Control Source of the text box in the main report:
=getVariable("varToStore")
I'm using the general function getVariable since there are a few variables from the detail section that I'm planning to store, so I thought it would be easier to just pass arguments to one function, rather than having a function for each.
If you look at a textbox in the detail section of a continuous form, it is really only one object, even if there are many rows on the screen.
If you change a property, you change all rows. If you read its value, you get the value of the current record.
With the detail section of a report, it's similar. You can't refer to the textbox of any row except the last after it has been printed.
But you can while it is being printed. The OnFormat property of the detail section is most probably the best event to use.
Something like
Private Sub Detailbereich_Format(Cancel As Integer, FormatCount As Integer)
If Me!Keyfield = 4711 Then
' do something specific with the textbox of this specific row
End If
End Sub
To create this sub, open the property sheet of the details section of the subreport.
On the Events tab, select Event procedure for the On Format event. Edit it ("..." button) and you're there.
You can assign the textbox value to a public variable to pass it to the main report. But depending on where/when in the main report you want to use it, it may be too late.
New answer for the Update.
I assume your code doesn't work?
That would be because getVariable("varToStore") is evaluated when the main report is opened, but varToStore is set later, when the subreport is formatted.
So you need a different approach. Don't try to fish your data from the subreport, get it with SQL when the main report is opened. Something like:
Report_Open()
Me.mainTextField.Value = DLookup("fieldB", "SubReportTable", "fieldA = 'keyString'")
mainTextField would be unbound then.
If "keyString" is actually constant, you could even use the DLookup as control source. But I guess it's a variable, so it's easier to construct the DLookup call in VBA.

Synchronizing record navigation control and record selection combo box

I have a form bound to a query, with controls bound to fields of the query.
I also have an unbound combobox, whose Row Source is the same query, used to select the current form record via its After Update event handler:
Private Sub Loc_cbo_AfterUpdate()
DoCmd.SearchForRecord , "", acFirst, "ID = " & Str(Nz(Loc_cbo, 0))
End Sub
The combobox has three columns:
the Bound Column, an invisible ID,
a location code,
a location name.
The combo's Row Source is SELECT * FROM Sites ORDER BY Loc DESC. (Originally, it was just Sites but since I have the form's Order By as Site.Loc DESC, I wanted the order to match.)
It works fine.
Of course, I can also navigate through the form’s records via its navigation control (First, Previous, Next, Last).
That, too, works.
However, I’d like the combobox to be synchronized with the current record as it changes via the navigation control.
I’ve tried to do it via the form’s Current event handler, three different ways.
First, to set the combobox’s Text value to match that of the corresponding textbox control
Private Sub Form_Current()
Loc_cbo.SetFocus
Loc_cbo.Text = Loc_txt ‘ ERROR
End Sub
but I get
Run-time error ‘2101’: The setting you entered isn’t valid for this
property.
Second, to set the combobox’s ListIndex value relative to the current record
Private Sub Form_Current()
Loc_cbo.SetFocus
Loc_cbo.ListIndex = CurrentRecord - 1 ‘ ERROR
End Sub
but I get either the same Run-time error ‘2101’ or
Run-time error ‘7777’: You’ve used the ListIndex property incorrectly.
in the same place.
Third, (thanks to HansUp's early answer) to set the combobox’s Value property:
Private Sub Form_Current()
Loc_cbo.Value = Loc_txt.Value
End Sub
This "works" in so far as there is no error message and, according to debug.prints, the value of Loc_cbo does change to match that of Loc_txt. However, the visible textbox-like portion of the combobox now appears to be empty/blank (nothing in it) always.
How can I get the record selector combobox to agree with the current record when the latter has been changed via the record navigator?
I cannot believe users will be happy having the two out of synch.
I also cannot believe I haven't found this problem described anywhere else!
Assign to the combo's .Value property to change its selection. For .Value, you don't need to SetFocus.
Private Sub Form_Current()
Me.Loc_cbo.Value = Me.Loc_txt.Value
End Sub
Note this suggestion assumes the combo's .Value is what you want to match to the text box value. Check to confirm you want the match between the combo and text box to be based on the combo's .Value instead of another of the combo's columns.
Post mortem:
The content of the text box was intended to match one of the combo's columns. However, since the combo's Bound Column contains numbers associated with that other column, Martin used a DLookup expression to fetch the number which corresponds to the text box content and assigned that number to the combo's .Value property:
Me.Loc_cbo.Value = DLookup("ID", "Sites", "Loc= '" & Me.Loc_txt.Value & "'")
Note a combo's .Value is the value in the Bound Column of its selected row.