Connect Bookmark to another - vba

I have word file with VBA userform with one ComboBox1, In ComboBox1 I wrote this code for three options
Private Sub UserForm_Initialize()
ComboBox1.List = Array("Mr.", "Ms.", "Miss")
End Sub
and I have 2 Bookmarks with name Bookmark1 and Bookmark
I wrote code
Private Sub CommandButton1_Click()
Dim Bookmark1 As Range
Set Bookmark1 = ActiveDocument.Bookmarks("Bookmark1").Range
Bookmark1.Text = Me.ComboBox1.Value
End Sub
The ComboBox1 is connected to the Bookmark1 (Whatever my choice from ComboBox will appear in Bookmark1).
What I want is when Bookmark1 = "Mr." then Bookmark2 should be changed to "Manger" , and if choose "Mis." should be Changed to "student", and if choose "Miss." should be Changed to "Job seeker"

I solved it by writing a Case statement.
The code is:
Private Sub CommandButton1_Click()
Dim Bookmark1 As Range
Dim Bookmark2 As Range
Set Bookmark1 = ActiveDocument.Bookmarks("Bookmark1").Range
Bookmark1.Text = Me.ComboBox1.Value
Set Bookmark2 = ActiveDocument.Bookmarks("Bookmark2").Range
Bookmark2.Text = Me.ComboBox1.Value
Select Case Bookmark1
Case "Mr": Bookmark2.Text = "Manger"
Case "Mis": Bookmark2.Text = "student"
Case "Miss": Bookmark2.Text = "Job seeker"
End Select
End Sub

Related

Get the selected item of a dropdown in a custom menu (and run a macro accordingly)

I have this simple menu setup and I am trying to run a macro based on user selection of the label item from a dropdown list. The OnAction only works on the entire dropdown object and not executing macros per dropdown-list-item selection:
Private Sub Worksheet_BeforeRightClick(ByVal Target As Range, Cancel As Boolean)
Dim oMenu As CommandBar
Set oMenu = CommandBars.Add("", msoBarPopup, , True)
Dim cbcm1 As CommandBarButton
Set cbcm1 = oMenu.Controls.Add(Type:=msoControlButton, Temporary:=True)
cbcm1.Caption = "Add new label"
cbcm1.OnAction = "AddNewLabel"
Dim cbcm2 As CommandBarComboBox
Set cbcm2 = oMenu.Controls.Add(Type:=msoControlDropdown, Temporary:=True)
cbcm2.Caption = "Select label:"
cbcm2.AddItem "NVPE" ' << this should run a macro that adds a 'NVPE' in some other range..
cbcm2.AddItem "COMP" ' << this should run a macro that adds a 'COMP' in some other range.. and so on.
cbcm2.AddItem "HOLD"
cbcm2.AddItem "INPROG"
cbcm2.AddItem "CANC"
cbcm2.Width = 150
cbcm2.ListIndex = 1 'default
cbcm2.OnAction = "NewCommand_OnAction"
cbcm2.Style = msoComboLabel
oMenu.ShowPopup 'display the menu
Cancel = True
End Sub
Any suggestions?
I can't think of a way to get the selected index or value (caption) of the selected item.
Thanks!
All you are missing is a Select Case in your event handler. Using your existing code, simply add the string cbcm2Text and set it to equal cbcm2.Text. Then, add the necessary Select Case prior to your Cancel = True statement to call your macros.
Example:
Private Sub Worksheet_BeforeRightClick(ByVal Target As Range, Cancel As Boolean)
Dim oMenu As CommandBar
Dim cbcm2Text As String
Dim cbcm1 As CommandBarButton
Dim cbcm2 As CommandBarComboBox
Set oMenu = CommandBars.Add("", msoBarPopup, , True)
Set cbcm1 = oMenu.Controls.Add(Type:=msoControlButton, Temporary:=True)
Set cbcm2 = oMenu.Controls.Add(Type:=msoControlDropdown, Temporary:=True)
cbcm1.Caption = "Add new label"
cbcm1.OnAction = "AddNewLabel"
cbcm2.Caption = "Select label:"
cbcm2.AddItem "NVPE"
cbcm2.AddItem "COMP"
cbcm2.AddItem "HOLD"
cbcm2.AddItem "INPROG"
cbcm2.AddItem "CANC"
cbcm2.Width = 150
cbcm2.ListIndex = 1
cbcm2.OnAction = "NewCommand_OnAction"
cbcm2.Style = msoComboLabel
oMenu.ShowPopup
cbcm2Text = cbcm2.Text
Select Case cbcm2Text
Case "NVPE"
Call NVPE
Case "COMP"
Call COMP
Case "HOLD"
Call HOLD
Case "INPROG"
Call INPROG
Case "CANC"
Call CANC
End Select
Cancel = True
End Sub
Sub NVPE()
MsgBox "You selected NVPE"
End Sub
Sub COMP()
MsgBox "You selected COMP"
End Sub
Sub HOLD()
MsgBox "You selected HOLD"
End Sub
Sub INPROG()
MsgBox "You selected INPROG"
End Sub
Sub CANC()
MsgBox "You selected CANC"
End Sub
The preceding code produces the following result:
Here is the relevant Microsoft Documentation if you would like to see more examples of how to utilize combobox change events.

VBA Populating Combobox With Alternative Text

New to VBA so please forgive what may seem a simple question.
Using MS Word I have produced a simple form. I have a ComboBox that is populated via an array. This works fine. What I am trying to achieve is when an option is selected in this ComboBox, an alternative text entry is actually placed into the document at a Bookmark named Advice.
I am using Bookmarks as placemarkers in the Word document. At the moment the value already defined in the ComboBox is being place in the document, instead of the alternative.
Here is my code.
Dim myArray1() As String
'Use Split function to return a zero based one dimensional array
myArray1 = Split("advice for option one|advice for option two|" _
& "advice for option three", "|")
'Use List method to populate listbox
ComboBox2.List = myArray1
Exit Sub
If ComboBox2.List = "advice for option one" Then
Advice.Text = "This piece of text for option one. It's much longer than that in the DropBox, but it is what is needed."
ElseIf ComboBox2.List = "advice for option two" Then
Advice.Text = "This piece of text for option two. It's much longer than that in the DropBox, but it is what is needed."
ElseIf ComboBox2.List = "advice for option three" Then
Advice.Text = "This piece of text for option three. It's much longer than that in the DropBox, but it is what is needed."
Else
Advice.Text = ""
End If
lbl_Exit:
Exit Sub
End Sub
I'm sure I'm doing something really silly that is stopping this from working.
Thanks!
Sorry, I've just realised that I had missed the key part. Unsurprisingly this still doesn't work.
I've provided the rest of it and included your suggestion.
Private Sub CancelBut_Click()
UserForm.Hide
End Sub
Private Sub EnterBut_Click()
Dim number As Range
Set number = ActiveDocument.Bookmarks("number").Range
number.Text = Me.TextBox1.Value
Dim Name As Range
Set Name = ActiveDocument.Bookmarks("Name").Range
Name.Text = Me.TextBox2.Value
Dim Name1 As Range
Set Name1 = ActiveDocument.Bookmarks("Name1").Range
Name1.Text = Me.TextBox2.Value
Dim Address As Range
Set Address = ActiveDocument.Bookmarks("Address").Range
Address.Text = Me.TextBox3.Value
Dim ReportDate As Range
Set ReportDate = ActiveDocument.Bookmarks("ReportDate").Range
ReportDate.Text = Me.TextBox4.Value
Dim Location As Range
Set Location = ActiveDocument.Bookmarks("Location").Range
Location.Text = Me.TextBox5.Value
Dim Reason As Range
Set Reason = ActiveDocument.Bookmarks("Reason").Range
Reason.Text = Me.ComboBox1.Value
Dim Advice As Range
Set Advice = ActiveDocument.Bookmarks("Advice").Range
Advice.Text = Me.ComboBox2.Value
Dim Office As Range
Set Office = ActiveDocument.Bookmarks("Office").Range
Office.Text = Me.TextBox6.Value
Me.Repaint
UserForm.Hide
End Sub
Private Sub ToggleButton1_Click()
If ToggleButton1.Value = True Then
ComboBox2.Visible = True
Else
ComboBox2.Visible = False
End If
End Sub
Private Sub UserForm_Initialize()
Dim myArray() As String
'Use Split function to return a zero based one dimensional array
myArray = Split("problem1|problem2|problem3|problem4|" _
& "problem5|problem6|problem7|problem8|problem9|" _
& "problem10|problem11|problem12|problem13|problem14", "|")
'Use List method to populate listbox
ComboBox1.List = myArray
Dim myArray1() As String
'Use Split function to return a zero based one dimensional array
myArray1 = Split("advice for option one|advice for option two|" _
& "advice for option three", "|")
'Use List method to populate listbox
ComboBox2.List = myArray1
End Sub
Private Sub ComboBox2_Change()
Dim Advice As Range
If ActiveDocument.Bookmarks.Exists("Advice") = True Then
Set Advice = ActiveDocument.Bookmarks("Advice").Range
Select Case ComboBox2.Value
Case "advice for option one":
Advice.Text = "This piece of text for option one."
Case "advice for option two":
Advice.Text = "This piece of text for option two."
Case "advice for option three":
Advice.Text = "This piece of text for option three."
End Select
ActiveDocument.Bookmarks.Add "Advice", Advice
End If
End Sub
You haven't shown the context in which the code is run. What's the sub, and how is it getting called? And you haven't what type of object the Advice variable is or how it got set. Your code seems to be setting the combo items and trying to act on a combo selection in the same sub. That won't work.
You should create an event procedure within the form to populate the combo when the dialog is initialized.
Private Sub UserForm_Initialize()
Dim myArray1() As String
'Use Split function to return a zero based one dimensional array
myArray1 = Split("advice for option one|advice for option two|" _
& "advice for option three", "|")
'Use List method to populate listbox
ComboBox1.List = myArray1
End Sub
The have another event procedure within the form for a combo change event:
Private Sub ComboBox1_Change()
Dim Advice As Range
If ActiveDocument.Bookmarks.Exists("advice") = True Then
Set Advice = ActiveDocument.Bookmarks("advice").Range
Select Case ComboBox1.Value
Case "advice for option one":
Advice.Text = "This piece of text for option one."
Case "advice for option two":
Advice.Text = "This piece of text for option one."
Case "advice for option three":
Advice.Text = "This piece of text for option one."
End Select
End If
End Sub
This will replace the bookmark placeholder text with the indicated text. Note: the replacement will get rid of the bookmark as well, so it will only work once unless you reset the bookmark. If you want the bookmark to remain, you need to recreate it. The range object hasn't changed, so you can use that to create the new bookmark:
Private Sub ComboBox1_Change()
Dim Advice As Range
If ActiveDocument.Bookmarks.Exists("advice") = True Then
Set Advice = ActiveDocument.Bookmarks("advice").Range
Select Case ComboBox1.Value
Case "advice for option one":
Advice.Text = "This piece of text for option one."
Case "advice for option two":
Advice.Text = "This piece of text for option two."
Case "advice for option three":
Advice.Text = "This piece of text for option three."
End Select
ActiveDocument.Bookmarks.Add "advice", Advice
End If
End Sub
So now, after you select an option in the combo box, the text in the doc will be updated, and the bookmark will be reset to the new text. So, you can make a different choice, and the text will be updated again.
Private Sub ComboBox2_Change()
Dim Advice As Range
If ActiveDocument.Bookmarks.Exists("Advice") = True Then
Select Case ComboBox2.Value
Case "advice for option one":
***Advice.Text = "This piece of text for option one."***
Case "advice for option two":
Advice.Text = "This piece of text for option two."
Case "advice for option three":
Advice.Text = "This piece of text for option three."
End Select
End If
End Sub
You are over writing the text at the bookmark with the following code which is probably run when the form is closed.
Private Sub EnterBut_Click()
...
Set Advice = ActiveDocument.Bookmarks("Advice").Range
Advice.Text = Me.ComboBox2.Value
...
End Sub

Improve 33 checkbox code subs to few? (Checkbox for auto-date in bookmarks)

:)
Im new to VBA!
I have a working code for inserting date where i have a bookmark when using a checkbox (ActiveX). Problem is i have 33 checkboxes (I actually wish for 33x2. one for yes and one for no). So i ended up with 33 Subs and 33 bookmarks. I bet this code can be more efficient braking it down to just a few subs. Annyone has anny idea if it can be done?
The code under is the first of 33 repeating subs where Sub and bookmark name is agi1, agi2 agi3.....
Private Sub agi1_Click()
Dim rngFormat As Range
Set rngFormat = ActiveDocument.Range( _
Start:=ActiveDocument.Bookmarks("agi1").Range.Start, _
End:=ActiveDocument.Bookmarks("agi1").Range.End)
With rngFormat
.Font.Size = 8
End With
Dim v
Dim BMRange As Range
v = ThisDocument.agi1.Value
'Sjekke om boks er sjekket eller ikke
If v = True Then
'Sett inn dato i bokmerke
Set BMRange = ActiveDocument.Bookmarks("agi1").Range
With Selection.Font
.Size = 9
End With
BMRange.Text = (Format(Date, "dd.mm.yyyy"))
Else
'Erstatte dato med tom tekst hvis boks ikke er sjekket
Set BMRange = ActiveDocument.Bookmarks("agi1").Range
BMRange.Text = " "
End If
'Sett inn bokmerke på nytt
ActiveDocument.Bookmarks.Add "agi1", BMRange
End Sub
You could use event sinking, maybe to.
In an normal module, create a collection and populate it to hold the classes that will control the check box events.
In this have the code, this will need to be run on opening the document, something early in it's life to populate the collection.
Public col As Collection
Public Sub SETUP()
Dim o As InlineShape
Dim c As MSForms.CheckBox
Dim cust As clsCustomCheckBox
Set col = New Collection
For Each o In ActiveDocument.InlineShapes
Set c = o.OLEFormat.Object
Set cust = New clsCustomCheckBox
cust.INIT c
col.Add cust
Next o
End Sub
and then have a class module called clsCustomCheckBox and have it's code as
Private WithEvents c As MSForms.CheckBox
Public Function INIT(cmdIN As MSForms.CheckBox)
Set c = cmdIN
End Function
Private Sub c_Click()
MsgBox "Here you can get the name " & c.Name
End Sub
This will divert each checkbox click to the classes c_click rather than it's own.
So for you
Dim rngFormat As Range
Set rngFormat = ActiveDocument.Range( _
Start:=ActiveDocument.Bookmarks(c.name).Range.Start, _
End:=ActiveDocument.Bookmarks(c.name).Range.End)
With rngFormat
.Font.Size = 8
End With
.......
ActiveX controls always register their event handlers like so:
Private Sub NameOfTheControl_NameOfTheEvent({args})
If you rename the handler, the control stops working - because the name of the handler must be formed as above, with an underscore separating the name of the control and the name of the handled event.
So if your controls must exist at compile-time, there's no way around it: for 33 controls you need 33 handlers.
That doesn't mean you need that huge procedure repeated 33 times!
Extract a procedure. Select the entire body of that handler, cut it.
Now make a new procedure prototype:
Private Sub HandleCheckBoxClick(ByVal controlName As String)
End Sub
And paste the body in there. Then replace all the places you have a hard-coded "agi1" with a reference to this controlName parameter:
Dim rngFormat As Range
Set rngFormat = ActiveDocument.Range( _
Start:=ActiveDocument.Bookmarks(controlName).Range.Start, _
End:=ActiveDocument.Bookmarks(controlName).Range.End)
With rngFormat
.Font.Size = 8
End With
'...
The places where you're referring to the control using its programmatic name will be a bit harder:
v = ThisDocument.agi1.Value
You can get the MSForms.CheckBox control through the ThisDocument.InlineShapes collection, but that won't let you find a checkbox by its name, so you need a function that can do it for you:
Private Function FindCheckBoxByName(ByVal controlName As String) As MSForms.CheckBox
Dim sh As InlineShape
For Each sh In ThisDocument.InlineShapes
If TypeOf sh.OLEFormat.Object Is MSForms.CheckBox Then
If sh.OLEFormat.Object.Name = controlName Then
'return the MSForms control:
Set FindControlByName = sh.OLEFormat.Object
End If
End If
Next
And now you can do this:
Dim cb As MSForms.ChecBox
Set cb = FindCheckBoxByName(controlName)
If cb Is Nothing Then
MsgBox "No ActiveX CheckBox control named '" & controlName & "' was found in ThisDocument."
Exit Sub
End If
v = cb.Value
Once all references to the ActiveX control are parameterized, your 33 handlers can now look like this:
Private Sub agi1_Click()
HandleCheckBoxClick "agi1"
End Sub
Private Sub agi2_Click()
HandleCheckBoxClick "agi2"
End Sub
'...
Private Sub agi33_Click()
HandleCheckBoxClick "agi33"
End Sub
Alternatively, you could have the checkboxes created at run-time, and then have their Click event handled in a dedicated class module, but that's a little bit more involved ;-)

VBA code simplification

I have the following macro
Private Sub ComboBox1_Change()
If ComboBox1 = "Berlin" Then
Range("C20").Activate
Else
If ComboBox1 = "Paris" Then
Range("C100").Activate
Else
If ComboBox1 = "London" Then
Range("C150").Activate
End If
End If
End If
End Sub
This macro takes the value from a dropdown menu and goes to the cell, where the value is. First question is:
How can I take the values from the cells and not write them specifically in the code?
Second question is:
How can I simplify the procedure and not write for every value an IF?
First, you probably don't actually want to Activate the range! See this answer:
How to avoid using Select in Excel VBA macros
Secondly, your code...
Your Code
Private Sub ComboBox1_Change()
If ComboBox1 = "Berlin" Then
Range("C20").Activate
Else
If ComboBox1 = "Paris" Then
Range("C100").Activate
Else
If ComboBox1 = "London" Then
Range("C150").Activate
End If
End If
End If
End Sub
Using ElseIf
Private Sub ComboBox1_Change()
If ComboBox1 = "Berlin" Then
Range("C20").Activate
ElseIf ComboBox1 = "Paris" Then
Range("C100").Activate
ElseIf ComboBox1 = "London" Then
Range("C150").Activate
End If
End Sub
See documentation: https://msdn.microsoft.com/en-us/library/office/gg251599.aspx
Not hard-coding the values
Private Sub ComboBox1_Change()
Dim rng as Range
Set rng = Nothing
Set rng = ActiveSheet.Range("C:C").Find(ComboBox1.Text)
If Not rng Is Nothing Then
' As I say, you probably don't actually want to use Activate!
rng.Activate
End If
End Sub
See more about the Range object here:
https://msdn.microsoft.com/en-us/library/office/ff838238.aspx
It has useful methods like Address or Value for common use in VBA. The Find function returns a Range object or Nothing if the given value isn't found in the given range.
How can I simplify the procedure and not write for every value an IF?
If you need to test your ComboBox repeatedly (like your If-ElseIf structure), you can use the SELECT CASE to simplify your code:
Private Sub ComboBox1_Change()
Select Case ComboBox1
Case Is = "Berlin"
Range("C20").Activate
Case Is = "Paris"
Range("C100").Activate
Case Is = "London"
Range("C150").Activate
Case Else
Range("C1").Activate
End Select
End Sub
This looks at the value of ComboBox1 and picks the appropriate section to run. For example, if ComboBox1 = "Paris", it skips to the "Paris" case and only runs that section (Range("C100").Activate).
This makes it much easier to add more items to your options, and it reduces the clutter of lots of If-ElseIf-Else lines.
Edit: As mentioned by Wujaszkun, adding the Case Else section handles a ComboBox1 value that was not one of the specified cases.

Copying cell value to textbox vba

I have been trying to write a macro that will dynamically fill a textbox on a new sheet will the value of a cell from another sheet.
I have managed to get it working using this:
Sub copyDetail()
' Define variables
Dim pre As Worksheet
Dim des As Worksheet
Set pre = Sheets("Presentation")
Set des = Sheets("Description")
Dim i As Integer
Dim lbl As String
' Scroll through labels and copy where boolean = 1
For i = 2 To 17
If des.Cells(i, 2) = 1 Then
lbl = des.Cells(i, 11)
Sheets("Presentation").Select
ActiveSheet.Shapes.Range(Array("TextBox 1")).Select
Selection.Text = lbl
Else
End If
Next i
End Sub
I basically want to be able to do exactly what this does but without using select all the time as this changes sheets and slows down my code (I have many other sub's to run alongside this one). I've tried things like defining the textbox using this:
Dim myLabel As Object
Set myLabel = pre.Shapes.Range(Array("TextBox 1"))
But then I get an "object doesn't support this property or method" error when I try and call:
myLabel.Text = lbl
You can set the text of a TextBox like so:
ActiveSheet.Shapes("TextBox 1").TextFrame.Characters.Text = "Hello world"
You can set-up a little helper Sub in a Module to make the code re-usable:
Public Sub SetTextBoxText(ws As Worksheet, strShapeName As String, strText As String)
Dim shp As Shape
On Error Resume Next
Set shp = ws.Shapes(strShapeName)
If Not shp Is Nothing Then
shp.TextFrame.Characters.Text = strText
Else
Debug.Print "Shape not found"
End If
End Sub