Conditionally Presetting Checkboxes on VBA Userform - vba

I need a Userform to call another Userform that fills text boxes and checkboxes that reads from approx 10 cells from workbook. Basically it is recalling properties of a product that the user can edit, some are text based and some are yes/no (true/false). How do I connect those Userform properties?

You can use Userforms like a class, and declare and instantiate them with New UserFormName. However, they're also a class that always has an instance in the workbook, that you can treat as though it were already declared with New. If you want to pull live information that is already inside it, you'll want to use that one.
For example, if the userform is called "Bacon", and contains some methods that do what you want(Solve_Everything_Click), and some internal data that has the information you need declared at the top(Public MyName As String, TheAnswer As Boolean), you can call it from another sub/function, in userform or out, as long as the code is in the same workbook, or references the book with the userform.
You can access the global instance of it, in the same workbook, by using its name, and accessing the things inside of it with ., keep an eye on the context help that comes up when you press ., which will tell you other methods and variables you might pull from it.
Sub PokeAtIt()
Bacon.TheAnswer = True
MsgBox Bacon.MyName & " Has the answer: " & Bacon.TheAnswer
Bacon.Solve_Everything
End Sub

Related

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;

Explanation of vba code in a word document having .docm extension

I have a Microsoft Word document with .docm format. A first glance it does not contain any macros (as when clicking the following on the ribbon; View -> Macros -> View macros pops up a window having an empty list).
But when enabling the Developer ribbon tab, and clicking the Visual Basic icon there, and then selecting the Document and ContentControlonEnter from the dropdowns in the VB window the following code appears:
Private Sub Document_ContentControlOnEnter(ByVal ContentControl As ContentControl)
Dim i As Long, j As Long
With ActiveDocument
If ContentControl.Title = "Classification" Then
ContentControl.DropdownListEntries.Clear
For i = 1 To .ContentControls.Count
If Left(.ContentControls(i).Title, 5) = "Level" Then
j = j + 1
ContentControl.DropdownListEntries.Add Text:=j & " - " & .ContentControls(i).Range.Text
End If
Next
End If
End With
End Sub
Selecting the other options in the dropdowns give only "blank" code (that is they contain only function declarations followed by theEnd keyword).
My question is what is the code meant to do?
*
Details:
The Word document in question contains hyperlinks to parts of the same document and a couple of links to Word files and Excel files of the same folder. It also contains lots of content control boxes, which I'm guessing is the focus of the code (as the code contains the ContentControl keyword)
Content controls can trigger macros when the user enters and exits them. Microsoft made the design decision that all content controls should trigger the same "events" - Document_ContentControlOnEnter / Document_ContentControlOnExit - and that the code in the event needs to check which content control was entered / exited.
Content controls are considered as part of the Document because the Document can trigger events. That's why they're in (and MUST be in) the ThisDocument class module.
(Note: View Macros can only show you PUBLIC SUB procedures with no arguments that are located in "normal" code modules. Any Private Sub, any Function, anything that takes a parameter and anything in a class module will not appear in that list. So you can't use that list to determine whether a document contains any code.)
The If ContentControl.Title = "Classification" Then checks which content control was entered. (Note: it usually makes more sense to use Select Case rather than If, especially when the event needs to distinguis between multiple content controls.) What's inside the If only executes if it was a content control with the Title "Classification". (Note that more than one content control can have the same Title, so more than one content control could run the code.)
If another content control is entered, the event is still fired, but nothing happens (in this case).
Catalin Pop correctly explained that the code is, in essence, "resetting" the drop down list.
Legacy Form fields use a similar pattern - macros can fire when the user enters/exits an form field. But the design for that was you had to create a Public Sub and assign that to the form field in the Properties.
I think the logic here is quite simple.
Basically the code searches for a content control named Classification within the entire document.
After it finds it, it clears all of its drowdown entries - like a reset.
After the cleaning part it again searches through the entire document for all content control that start with word "Level" and it collect the text for those controls and their order in appearance.
With this info collected it then fills the dropdown optios for the classification control above. (e.g. 1 Level X, 2 Level Y.. - based on what it finds in the document for controls starting with Level in their name)

Word 2010 VBA Checkboxes

Hopefully an easy one.
I've got a Word document littered with Checkboxes. Is there any way to access these checkboxes as a collection I can loop through and check/set properties such as ID and Value?
For reference, I've already tried the ActiveDocument.ContentControls collection; it's empty.
I've also tried ActiveDocument.Fields. This actually contains the same number of checkboxes as there are on the page, but I can't cast it to a Checkbox to access the properties I need.
One final question, is it possible to dynamically assign an event handler to the click?
Thank you in advance
The collection of all ActiveX fields embedded in your document (let's say its name is ThisDocument) is the collection ThisDocument.Fields. The items of this collection are objects of class Field.
To query the value of the objects in this collection, you'd use something along the lines of:
ThisDocument.Fields.Item(1).OLEFormat.Object.Value
To add code to the event handlers, just right-click on the ActiveX control, and then select "View Code" from the context menu. You'll get the Click event handler:
Private Sub CheckBox1_Click()
' empty if event handler not set, VBA code otherwise '
End Sub
To insert code dynamically, you need to manipulate the Code Module directly, and add the event handlers line by line. This might be tricky but, basically, you'd do something like:
ThisDocument _
.VBProject _
.VBComponents(ThisDocument.CodeName) _
.CodeModule _
.InsertLines(<<Line>>, <<String>>)
where <<Line>> is where you want to insert (number), and <<String>> is the text that you want to insert. Be careful not to insert in the middle of an existing Sub, Function or custom type definition (obviously).

How to pass the value (true/false) of checkboxes created run-time on a userform

I am trying to create a number of checkboxes on a UserForm after reading all the non-empty rows in an excel sheet. That means these checkboxes have to be created in run-time. I also want to put a CommandButton on the UserForm. What I want is that once the user presses this CommandButton, the code should be able to send to a subroutine the information on which checkboxes are checked and what their names are.
Could anyone help me with problem.
Instead of trying to dynamically create checkboxes on a userform (which I'm not even sure is possible) consider using a listbox with a ListStyle of fmListStyleOption and with MultiSelect turned on with fmMultiSelectMulti
Populate the Listbox using the AddItem Method
For i = 0 to 9
Me.lbxDivisions.AddItem
Me.lbxDivisions.List(i) = "Checkbox " & format(i)
Next i
And determine which items are checked via the Selected property:
For i = 0 To lbxDivisions.ListCount - 1
If lbxDivisions.Selected(i) Then
MsgBox "Item " & Format(i) & " is selected and has value " & lbxDivisions.List(i)
End If
Next i
You can programmatically add form controls (check boxes, listboxes, etc) to userforms. From within the form's code module,
Me.Controls.Add "Forms.CheckBox.1", "CheckBox1", True)
From any other code module, just reference the form by name, instead of Me, e.g.,
MyUserForm.Controls.Add "Forms.CheckBox.1", "CheckBox1", True)
Personally I would favor using a more dynamic control (like a list box or combobox) unless your task absolutely requires you to use check boxes. With dynamic controls you need to manage their size, location relative to other controls, resize the userform (if necessary), etc., and although it's kind of possible to add event handling to these controls (see here), that's really limited (e.g., if you expect you need to add 10 check boxes each of which do a different thing, you need to pre-write 10 check box subroutines. If you create 11 check boxes but only 10 pre-written routines, the last check box won't do anything. It would be easier to just create all the check boxes when designing the form, and then programmatically set them to Visible=True or Visible=False as circumstance requires.
So, I'd favor using a dynamic control like a listbox or combobox, but it is possible to add form controls like checkboxes at run-time, if you must.

Modeless MsgBox, Error trapping, .Find

I have a subroutine that searches for an occurrence of a string in another workbook. I'm trying to get an error message to pop up if the string can't be found (it's most likely due to spelling mistakes), as vbModeless, and allows to user to click on the cell in the searched sheet with the correct value. Then I'd like to resume the search with the new value.
I'm at the moment stuck on making my simple MsgBox to be modeless.
Can anyone help? So far I have (simplified):
With ...
On Error GoTo UserSelect
celladdress = .Range("a1:bb100").Find("searchstring").Address
And my error label:
UserSelect:
MsgBox("Select the cell with the correct spelling") vbModeless
newstring = ActiveCell.Value
searchstring = newstring
Resume
I think it's the Modeless MsgBox giving me grief.
I don't believe that you can use vbModeless with msgbox. That is for use with the Show method of a user form.
What you probably need to do is create a user form that has a refedit control and a button on it. They can then pick a cell with the refedit control. When the user clicks on the button set a public variable on the form with the cell reference the selected.
Then you you need to use ".Show vbModal" on the user form and read off the cell they selected from the form public variable.
Edit:
Actually, you shouldn't need the public variable as the refedit control should be a public property of the form anyway.
I'm not 100% sure on the requirements here. Given a search string of dgo and a worksheet with cells containing bird, cat and dog. Do you want the user to:
(a) edit the cell containing dog and change it to say dgo instead
This would use the modal form and RefEdit control outlined by andynormancx. Like a MsgBox, the modal form pauses the macro until the form is closed
(b) allow the user to click on the cell containg dog and then re-run the search with dog as the search term
This is more complicated. I think that you would need to look at events here. This is fine if your subroutine is pretty much standalone but if it is part of a larger program then this could require substantial rewriting