I have a navigation form with two subforms that looks something like this:
When the button in Subform A is clicked, I would like to call a method from Subform B. The method is defined like this:
Public Sub MyMethod()
Debug.Print "MyMethod called"
End Sub
I tried using Forms!SubformB.MyMethod but I get the error Database cannot find the referenced form 'SubformB'. Referring to this page, I also tried Forms!NavigationForm!SubformB.MyMethod but then I get Database cannot find the referenced form 'NavigationForm'. Does anyone know how to do this properly?
Thank you.
Function and Sub procedures defined in the class module of a Form are considered to be private to that form. If you want a Sub that can be called from several different forms then move that Sub to a "regular" VBA Module (i.e., one that is created when you choose Insert > Module from the menu bar in the VBA editor) and be sure to declare it as Public.
Here is a snippet of code I have that toggest the AllowEdits method of a subform when clicking a button on the mainform. Pretty sure this is what you are after.
Function ToggleEditStatus_Child(WhichForm, WhichChild As String)
'Changes the AllowEdits setting of the SubForm parameter
Dim Cform As Object
Set Cform = Forms(WhichForm).Controls(WhichChild).Form
Cform.AllowEdits = Not Cform.AllowEdits
Forms(WhichForm).Refresh
Cform.Refresh
Forms(WhichForm).Controls(WhichChild).SetFocus
Set Cform = Nothing
End Function
I did figure this out but there's a catch - the other form has to be open. This worked for me though so I'm posting the solution here.
First, navigate to the form:
DoCmd.BrowseTo ObjectType:=acBrowseToForm, _
ObjectName:="SubformB", _
PathToSubformControl:="NavigationForm.NavigationSubform", _
DataMode:=acFormEdit
Then make a pointer to the subform:
Dim f As Form_SubformB
Set f = Forms("NavigationForm").NavigationSubform.Form
And you are free to call the method:
f.MyMethod()
Note: you will most likely have to make the method Public for this to work.
Related
In an Access application, I'd like to programmatically set up a dynamic key handler to execute a VBA function when this key is pressed.
I was able to manually create a macro that calls the function and another AutoKeys macro that "calls" the first macro when a specific key is pressed.
Now, I'd like to be able to create these macros with VBA only. Is this somehow possible?
Of course, if there are other ways to achieve what I want, I am open to suggestions.
You could do something like this, create a class called clsCustomForm, containing this code
Option Compare Database
Option Explicit
Private WithEvents frmCustomForm As Form
Public Function INIT(frmFormToMakeCustom As Form)
Set frmCustomForm = frmFormToMakeCustom
frmCustomForm.OnKeyDown = "[Event Procedure]"
End Function
Private Sub frmCustomForm_KeyDown(KeyCode As Integer, Shift As Integer)
MsgBox "Hello"
End Sub
Then in a module, you will need to have a public collection, which you add a class for each form opened and removed when each form closed, I'd suggest a dictionary, with the key as the form name and the item as the class, for example, just testing (limited time) I've used the following code in 1 form, hope this helps, i'll have a bit more time later to show the dictionary if needed.
Option Compare Database
Option Explicit
Public CUSTOM_FORM As clsCustomForm
Private Sub Form_Load()
Set CUSTOM_FORM = New clsCustomForm
CUSTOM_FORM.INIT Me
End Sub
I am sure this is really simple but i just cant make it work. I have a form with three sub forms. The main form loads and finds the requested record fine and if i run the below from a button on the main form it works fine. What i want to do is have it run after form loads requested record and with out user interaction. I have tried on load and on current but as the form is not yet on screen there are no values to populate the variables and the msgboxes are blank. But like i said fired from a button on the main it works fine.
any help would be greatly appreciated.
actCount = [ActMovDelete].Form![Text93].Value
PlaCount = [PalnMovDelete].Form![Text91].Value
noCount = [NotesDelete].Form![Text50].Value
MsgBox actCount
MsgBox PlaCount
MsgBox noCount
I'm not sure if I understand your question, but if you are you trying to active the code upon initialize, then just
Private Sub UserForm_Initialize()
[your code here...]
End Sub
If however, you are trying to populate variables of forms that are not loaded, then a better approach might be to store the variables as public variables in the forms. This should give you access to the variables even when they are not loaded. Just as an example of how this might work:
In the form from where you want to get the variable (named UserForm1):
Public strFromTheForm As String
Private Sub UserForm_Activate()
TextBox1.Value = strFromTheForm
End Sub
From another form or a module, you can include the code:
Public Sub Test2()
Dim strTest As String
strTest = UserForm1.strFromTheForm
If strTest = "" Then
UserForm1.strFromTheForm = "Test"
End If
UserForm1.Show
End Sub
I've made a graphical navigation system so the user can easily find the record he/she wants to. I'm using this on multiple forms now, and since I'm going to use it on another form, I want to standarize the code into a class instead of copy-pasting the VBA code. This way I can improve the code in one place, instead of doing the same change on all the forms.
This is how it works right now:
Dim v As New clsNav
Set v.button1 = Me.button1
Set v.button2 = Me.button2
v.init
And in v.init, I want to set up all the events like click. So when the user clicks button1, it should run a specified method.
How can I do that?
The events for the button clicks will look like this
Private Sub Button1_Click()
' Your code
End Sub
And they are located within the code found behind the respective forms. To be able to reuse code you simply write a sub in a separate module and then call it in the event.
Private Sub Button1_Click()
call MySub()
End Sub
' This is in a module
Private Sub MySub()
' Your code
End Sub
Now this works as long as the code you have to run doesn't use controls specific to that form. If you do need to write code like that, its simply a matter of passing a control to the sub, instead of calling it by its Name
Example. Lets say when we click our buttons it updates a TextBox with today's date. The textboxes have different names on each form. txtDate1 on Form1 and txtDate2 on Form2. So how that will look is
'Form 1 Button
Private Sub Button1_Click()
call MySub(txtDate1)
End Sub
'Form 2 Button
Private Sub Button2_Click()
call MySub(txtDate2)
End Sub
' This is in a module
Private Sub MySub(t as TextBox)
t.Text = Date()
End Sub
If you're trying to do this during run time
How to add events to Controls created at runtime in Excel with VBA
seems like a good place to start. I can't imagine a situation where this would be worth the effort.
I have a generic class I use called frmCtrl, which has WithEvents pointers set up for different control types, with the corresponding events (click, dbl-click, etc...). I then have a function on that class to "set up" the object, so I pass the control in, and the function assigns it to the object pointer of the correct type. This routine also has a parameter for what function to call on which object, so it knows how to respond to the event.
So when I build a form that uses dynamically-added controls, I just create one of these objects for each of the controls, and set it up to call code within the form. The code in the form takes an argument of just the frmCtrl object, so it can easily see which control fired the code, and go from there. I work in Visio, so I'm able to also call a function by module and function name, so the class can handle that too. You'd have to work in how to make such a call using Access, though.
This approach can be used when your controls have been added via the VBIDE, but it may not be worth the effort to set this class up for that, when you can just use the method in the other answer here.
I'm trying to create my own toolbar button (commandbarbutton) in Access VBA Editor. Its a button to run a procedure in a public module called "RunTests". I made the button like so:
Public Sub CreateToolbar()
Dim cmdBar As CommandBar
Dim cmdButton As CommandBarButton
Set cmdBar = Application.VBE.CommandBars.Add("Run Tests")
Set cmdButton = cmdBar.Controls.Add(msoControlButton)
cmdButton.FaceId = 558
cmdButton.DescriptionText = "Run the tests"
cmdButton.Caption = "Run Tests"
cmdButton.OnAction = "RunTests"
cmdButton.Enabled = True
cmdButton.Style = msoButtonIconAndCaption
cmdBar.Visible = True
End Sub
I ran the procedure as a test. And it created the button just fine. But the "click" action doesn't respond. The button doesn't seem to be clickable. Does anybody know why?
Edit: Found the solution: http://support.microsoft.com/default.aspx?scid=kb;en-us;Q280607 - I will answer my own question as soon as I can to close it.
The key for me was to make "RunTests" a public subroutine. When it was private it didn't work.
cmdButton.OnAction = "=RunTests()"
public sub RunTests()
private sub RunTests() does not work.
Found the solution - To make it work you have to implement the workaround described here: http://support.microsoft.com/default.aspx?scid=kb;en-us;Q280607
Unfortunately I tried to be fancy and define my button and menus in a class. The OnAction subroutine was never executed while it was defined locally as part of the class. It would only execute when defined outside of the class definition.
When I redefined my class to be just a module containing many subs and functions, then the OnAction subroutine was executed. Might have to do with scope and visibility or that I didn't have the OnAction semantics correct for a call to a class method. Bottom line is to define the button/menu/widget in a module and it will be much easier. (I was using Excel VBA but I think Access and Excel VBA are the same.)
So I have a sub form that creates a record (and record it), and then another sub form opens up for data entry...however can I still use an open arg to pass the value (ProjectID) between these sub forms.
I know exactly how to do this with forms, but trying this with subforms is a little different.
docmd.openform "FormExample",,,,,,Passingvalue
but can't do it like this:
me.MyChild.SourceObject = "SecondSubForm",,,,,,PassingValue
so yeah the above might look ridiculous to those who know the right way, but I thought that this would illustrate exactly what I am trying to do.
So how can I pass the value? Do I even need to with subforms instead of forms? or would this variable hold my value even though it was created in the first sub form?
thanks
justin
I don't think there's any way to pass an OpenArg with SourceObject =
Can it work to assign the value to a control after you set the SourceObject?
Me.MyChild.SourceObject = "SecondSubForm"
Me.MyChild!SomeControl = PassingValue
Rather than using a Hidden control as an alternative you can add a public method or public property to the Child form and then call it.
Personally I prefer to use a method because typically mutating a property shouldn't have side-effects.
Here's an example.
The child form Called SomeChildForm will have this method
Public Sub SetSomeValue(ByVal somestring As String)
Me.Text1.Text = somestring 'or probably something to do with the record source
End Sub
From the parent you do this
Dim frm As Form_SomeChildForm
Set frm = Me.SubFormControlName.Form
frm.SetSomeValue "x"