I have a bunch of TextBox-Button pairs on a form. When the button is clicked I want to insert the value of the text box into a database. The name TextBoxes and Buttons follow a naming standard, for example Value1Tb - Value1Cmd and Value2Tb - Value2Cmd.
My problem is that since I want to do the same for every button I would like the possibility to write a Sub like:
Private Sub AnyButton_Click(sender As CommandButton)
Dim tb As TextBox
Set tb = GetTBByName(s.Name)
PutValueToDatabase(s.Name,tb.Text)
End Sub
But I cannot find a way to point the Click-event of a Button to a different sub than the standard Name_Click().
Anybody know a way around this, that doesn't involve me writing 50 or so different Name_Click() subs?
If you are OK to use Form Controls rather that ActiveX, as it looks as though you may be at the moment, then Chris' solution seems good.
However if you need ActiveX CommandButtons then you are unable (as the VBA compiler will tell you, "Procedure declaration does not match...") to have parameters in the callback for the click event, and you are unable to raise the event from multiple objects, although you do of course know which button raised the event (since the relationship is 1 CommandButton = 1 Sub).
So... I would go with something like:
Private Sub Value1Cmd_Click()
Call TheMethod(Value1Cmd)
End Sub
Private Sub Value2Cmd_Click()
Call TheMethod(Value2Cmd)
End Sub
Private Sub TheRealMethod(sender As CommandButton)
' Do your thing '
Dim tb As TextBox
Set tb = GetTBByName(s.Name)
PutValueToDatabase(s.Name,tb.Text)
' Etcetera... '
End Sub
Requires a stub for each button, so some copying and pasting to begin with, but then easy to maintain etcetera as all _Click event callbacks are pointing at the same method...
Edit:
E.g.
Sub AutoWriteTheStubs()
Dim theStubs As String
Dim i As Long
For i = 1 To 10
theStubs = theStubs & "Private Sub Value" & CStr(i) & "Cmd_Click()" & vbCrLf _
& " Call TheMethod(Value" & CStr(i) & "Cmd)" & vbCrLf _
& "End Sub" & vbCrLf & vbCrLf
Next i
Debug.Print theStubs
End Sub
It seems that what you want is to get the name of the clicked button. If you are creating buttons like this:
(where 'i' increments in a loop)
Set btn = Sheet1.Buttons.Add( , , , ,)
With btn
.OnAction = "btnSub"
.Caption = "Upadate"
.Name = "btn" & CStr(i) & "Cmd"
End With
and then defining a generic "private sub btnSub()" for all the buttons, you could at least get the name of the button that was clicked using Application.Caller. Something like:
Private Sub btnSub()
Dim ButtonName As String
ButtonName = Application.Caller
MsgBox ("Hello:" & ButtonName)
End Sub
Hope it helps!
I decided to make this an answer because I am doing something similar and I confirmed that it works.
You can store the OLEobjects in a Collection, of arbitrary size, containing Custom Class Objects that include the OLEobjects and associations and the events that you need. Thus you can completely avoid any code stubs.
Create a Custom Class to bind the Button and TextBox pairs.
Declare the Button object WithEvents.
Include your call-back in the exposed button event handler in the Class Module.
Put a Public routine in a Standard Module to initialise a Collection of these Custom Class objects by spanning the Form Controls. You can also use this to Add the controls programmatically as a 'reBuild' option. The Collection can be inside another Class Module with all of the management routines, but it needs to be Instantiated and loaded in a Standard Module.
Put a public routine in a standard module to receive the call-backs with whatever context you need. This can also be in a Worksheet Module if it makes for better encapsulation. You can use late binding to reference the callback or CallByName.
You need to bear in mind that the Module of the Form will recompile every time you add a control, so you have to be careful where you put your code.
My application has the controls directly on the Worksheet Surface, so I can't put the the Collection Class in, or source any initialisation of the Collection from the Worksheet module. This would amount to self modifying code and it grinds excel to a halt.
I dreamed this idea up through bloody-minded idealism (not necessarily a good thing) but, of course, I was not the first one to think of it as you can see here. #Tim Williams explains it in his answer. You can also google VBA Control Array Events to see plenty of similar examples including an excellent article by #SiddharthRout. In line with the VB6 analogy, he uses an Array instead of a Collection to achieve the same result.
I'll try to post some code later. My application is a bit different so it will take a lot of work to trim it down, but the principle is the same.
The other thing to bear in mind is that VBE really struggles with this type of thing so don't worry if it is loading up you processors. After you re-start with VBE off, all will be fine.
I have this same situation, and I just have a click event for every button that is a wrapper to the function I want to call. This also allows you to pass sheet-specific parameters if you need to.
Example:
Public Sub StoreButton_Click()
' Store values for transaction sheet 3/27/09 ljr
Call StoreTransValues(ActiveSheet)
End Sub
I just published (Open Source) the Event Centralizer for MSForms.
Citation: "The Event Centralizer for MSForms is a VBA programming tool that allows all sorts of custom grouping when writing handlers for the events occurring in UserForms.
With the Event Centralizer for MSForms, it is easy for example to have all TextBoxes react the same way when the Enter event occurs, or all except one, or only those with a particular Tag value.
Thanks to its events logs system, the Event Centralizer for MSForms is a powerful learning and debugging help."
I can't explain here how it works. I tried to do it on the site.
Set the event to =FunctionName(parameter).
A bit late but this may help someone else:
If you have a function called OpenDocumentById(docID as integer), then each control calling the function would have in the event the following:
cmd1's On Click event:
=OpenDocumentById([DocID])
cmd2's On Click event:
=OpenDocumentById([DocID])
etc...
Related
Question
How can i make or not make particular Tabs visible by loading a form in Access with VBA?
Explanation:
I have a Acess Database with 4 forms containing buttons. Today i started to create ribbons to get rid of those in the forms so everything is sorted and easy to overview. I want the Tabs in my Access Database to be unvisible until i open a form from my main form.
Main Form (no tabs showed) --> Switching to another form by clicking a button in my main Form (now i want to show a particular tab after opening the form)
I created my Ribbons with "Ribbon Creator 2019" for Office 2019.
Problem:
I cant solve it ... i tried so many things until i found a excel worksheet having a function by swithing sheets to display particular tabs. Its exactly what i want but i can't get it to work for my access.
By getting the name of the active form i cant get it to work and in my opinion this would be tha fastest way.
My approach:
I set "StartFromScratch" in my XML to 'true' and gave my tabs names like this: "CustomTagValue1:=xstart".
Code for my tabs (Module):
Sub GetVisible(control As IRibbonControl, ByRef visible)
' Callbackname in XML File "getVisible"
' To set the property "visible" to a Ribbon Control
' For further information see: http://www.accessribbon.de/en/index.php?Downloads:12
' Setzen der Visible Eigenschaft eines Ribbon Controls
' Weitere Informationen: http://www.accessribbon.de/index.php?Downloads:12
Select Case control.ID
Case "tab_3"
' Tab: tab_3
visible = False
Case "tab0"
' Tab: tab0
visible = False
Case "tab1"
' Tab: tab1
visible = False
Case Else
visible = True
End Select
End Sub
Code from another Module to declare my tabs:
Option Compare Database
Option Explicit
'**************************************************************************
' About this Code:
'
' This Code checks if a Formular is in active use by his 'Name'. Simple.
'**************************************************************************
Dim MyTag As String
'Callback for customUI.onLoad
Sub RibbonOnLoad(ribbon As IRibbonUI)
Set Rib = ribbon
End Sub
Sub GetVisible(control As IRibbonControl, ByRef visible)
If control.Tag Like MyTag Then
visible = True
Else
visible = False
End If
End Sub
Sub RefreshRibbon(Tag As String)
MyTag = Tag
If Rib Is Nothing Then
MsgBox "Fehler RBC1018, bitte starten Sie das Programm neu."
Else
Rib.Invalidate
End If
End Sub
Code in the Form onLoad:
Private Sub Form_Load(ByVal Sh As Object)
Select Case Screen.ActiveForm
Case "frmVerteiler": Call RefreshRibbon(Tag:="xverteiler")
Case Else: Call RefreshRibbon(Tag:="")
End Select
End Sub
Help:
I want it to work as in this excel Dokument: https://www.rondebruin.nl/win/s2/win012.htm
It is far less work and significant more easy to just create a custom ribbon for each form, and that way you don't have to write all kinds of code to hide show tabs on the ribbion. So just specify the correct ribbon for the given form, and no code is required.
I also suggest that you do NOT use call backs in the ribbon. If you adopt this approach, then the ribbon can directly call your EXISTING button code. So the code, buttons you now have can be transferred to the ribbon for that form, and you don't have to setup ribbon call backs, and all of your existing button code can remain "as is" and be called directly from the ribbon - and the ribbon will run the button code that is and remains in the form.
All you need to do is to declare any function you want called as a public function.
You then set the on action as follows:
=MyPublicFunctionName()
Note CAREFULL how we have = and () (you must have these), and they must be under the quotes.
Eg for the xml we have:
onAction="=MyDelete()"
Note how the above is DIFFERENT then a call back for a ribbon (and callbacks use sub, where this is a function). Even better is the above means you do NOT have to place the code in a standard code module, but can place the function it in the current form. So, no macro needed, no callback, and the code can go in the current form (just like it does for a command button). And bottoms to dollar, in 9 out of 10 cases, the code you need for particular form and button belongs in that particular forms code module anyway. In fact it's a very bad programming practice to start taking code that belongs in a form and placing that code in a standard public code module. The reason is for many, but all kinds of issues can crop up if you have multiple instances of that same form which is allowed in access. Furthermore when copying forms between applications, or even making a copy of the form within the same application means that you have outside code dependencies that we normally as access developers do not expect (we assume for the most part is that the code we're using for the form belongs in the forms code module and I 100% agree). I am open to the idea that this does go against the well known concept of moving UI and code apart but this is "HOW" access works. So the access way does go against trends in our industry.
Keep in mind the above function call idea is the same format we can and would have been using since near day one with menu bars in previous editions of access. So, if you are wanting to change menu bar code to ribbon, use the above idea. Also, if you have several buttons that runs code in a given form, then again the above syntax allows one to KEEP the code in the current form and simply declare the button code as a public function (you can thus real easy move buttons from a form to a ribbon if you do this).
If the function name you specified in the menu or ribbon was named as public in the form's code module, then the CURRENT FORM with the CURRENT FOCUS is where the function will be first looked for to execute. This is SIGNIFICANTLY important because it means you can use one custom menu bar for five different forms, but each of the five different forms will run a custom delete routine for example (no messing with screen.Activeform). And, you don't have to use a bunch of messy case statements as you do with a call back. In fact, all of the code stays in the form where it likely was or belongs in the first place, and that is in the forms code module.
So, if you have specific and specialized delete code that might be required for the given form that has the focus, then that's forms function code in the forms module will be run when it is called from the on action in the menu bar, or now ribbon.
This means if you set the on action to =MyDelete()
Then in each form you have, you simply declare a public functions such as
CODE
Public function MyDelete()
Code here to delete the record
End function
However it turns out for probably more then half or even close to 90% of your forms, it's entirely possible that you want a general catchall delete routine that works for all forms that don't need specialized custom deleting code. In this case you simply place the function in a standard code module (and again as public). If the current form does not have that function, then it is run from a standard code module (again, ideal behavior, and again this is how the onAction worked before ribbon).
Also, note that you also pass values directly from the ribbon.
So, ribbon xml might be:
CODE
<button id="MyDelete" label="Delete Record"
imageMso="Delete" size="large"
onAction="=MyDelete('Staff','tblStaff')"
supertip="Delete this record"
/>
In the above, I passed the table name, and a prompt text of Staff.
And the public catch all function in a standard code module will be:
CODE
Public Function MyDelete(strPrompt As String, strTable As String)
Dim strSql As String
Dim f As Form
Set f = Screen.ActiveForm
If MsgBox("Delete this " & strPrompt & " record?", _
vbQuestion + vbYesNoCancel, "Delete?") = vbYes Then
etc....
Note again how I passed two parameters to the delete routine (the text must be under single quotes). The prompt part so the msgbox command will say "Delete this staff ?". And, then I also pass the table name. What the above means is that for ten forms, if you don't specify a public function inside of the form, when the menu button is clicked on is selected, the catchall general routine in an standard code module (non forms code module) will run.
And for the few forms that have specialize deleting code, the function inside of the forms code module will run. That code might look like:
CODE
Public Function MyDelete(s1 as string, s2 as string)
' check for history
....
lngHistory = Nz(DLookup("ContactId", "NHistory", "ContactID = " & Me.ContactID), 0)
If lngHistory > 0 Then
Beep
MsgBox "You cannot delete a person with past history bookings!" & vbCrLf & vbCrLf & _
"You should simply check the 'Do NOT INCLUDE in mailings' to remove from" & vbCrLf & _
" future mailings.", vbExclamation, "Rides"
Exit Sub
End If
...code here with sql to delete record....
So a few things:
I would just create a ribbon for the given form (take your xml for the given tab, and create a whole new ribbon). Now, if you have 2 or 5 forms open, then the ribbon will automatic flip and change for you. If you try and use tabs, then a simple change of focus to another form will require you to hide+show given tabs - it becomes a real mess rather quick.
How can I stop Access from prompting "Do you want to save changes to the layout of query" when I try to close a form that has a subform in datasheet view?
I have a form with a subform in Datasheet view with the .SourceObject set to a temporary pivot / crosstab query (no actual form object). If the user changes the width of a column and closes the window with the built-in Access close button, the user (and annoyed developer) is always presented with the "Do you want to save changes to the layout of query" prompt.
I am able to avoid this prompt by setting MySubform.SourceObject = "" in the click of my Close button but anyone clicking the [x] button or pressing CTRL+F4 or CTRL+W gets the prompt.
I have put breakpoints in the Form_Close and Form_Unload events but this prompt appears before they fire.
I want to clarify further that this subform object is Unbound and not based on a form object. I am building a dynamic Crosstab SQL statement, creating a QueryDef with the SQL, and then setting MySubform.SourceObject = "query.qry_tmp_Pivot" This is a neat technique for a crosstab/pivot query because the columns can vary and I don't think a form object would support that.
I am able to use the Watch window on the MySubform object and I can see that a MySubform.Form object exists. It has a Controls collection containing a control for all of the columns in my query. I have an optional routine that I can run that will loop through all of the controls and set their .ColumnWidth = -2, which will auto-size the column width based on the widest data of the visible rows in the datasheet. When this routine was running I was noticing that every time I closed the form (not using my Close button) I was getting the save prompt. I have disabled this routine for debugging but a user will still get the prompt if they manually adjust any column width.
I feel like I need to explain this extra detail so you realize this is not an Access 101 issue. You probably know that if you've read this far. Here's another thought I had: Maybe I could trap the Unload event in the subform control before the prompt happens. Because there is no true Form object to put test code in, I created a class object and passed MySubform to it. The class uses WithEvents and creates events like OnClose and OnCurrent on the class module's mForm object. Sample class code is below.
Private WithEvents mForm As Access.Form ' This is the form object of the Subform control
Public Sub InitalizeSubform(Subform As Access.Subform)
Set mForm = Subform.Form
Debug.Print Subform.Name, mForm.Name, mForm.Controls.count
' Create some events to intercept the default events.
mForm.OnClick = "[Event Procedure]"
mForm.OnClose = "[Event Procedure]"
mForm.OnUnload = "[Event Procedure]"
mForm.OnCurrent = "[Event Procedure]"
End Sub
Private Sub mForm_Click()
Debug.Print "Clicking " & mForm.Name
End Sub
Private Sub mForm_Current()
Debug.Print "On Current " & mForm.Name, "Record " & mForm.CurrentRecord & " of " & mForm.RecordsetClone.RecordCount
End Sub
Private Sub mForm_Unload(Cancel As Integer)
Debug.Print "Unloading " & mForm.Name
End Sub
Private Sub mForm_Close()
Debug.Print "Closing " & mForm.Name
End Sub
The VBE Watch window shows my new events on the mForm object but unfortunately they never fire. I know the class works because I used it with a bound subform and all of the events are intercepted by the class. I'm not sure what else to try.
Events on the subform never fire because it's a lightweight form (without a module). See this Q&A and the docs. Lightweight forms don't support event listeners, but do support calling public functions from an event, e.g. mForm.OnClick = "SomePublicFunction()"
Note that the workaround described in this answer also opens up the possibility of displaying a crosstab query in a form without saving it at all.
Alternatively, you could try capturing the event on your main form, and suppress saving there.
I gave answer credit to #ErikA for this question. He directed me to an answer to a somewhat related question here which is what I ended up implementing and it works. His instructions were very straightforward and easier to implement than I first anticipated.
The answer to my question seems to be that it may not be possible to avoid the save prompt when using a lightweight form object. If someone comes along with a solution I'd still like to hear it.
What I learned from this experience:
An unbound subform that has its .SourceObject set to a table or query will not have a code module. This considered to be a lightweight object.
Public functions can be added to event properties on lightweight forms but they don't seem to fire before the save prompt appears as the parent form is closing.
Binding a pivot query or temp table to a real form object that has 254 controls on it is the only way I found that doesn't prompt to save design changes when the parent form closes. Mapping the query/table columns to the 254 datasheet controls is super fast.
A class is a nice way to intercept events if you're not using a lightweight object. A class also lets you open multiple instances of the same object.
I ended up not implementing my solution without using a class because there were no events I needed to capture. At this point I don't need my frmDynDS to be used for other purposes and I'd like to keep the object count down (this is a large app with 1400+ objects).
I put the following code in a function that gets called when the subform is loaded or refreshed.
With subCrosstab
' Set the subform source object to the special Dynamic Datasheet form. This could be set at design time on the Subform control.
.SourceObject = "Form.frmDynDS"
' Run the code to assign the form controls to the Recordset columns.
.Form.LoadTable "qry_tmp_Pivot" ' A query object works as well as a table.
' Optional code
.Form.OnCurrent = "=SomePublicFunction()"
.Form.AllowAdditions = False
.Form.AllowEdits = False
.Form.AllowDeletions = False
.Form.DatasheetFontHeight = 9
End With
I can now resize the columns and never get a prompt to save layout changes no matter how I close the parent form.
System Description: I have a userform that takes input on an item that is being returned. A user clicks the row of the item that needs to be returned and then clicks a "Check-In button"
My Attempt: I created a button checkin_cmdbutton on the spreadsheet that measures which item is selected by which cell is selected Application.ActiveCell.Row, writes the info into a userform Checkin_Form, the user finishes the rest of the check-in info, and clicks submit.
This code is the event for the button checkin_cmdbutton on the spreadsheet:
Private Sub checkin_cmdbutton_Click()
Set ItemID = Cells(Application.ActiveCell.Row, 1)
Set ItemDescription = Cells(Application.ActiveCell.Row, 2)
If ItemID Is Nothing Then
MsgBox ("ID is null, ending...")
Exit Sub
End If
Checkin_Form.UserForm_Initialize
Checkin_Form.itemid_dynamiclabel.Caption = ItemID.Value
Checkin_Form.description_dynamiclabel.Caption = ItemDescription.Value
Checkin_Form.checkin_datepicker.Value = Date
Checkin_Form.Show
End Sub
Problem: The code throws an error 91 "Object variable or with block variable not set" at Checkin_Form.itemid_dynamiclabel.caption and the following 2 lines. Why is an object on a form throwing this error? I can't declare these, can I?
You shouldn't be explicitly calling UserForm_Initialize - that's an event handler, and there's a reason handlers are Private by default: they're invoked by the event provider, when the event provider deems it necessary - in this case, when the object instance is getting initialized.
The best way to ensure the form gets initialized properly, is to treat it like the object it is, instead of storing global state on its default instance.
A UserForm class is little more than a class module with a designer and a VB_PredeclaredId module attribute. This attribute makes VBA create a global-scope object variable named after the class, and that is how this code is legal:
UserForm1.Show
Except, it shouldn't be.
You DON'T want to store global state in the default instance: that's the very last thing you want, especially if your form involves dynamic controls.
New it up instead.
With New UserForm1
.Show
'what follows only executes when the form is closed:
'...
End With
For this to work, you must handle the form's QueryClose event, to prevent the object instance from self-destructing itself when the user clicks the [X] button.
For this to work, you must also avoid destroying the form yourself, e.g. with Unload Me (or worse, Unload UserForm1) calls - say, when the user clicks the [Ok] button. Instead, you Hide (or Me.Hide) the form, so that the caller (the code that New'd it up) can still access the object's state.
From the look of your code - i.e. with the .Show call being the very last thing your macro does, I can tell that you're having the form run the show: this is an anti-pattern that will keep creating problems every time you do that.
Forms don't implement application logic: forms present and collect data. Nothing more, nothing less. It's not the form's job to write to any spreadsheet, or even to know anything about worksheets.
Read this recent article of mine if you want more information about doing forms right.
Now, the actual problem.
Checkin_Form.itemid_dynamiclabel.Caption = ItemID.Value
If that label is dynamic (i.e. created at run-time), then I'm surprised accessing it like this even compiles. First, remove the underscore in the form's name: underscores have a special meaning in VBA - I'm sure you've noticed the pattern by now, of how VBA generates event handlers for a given object:
Private Sub ObjectName_EventName()
End Sub
If ObjectName or EventName has an underscore, you're asking for compile errors at one point or another - one day you'll want to use an Implements statement and discover that your code can't be compiled anymore, if you kept that underscore habit: better lose it now.
If the control is dynamic, you can't do what you're trying to do the way you're doing it.
Dynamic controls need to be accessed through the form's Controls collection:
Dim myLabel As MSForms.Label
Set myLabel = Me.Controls("NameOfTheLabelControl")
Otherwise, you need to keep a reference to the dynamic contols at module-level, in the form's code-behind - you could expose it via a property:
Option Explicit
Dim myLabel As MSForms.Label
Private Sub UserForm_Initialize()
Set myLabel = Me.Controls.Add(...)
End Sub
Public Property Get ThatLabel() As MSForms.Label
Set ThatLabel = myLabel
End Property
Or better, use an actual model class, and let the calling code not be bothered with controls at all - see the previously linked article for details.
TL;DR:
You're getting that error because your label object instance isn't initialized, i.e. it's Nothing. Since you aren't showing your form's code-behind, we can't really point out why that is the case, but my money is on the form's default instance making you yet another victim of the "hey look how easy it is!" VBA tutorials that teach things wrong.
Implement the worksheet-handling code outside the form, make the form collect data, make the calling code read this data after the form is hidden, and then make the calling code create and destroy the form instance.
Now, with all that said, I've no idea why you think you need a dynamic control for this.
Just shooting in the dark, as far as I really do not know the names of your variables and what they are (a few screenshots will be helpful). Try like this, if your code is in a form (as far as you have _Click I assume it is):
Private Sub checkin_cmdbutton_Click()
Set ItemID = Cells(Application.ActiveCell.Row, 1)
Set ItemDescription = Cells(Application.ActiveCell.Row, 2)
Me.itemid_dynamiclabel.Caption = ItemID.Value
Me.description_dynamiclabel.Caption = ItemDescription.Value
Me.checkin_datepicker.Value = Date
Me.Show
End Sub
And try at least declaring the variables (e.g. ItemID etc) and using Option Explicit on top.
I created my own Outlook form to use it as standard surface to enter certain orders instead of the normal message form. The creation, editing and sending works perfectly fine and in the next step I want to insert some code via VBA.
My problem is that I can´t access the objects of my form in the VBA editor. E.g. I want to show a message box when a certain checkbox is checked. According code would be:
Sub example()
If CheckBox1.Value = True Then
MsgBox("Checkbox 1 is checked.")
End If
End Sub
When I run the code I get the error that the object could not be found. The same goes for every other object, like textboxes or labels etc.
I guess the solution is pretty simple, like putting Item. or sth. like that in front of each object. But so far I wasn't able to find the solution.
I´m using Outlook 2010.
I know this is a year too late but you'll want to do something like this example below. It's kinda a work around but you can get whatever value was selected.
Sub ComboBox1_Click()
Set objPage = Item.GetInspector.ModifiedFormPages("Message")
Set Control = objPage.Controls("ComboBox1")
MsgBox "The value in the " & Control.Name & _
"control has changed to " & Control.Value & "."
End Sub
You should be able to get the value, just get a handle on the object you want using the Inspector
The following is an excerpt from here
When you use a custom form, Outlook only supports the Click event for
controls. This is a natural choice for buttons but not optimal for
controls like the combo box. You write the code by inserting it into a
form’s VBScript editor. You need to have the Outlook form open in the
Form Designer and click the View Code button found in the Form group
of the Developer tab.
Sub CheckBox1_Click()
msgbox "Hello World"
End Sub
The code page is fairly minimal with no syntax highlighting. I just tried this now and it does work. Dont forget to Publish your form to pick up the new changes.
I know this is almost 6 years late but, in VB and VBA, simply start with the form name. (And if that doesn't work, just keep going up a parent object and you'll get there.) So, your code becomes:
Sub example()
If MYFORMNAME.CheckBox1.Value = True Then
MsgBox("Checkbox 1 is checked.")
End If
End Sub
Of course, after typing "MYFORMNAME." you'll know if it will work because typomatic will kick in when the system recognizes "MYFORMNAME" after you hit the period.
Let's say I have a userform with a command button named cmd_1.
For purposes of my application, I have a lot of validation I would like to do on this click - see this question for some discussion about what I am attempting to do. Basically I am hoping to implement a single callback function with logical rules I check, based on the calling object's name, to determine the eligibility of that control's actions.
This would look something like:
private function isValid(p_controlName as string) as boolean
'logical checks based on the the value of p_controlName
'returning true/false as appropriate
end function
and I would be using it something like this
Private Sub cmd_1_Click()
if isValid("cmd_1") = false then exit sub
End Sub
Now I am going to be putting this into a lot of UI callbacks (I don't like thinking about how many). I would rather avoid having to tediously add the name of the control to each callback. I would very, VERY much prefer do something like:
Private Sub cmd_1_Click()
'this is not valid syntax
if isValid(ThisControl.name) = false then exit sub
End Sub
This is better for a whole variety of reasons, ease of implementation, consistency, less likely for errors, etc.
However I cannot seem to find a way to get the name of the calling control within a callback function.
Is there some way to get the name of a control in VBA code from a callback function to use as an argument like I am trying?
If it's only button click event handlers, then Screen.ActiveControl.Name should get you what you want - Screen.ActiveControl returns the control with the keyboard focus, and a command button takes the keyboard focus on being clicked.
Alternatively, use a callback function for each, with a parameter based on the control name or information that you need, and make the controls each have:
Dim rect As Control
Dim coll As Form
Set coll = Forms("myForm")
For Each rect In coll.Controls
If rect.ControlType = acRectangle Then
rect.OnClick = "=myCallbackFunction(" & param & ")"
End If
Next rect
myCallbackFunction is just a function on the same form as the control, or a public function in a module.
I've done this where each rectangle (up to 100, pre-generated at height 0 on my form) is moved and made visible based on a recordset, then they have this onclick event setup based on an ID from the recordset. In my case it selects a different subform record to match the rectangle clicked.
Don
Have a great day