Built in Arguments in Custom Event - vba

When I want to add an event to a form built in Microsoft Access 2000, I use the [Event Procedure] option. Some of my events are global functions I created in VBA so that I can just enter the function name in the event property. For instance, I have a back button on my forms that have this as their OnClick event property: =GoBack().
I want to be able to use the built-in arguments in my custom functions as the event property. For instance, the Form_Error event has two arguments: DataErr and Response. In my global =HandleFormError(), I want to be able to set the Response variable.
Is this possible, and if so, how would I be able to accomplish this?

I'd say your best bet is to do something like this
Private Sub Form_Error(DataErr As Integer, Response As Integer)
HandleFormError DataErr, Response
End Sub
and do all your logic in HandleFormError. There is no way to link Form_Error to any method other than Sub Form_Error(DataErr As Integer, Response As Integer). This is just a VBA restriction.
For events like Click you can put your functions in the expression builder (makes for easier copy/pasting) but are probably just as well off doing this in a regular Form_click style event.

Related

Listbox not displaying in UserForm

I'm trying to display a listbox in a UserForm from a separate sheet called "Fields". The problem is, the list will not display. It shows as a drop down arrow next to the cell, but not in the userform like I'm wanting.
Private Sub UserForm_Activate2()
On Error Resume Next
Me.ListBox2.Clear
For Each element In gFieldsListArr
Me.ListBox2.AddItem element
Next element
UserForm_initialize2
End Sub
Private Sub UserForm_initialize2()
For Each element In Split(gCellCurrVal2, ",")
For ii = 0 To ListBox2.ListCount - 1
If element = Me.ListBox2.List(ii) Then
Me.ListBox2.Selected(ii) = True
End If
Next ii
Next element
End Sub
TL;DR: You can't rename event handlers or change their member signature in any way*, because the correct member definition is defined by the event, not its handlers.
Watch the dropdowns at the top of the editor as you navigate between procedure scopes:
Whenever the left-side dropdown says (General), you're not inside an event handler.
Contrast with:
The left-side dropdown is listing all available interfaces and event sources; to handle the events of a form, you must pick UserForm from that dropdown, and then pick a member from the right-side dropdown.
When you do this, the VBE creates the procedure stubs for you, with the correct name and signature every time.
Whenever you navigate to what's intended to be an event handler and the left-side dropdown says (General), you're looking at dead code that isn't responding to any events.
* You may change the accessibility from Private to Public, but invoking an event handler directly is a design smell so there shouldn't be a need to do that. You may change the parameter names, but not their type; renaming handler parameters is a rather surprising thing to do though, and best avoided too. So yeah, best not change these member signatures in any way.

How to use Form Controls in Modules

In my modules, I want to use my controls from my form. For example: I want to set focus on a textbox after a certain Sub.
My current solution is to make a subroutine to set all controls in a public variable (see below).
My questions:
What is the best practice? How do people usually do this?
When should I call my subroutine? (is it the first call in the FORM_LOAD sub?)
Public TBnr As TextBox
Public Sub controlsInitieren()
Set TBnr = Forms("frm_TreeView_Example").pstNr
End Sub
Well, as a general rule, while many platforms seperate out the UI part and the code part? Well, Access is quite much a different approach - it is standard fair to place the required code inside of the form "class" (all forms in Access are a "class", and you can even have muliple instances of the SAME form open more then one time).
So, in general, your code should be in the forms code (class) module.
However, you could and call a external routine.
So in the form, you could call your above routine like this:
Call MySetFocus(me, "NameOfControlToSetFocusTo")
And your sub would look like this:
Sub MySetFocus(f as form, sCtrl as string)
f(sCtrl).SetFocus
End Sub
However, as noted, the amount of code above is more code then simply in the forms code module going:
me.ControlName.SetFocus
However, while the above is a less then ideal example, passing the form "instance" (me) to a external sub or function allows you to referance any property or method or feature that exists in the form in an external routine.
So in place of say
me("LastName") = "Zoo"
In the above sample routine, you would and could go;
f("LastName") = "Zoo"
So any place you would and could use "me" in the form, you can use the form instance you passed from the form. As noted, it is a good idea to use "me", since as I noted, Access does allow multiple copies of the form to be opened at the same time - and thus your code can't distinguish between what form instance you are using unless you pass the current "in context" form. So like in JavaScript, using "this" ?
In access that current instance of the class object is "me", and you are free to pass that instance to any sub or function you want as per above.
The best practice is to use only procedures inside the form code. In such a case you refer to a text box control in this way: Me.Textbox1.SetFocus. If you want to set some controls properties during the form loading, you can do that in the frm_TreeView_Example_Initialize event;
They usually do it in the way I described at item 1;
If you want to use such a strange/unusual way you can do it calling the subroutine whenever you want. But, in order to set a specific property value of the form frm_TreeView_Example controls you can simply use frm_TreeView_Example.TextBox1.SetFocus. You can use this way of setting in a module procedure, even before the form has been shown. You can simply show it in that procedure code using at the end: frm_TreeView_Example.Show;

Change access form properties from a function

I'm trying to move the OnClick code from one of the buttons on a form to a function, but I'm having some issues. The button in question, called "Reset", changes the properties of most of the objects on the form. Stuff like:
Me.btnName.Caption = "Caption"
Me.btnName.Visible = True
Me.btnName.Top = 123
Me.btnName.Height = 456
'Etc
When moving this over to a function I can't seem to get this to work. I've tried a few different ways of writing it that I found while searching around, but none of them seem to work. I'm messing with some objects in the main form and some in the subform, so I'll show a few examples of both. Edited to include error messages
Forms("FormName").btnName.Caption = "Caption"
'Application-defined or Object-defined error
Forms("FormName").Controls("btnName").Caption = "Caption"
'Ms Access cannot find the referanced form (I've double and triple checked that it is correct)
Forms("FormName").SubFormName.Form.btnName.Caption = "Caption"
'Application-defined or Object-defined error
Forms!FormName!btnName.Caption = "Caption"
'Cannot find the referenced form
Forms!FormName!SubFormName!btnName.Caption = "Caption"
'Cannot find the field reffered to in your expression
Forms!FormName.Controls!btnName.Caption = "Caption"
'Cannot find the referanced form
Forms!FormName!SubFormName!Form.btnName.Caption = "Caption"
'MS Access can't find the field 'SubFormName'
No matter what I try of these I can't seem to get it to work. Maybe I'm just doing something simple wrong, or maybe you can't change properties like this from a function. Regardless, if anyone knows, I would appreciate the help.
Note that the function I'm trying to use is in a separate module, not in the code behind the form.
So you have a OnClick handler on some form's code-behind, responsible for assigning a bunch of properties for objects that live on that form.
What you have done is called encapsulation: outside code doesn't need to care about the Top and Height properties (and others) of the form's btnName button - truth is, outside code shouldn't even need to care that there's a button on the form.
So you're taking this nicely encapsulated object, and moving code around for no apparent reason.
If you need that functionality to be invoked from the outside, then yes, move it out of the OnClick handler.. but not outside the form's code-behind.
Move it to some Public Sub OnReset() procedure, and if outside code needs to invoke that logic, then have it call theForm.OnReset.
Private Sub ResetButton_Click()
OnReset
End Sub
Public Sub OnReset()
Me.btnName.Caption = "Caption"
Me.btnName.Visible = True
Me.btnName.Top = 123
Me.btnName.Height = 456
'Etc
End sub
That way you leave the implementation details of the form within the form itself, while giving outside code an abstraction to say "I don't care what your buttons are named; I don't care what size they are or what their captions are - but when I say 'Reset', you shall re-initialize whatever values you've got for them" - outside code doesn't need to know what Reset does specifically.
Put it this way: the day you rename that btnName button, would you rather need to simply adjust the form's code-behind, or hunt down every possible place in the entire project that could possibly be changing that button's Caption or whatever other property value?
FYI Foo!Bar.Something is late-bound code, shorthand for Foo.Item("Bar").Something: neither Bar nor Something are validated at compile-time. Contrast with Me.btnName.Whatever, where a typo is immediately picked up by the compiler: early-bound code that the compiler is able to validate should always be preferred.
You need to set the property to something:
Forms!FormName!btnName.Caption = "New Caption"
or, if on a subform:
Forms!FormName!<NameOfSubformCONTROL>.Form!btnName.Caption = "New Caption"

pass control values window forms

I have a form with 5 controls.
For textbox I am accesing like frmNote.txtNumber.Text.
When I am accessing this property directly from form frmNote then I am getting the value of textbox.
But when I created a method for example NewMethod() in different vb file and call this method from frmNote form button click event then I am not able to access frmNote.txtNumber.Text value over there inside method.
It is coming blank. Do I need to pass all control values to method from form or is there any other way around.
Because txtNumber isn't shared, you can't access it from other class. You must pass it's value to NewMethod. Code will look like this:
Public Sub NewMethod(text as String)
'Use text
End Sub
And calling NewMethod:
NewMethod(txtNumber.Text)

Updating contents of an iRibbon control from another control's callback

Back in the old days of VBA, we used to be able to directly access controls on a form by name. I'm a little perplexed by how to do this in VBA 2010 (or if it's even possible any longer).
Say I have a ribbon control with a dropdown list called "channelList", and elsewhere on the ribbon I have a textbox called "labelText". Each of these items has a unique callback function - when I type something into labelText, its callback fires, and when I select an item from the channelList listbox, its callback fires with the list item passed as an argument.
Where I'm stumped by is how I would update the labelText textbox contents with 'something' from within the channelList callback.
Is there a way to directly access the textbox control from within the listbox callback, or must I generate some sort of event? What method or function would I use to set the text value for the control? (Do I need to cast the IRibbonControl object to something?)
The solution was a combination of an answer and the comments, so here goes:
Add a "getText" callback subroutine name to the Ribbon XML, specific to the "labelText" editbox.
editBox id="labelText" label="Text:" sizeString="xxxxxxxxxx"
onChange="TextboxCallback" getText="UpdateTextBoxText"
screentip="Channel label"
supertip="Limited to 10 characters. Press Enter once finished typing."
In the combo box callback function, set the desired editbox text to a global and call Ribbon.InvalidateControl with "labelText" as the argument.
MyRibbon.InvalidateControl "labelText"
Implement the editbox callback, passing a handle to the ribbon and another variable ByRef to contain the 'stuff' to be updated. Use the global to update the control's text via the ByRef argument.
Sub UpdateTextBoxText(control As IRibbonControl, ByRef returnedVal)
Select Case (control.id)
Case "labelText"
returnedVal = LabelText
End Select
End Sub
I guess this is "progress". labelText.Value = "blah" is far too simple.
Take a look at the Ribbon.Invalidate method. When you "invalidate" the ribbon you basically reset it, and in that moment you can set the properties of controls on the ribbon based on whatever you can track in that moment. In order to do so, you need to have Control.GetEnabled, GetVisible, etc., subroutines in your VBA.
You can also invalidate individual controls with Ribbon.InvalidateControl.
Here's a link that might help: http://sourcedaddy.com/ms-excel/resetting-controls.html