Access Checkbox value update does not trigger AfterUpdate event - vba

I could not find an answer to my specific question, but I am also relatively new to VBA, so maybe I just didn't look for the right terminology.
What I have, is a form that contains a bunch of comboboxes. These are arranged in rows and columns like so:
cboThingOne1 cboThingTwo1 cboThingThree1
cboThingOne2 cboThingTwo2 cboThingThree2
... ... ...
cboThingOne15 cboThingTwo15 cboThingThree15
I have set cboThingOne to show a selection of items (e.g. department1, department2, department3,...) from an SQL database.
cboThingTwo and cboThingThree are also set to a certain Rowsource (containing things like Apple, Bananas and Cherries).
What I would like to happen is, as soon as I change the value in cboThingOne1 to department3, to fill all of cboThingOne checkboxes with the same value.
This is working. I am using the AfterUpdate event of each cbo, to call a function, which iterates over all items in Me.Controls, checks their name + 1 (in this case cboThingOne2) and sets the value of this control to department3.
Private Sub fun_fill_cbo_with_value(FieldName As String, FieldValue As String)
Dim i as Integer
'This function returns the name without number
my_fieldname = Striptext(FieldName)
'gets me the number of the field I am working with (i.e. cboThingOne1 --> 1)
i = Int(Replace(FieldName, my_fieldname, ""))
i = i+1
cbo_name = my_fieldname + CStr(i)
For Each my_control In Me.Controls
If my_control.Name = my_fieldname Then
my_control.Value = FieldValue
End If
Next my_control
End Sub
When the value for cboThingOne2 is changed, I expected the AfterUpdate event of this cbo to be triggered, which apparently does not happen.
In this AfterUpdate event the Rowsource for cboThingTwo2 should be updated.
Private Sub cboThingOne2_AfterUpdate()
'To update the cbo in the row below
Call fun_fill_cbo_with_value(cboThingOne2.Name, cbo_ThingOne2.value)
Me.cboThingTwo2.RowSource = "somedifferentqueryhere"
Me.cboThingThree2.Rowsource = "yetanotherquery"
What I expected, was that updating the value in fun_fill_cbo_with_value would update the value of the cbo + 1 (which works) and also triggers the AfterUpdate event (which does not happen).
Furthermore, because I am calling fun_fill_cbo_with_value in the AfterUpdate event, I expected the column to fill to the end, rather than just the cbo below (as it does right now).
I can fill the entire column of cbos, by adding an additional loop in fun_fill_cbo_with_value which just goes from 1 to 15.
That still does not help me with updating the rowsource of cboThingTwo and cboThingThree.
I hope you can help and if you need more information, I am glad to provide it.

I expected the AfterUpdate event of this cbo to be triggered, which
apparently does not happen.
It is triggered by a user action only.
Call the AfterUpdate event function itself from your code (bad practice); or (allows for better organised code) let the AfterUpdate event call a separate function which your other code can call as well.

Related

How do I properly use if/then for a bolean value in order to make an object visible?

I want to display an endless access form. For each data set there is a yes/no value (PA). If yes, then a hidden object should be displayed. It seems very straight forward, but it doesn't work.
I have tried by changing the value of PA to 1, 0, -1. Either nothing happens, or the object will be displayed for all data sets.
The object is defined as hidden in the form.
Private sub form_current()
If PA.value = true Then
me.object.visible = True
End if
End Sub
I would be very happy for some advice. /LP
Handle the control's Change event, and then you can assign to its value:
Private Sub PA_Change()
Me.object.Visible = PA.Value ' TODO: give 'object' an actual name
End Sub
Find PA in the top-left codepane dropdown, then select the Change event in the top-right code pane dropdown if it's not automatically selected - the VBE will generate the event handler procedure for you.
As the object is unbound, you can't do this. When unbound, it will either be visible or not - for all records.
One workaround is to move the control to a tiny subform having a master/child relation to the main form.

How do I get the updated value of a textbox in VBA Access in a custom Sub?

This is for VBA Access 2003
I've got a textbox I want to use as a filter for a list box rowsource command. I also have a checkbox which adds another filter for the same rowsource command. I've only programmed in C# and I'm trying to write a single Sub which will simply set the RowSource regardless of if my textbox filter is changed or if my checkbox filter is changed. However, my textbox is giving me problems.
If my checkbox filter changes and I run my method the textbox.Text throws an error saying that it must have focus - Text is null. If I do a null check on that property it throws an error saying the control must have focus.
I've used the .Value property, but for whatever reason it doesn't update to the newer values.
My current attempt:
If Me.txtClientFilter.Text = Null Then ' Error 2185
filter = Me.txtClientFilter.Value
Else
filter = Me.txtClientFilter.Text
End If
Should I
Manually add focus then remove it everytime I want to check a
control?
Duplicate my code in each control's event Sub?
Manually set
the .Value property when the change happens?
I fixed my problem with some code which I'll show below. I don't know what was happening behind the scenes, but the .Value was not getting updated with the .Text value. I decided to set it explicitly, which then caused the entire text box value to be selected.
I ended up with the following code which explicitly sets the .Value of the control and also reset the cursor to the end of the text in the control. Thanks to some guy named Brent Spalding here for the cursor code.
Private Sub txtClientFilter_Change()
Me.txtClientFilter.Value = Me.txtClientFilter.Text
ProcessFilter
txtClientFilter.SelStart = Len(Me.txtClientFilter.Text)
txtClientFilter.SelLength = 0
End Sub

MS Access Can't go to specified record because of another control's VB where clause

I have a lookup listbox which is programmed to allow the user to find a specific record/help topic from the list and view it. Now when the list box is used the where clause locks in the record and the first, previous, next, last buttons freeze up and you cannot use them to go to a record. Is there a way to free up the functionality of the buttons to navigate through the records along with the where clause to select.
Here is the code that operates the listbox selections:
Private Sub List35_AfterUpdate()
Dim myTopic As String
myTopic = "Select * from FormsHelpTable where ([ID] = " & Me.List35 & ")"
Me.Form.RecordSource = myTopic
Me.Comment.Requery
End Sub
I believe since the where clause locks in the selection in the box it does not allow navigation from other controls to interfere. What might be a way around this?
You get the runtime error:
You can't go to specified record.
It appears not to be reading the other record in the source table named Help once it updates using the listbox.
Instead of changing the recordset (this is the 'pool' of records which the form could display), you just need to go to the record the user selects from the listbox.
Method 1 (probably the easiest way if your listbox is in the same order as the records of your form)
Use this code:
Private Sub lstSelect_AfterUpdate()
DoCmd.GoToRecord acDataForm, "HelpForm", acGoTo, Me.lstSelect.ListIndex + 1
End Sub
You need to ensure that:
The recordset of the form is ordered the same as the listbox. So, for example, you could order both by ID, or by Title.
Note that the +1 comes from the fact that the ListIndex starts at 0, whereas the record indexes start at 1.
Method 2
Ensure each record's Title is unique (set No Duplicates on this field).
Change your listbox to return the Title of the record, rather than it's ID.
Then use this:
Private Sub lstSelect_AfterUpdate()
Me.Title.SetFocus
DoCmd.FindRecord Me.lstSelect
Me.lstSelect.SetFocus
End Sub
Things to note:
It works by searching the field with focus for the string specified. That's why we have to SetFocus on the Title textbox on our form.
We could use ID instead, (which would mean we could have duplicate titles if we wanted), but then we would have to have an ID control on the form to SetFocus to. You can't hide this control either, because it needs to have focus whilst using FindRecord.
Update: Method 1 with reverse-selection
Add an Event Procedure in the Form_Current event, with this code. Then update the code in the lstSelect_AfterUpdate procedure as shown after.
Private Sub Form_Current()
Me.lstSelect = Me.lstSelect.Column(0, Form.CurrentRecord - 1)
End Sub
Note that, depending on how your lstSelect is set up, it may be Column(1, Form.CurrentRecord - 1) instead. Read on for details!
Private Sub lstSelect_AfterUpdate()
DoCmd.GoToRecord acDataForm, "HelpForm", acGoTo, Me.lstSelect.ListIndex + 1
Me.lstSelect = Me.lstSelect.Column(0, Form.CurrentRecord - 1)
End Sub
Explanation of new lines:
The Form_Current event fires every time we go to a new record. We need to look at the index of the record (ie. the position of it in the recordset), which we get by using Form.CurrentRecord. We then want to make the listbox select that record. However, we can't use me.lstSelect.ListIndex as before, because that is a read-only property (we can access it to read it, but we can't set it).
Therefore, we use me.lstSelect.Column(colNum,rowNum) instead, where we specify a column number and a row number. If your listbox has two columns (eg. ID and Title), we want to choose the second column. The index starts at 0, so we would use a value of 1. If, like my lstSelect, you only have one column (Title) then we use 0. Note: it doesn't matter if a column is hidden (ie. has width 0). It still 'counts'.
The row number is Form.CurrentRecord - 1. Remember that the forms recordset index starts at 1, but the index of our listbox starts at 0; hence the - 1.
So why do we need a duplicate of this new row in the AfterUpdate event? Try and comment it out to see what happens if we don't put it in. It's has to do with the Form_Current event firing after we use the listbox.
I fixed this issue with a union clause in the SQL lookup code. The UNION ALL clause and the following union on the table used in the other part had allowed all the records to be used.

MS Access Force Update on Combobox

How do I force an update when the ComboBox value is changed in code. Below is piece of code I have tried but does not seem to work
If (Not Mid(sCode, 1, 2) = ddlLevelID1) Then
ddlLevelID1 = Mid(sCode, 1, 2) 'force change/force AFTER_UPDATE event to run.
End If
Assuming ddlLevelID1 is the ComboBox:
ddlLevelID1.value = foo
will change the value. I do not believe you can link a value displayed in a ComboBox to a variable value without pushing changes up to the userform after the value is changed.
Regarding the AfterUpdate method, from msdn:
Changing data in a control by using Visual Basic or a macro containing
the SetValue action doesn't trigger these events for the control.
However, if you then move to another record or save the record, the
form's AfterUpdate event does occur.
http://msdn.microsoft.com/en-us/library/office/bb238392(v=office.12).aspx

Is there a way to allow only unique values in a column of an Infragistics UltraWinGrid?

I'm using an Infragistics UltraWinGrid with a column of drop-down boxes. I don't want the user to be able to select the same value for multiple rows in that column. Is there a simple (or heck, I'd settle for advanced) way to do this?
I'm using VB.NET
-EDIT-
I tried setting a filter on the data source for the drop-down box. But when I did that, the values in the other boxes in that column started disappearing (not the values themselves, but the descriptions that they were supposed to represent, so instead of reading "Information", it just said "1"). All of the cells in a column refer to the same combo box, so if you filter out a value from the data source for one of them, you filter it out for all of them.
I'm currently trying to catch a CellChange event and check against all other currently-used values. If it was already used, I would put up a message saying as much and revert back to the old value. However, the value comes back as the previously-saved one, so... not helpful. About to see if I can use the "text" property.
Since you're using Infragistics, you could use an UltraDropDown which is bound to a DataTable (or something similiar) which you can add a "Selected" column in addition to a column holding the values you want to show.
As each value is selected (via AfterCellUpdate or AfterCellListCloseUp for instance), you could update the "Selected" column in that data source and use a column filter to only show items which haven't been marked as selected. That way as items are selected or removed, the contents of the drop-down would be automatically updated.
To clear the selected flag from the old value, you can use the BeforeCellUpdate event to access the cell's current value then perform a lookup on the data source bound to the UltraDropDown using that value to clear the flag.
Solved it:
The trick was to use BeforeCellUpdate whose BeforeCellUpdateEventArgs has a "NewValue" and a "Cancel" member. I just look through all of the items in the column to see if any of them match the new value. If one does, I notify the user and cancel the operation.
And that's it. Here's the code:
Private Sub myUltraWinGrid_BeforeCellUpdate(ByVal sender As Object, ByVal e As Infragistics.Win.UltraWinGrid.BeforeCellUpdateEventArgs) Handles myUltraWinGrid.BeforeCellUpdate
If e.Cell.Column.Key = "myColumn" Then
Dim newValue As Integer = CInt(e.NewValue)
For Each row As Infragistics.Win.UltraWinGrid.UltraGridRow In myUltraWinGrid.Rows
If row.Cells("myColumn") IsNot e.Cell _ 'So I'm not checking against the current cell
AndAlso CInt(row.Cells("myColumn").Value) = newValue Then
MsgBox("That value has already been used in this column")
e.Cancel = True
End If
Next
End If
End Sub