VBA Create a macro that creates new macros - vba

I have a macro that inserts Image controls on a form.
When these controls are clicked the user is asked to select an image file using the GetOpenFileName dialog box. The selected image is loaded into the control and the file path is added to column B on Sheet2.
When the Image control is clicked again the selected image is loaded to an Image control on a second form and displayed.
How do I add or attach the required code to each image control so the Click events will work?
The code I have so far is below:
Sub macroA1()
Application.ScreenUpdating = False
Application.DisplayStatusBar = False
Application.EnableEvents = False
Set miesto = Sheets("Sheet2").Range("B2")
strfilename = Sheets("Sheet2").Range("B2").Value
If strfilename = "" Then
strfilename = Application.GetOpenFilename(filefilter:="Tiff Files(*.tif;*.tiff),*.tif;*.tiff,JPEG Files (*.jpg;*.jpeg;*.jfif;*.jpe),*.jpg;*.jpeg;*.jfif;*.jpe,Bitmap Files(*.bmp),*.bmp", FilterIndex:=2, Title:="Select a File", MultiSelect:=False)
Sheets("Sheet2").Range("B2").Value = strfilename
ElseIf strfilename = "False" Then
strfilename = Application.GetOpenFilename(filefilter:="Tiff Files(*.tif;*.tiff),*.tif;*.tiff,JPEG Files (*.jpg;*.jpeg;*.jfif;*.jpe),*.jpg;*.jpeg;*.jfif;*.jpe,Bitmap Files(*.bmp),*.bmp", FilterIndex:=2, Title:="Select a File", MultiSelect:=False)
Sheets("Sheet2").Range("B2").Value = strfilename
Else
Sheets("Sheet2").Range("B2").Value = strfilename
End If
On Error Resume Next
UserForm1.Image1.Picture = LoadPicture(strfilename)
If strfilename = "False" Then
MsgBox "File Not Selected!"
Exit Sub
Else
End If
UserForm1.Image1.PictureSizeMode = fmPictureSizeModeStretch
UserForm1.Show
Application.ScreenUpdating = True
Application.DisplayStatusBar = True
Application.EnableEvents = True
End Sub

Each Image control on your userform will need a click event. This single event is stored within a class module and attached to each Image control on the form.
Insert a class module, name it clsLoadImage and add the code below to it.
Public WithEvents Img As MSForms.Image 'Place at very top of module (after Option Explicit though).
Private Sub Img_Click()
Dim FullPath As String
With Img
'Only load the picture if the control is empty.
If .Picture Is Nothing Then
'Get the file path for the image.
FullPath = Application.GetOpenFilename
If Len(Dir(FullPath)) = 0 Then
MsgBox "No file find.", vbOKOnly + vbCritical
Else
.Tag = FullPath 'The Tag property can store extra info such as a text string.
'Store the path in last row of Sheet2 column B.
ThisWorkbook.Worksheets("Sheet2").Cells(Rows.Count, 2).End(xlUp).Offset(1) = FullPath
.Picture = LoadPicture(FullPath)
.PictureSizeMode = fmPictureSizeModeStretch
.Parent.Repaint
End If
Else
'If the image control isn't empty load the image
'into UserForm2 using the file path stored in
'the Tag property.
Load UserForm2
With UserForm2
With .Image1
.Picture = LoadPicture(Img.Tag)
.PictureSizeMode = fmPictureSizeModeStretch
.Parent.Repaint
End With
.Show
End With
End If
End With
End Sub
Next add a UserForm to the project. In the sample code I have left it named as UserForm1. Make the Height at at least 340 and fairly wide.
Add a CommandButton near the top and an Image control near the bottom (I put the Top at 218 for the image control).
These controls probably won't be included in your final solution but give different options depending on your requirements.
Add the below code to UserForm1.
This code will fire when you open the form.
The top part of the code will attach the Click event to any existing Image controls - such as the one that's placed near the bottom.
The bottom part of the code will create an Image control for each file path listed in Sheet2 column B and attach the Click event to it.
Note: Top is set as 134 placing them in the middle area of the form.
Public ImageControls As New Collection 'Place at very top of module (after Option Explicit though).
'Could execute when the form opens.
'''''''''''''''''''''''''''''''''''''''''''''''''
Private Sub UserForm_Initialize()
'Relies on image controls added at design time.
'Attaches the click event to each image control.
Dim Ctrl As Control
Set ImageControls = New Collection
Dim ImgEvent As clsLoadImage
For Each Ctrl In Me.Controls
If TypeName(Ctrl) = "Image" Then
Set ImgEvent = New clsLoadImage
Set ImgEvent.Img = Ctrl
ImageControls.Add ImgEvent
End If
Next Ctrl
''''''''''''''''''''''''''''''''''''''''''''
'Creates an image control for each file path
'in Sheet2 column B, loads the picture,
'stores the path in the tag property,
'attaches the click event.
Dim x As Long
Dim tmpCtrl As Control
For x = 2 To ThisWorkbook.Worksheets("Sheet2").Cells(Rows.Count, 2).End(xlUp).Row
'Add the control, name it and position it.
Set tmpCtrl = Me.Controls.Add("Forms.Image.1", "AddedInLoop_Image_" & x)
With tmpCtrl
.Left = .Width * (x - 2)
.Top = 134
.Picture = LoadPicture(ThisWorkbook.Worksheets("Sheet2").Cells(x, 2))
.PictureSizeMode = fmPictureSizeModeStretch
.Tag = ThisWorkbook.Worksheets("Sheet2").Cells(x, 2)
End With
'Attach the Click event to the control.
Set ImgEvent = New clsLoadImage
Set ImgEvent.Img = tmpCtrl
ImageControls.Add ImgEvent
Next x
Me.Repaint
End Sub
Add this code to UserForm1 as well to deal with the CommandButton that you added.
This will add an Image control each time you press the button.
Note - Top is set at 40 so they'll appear near the top of the form.
'Creates an image control and attaches
'a Click event to the control.
Private Sub CommandButton1_Click()
Dim CtrlCount As Long
Dim Ctrl As Control
Dim tmpCtrl As Control
Dim ImgEvent As clsLoadImage
'Count the Image controls so each
'new control has a unique name.
CtrlCount = 1
For Each Ctrl In Me.Controls
'NB: The InStr command is only needed so the controls
' added in the Initalise event aren't counted.
If TypeName(Ctrl) = "Image" And InStr(Ctrl.Name, "BtnClck_Image_") > 0 Then
CtrlCount = CtrlCount + 1
End If
Next Ctrl
'Add the control, name it and position it.
Set tmpCtrl = Me.Controls.Add("Forms.Image.1", "BtnClck_Image_" & CtrlCount)
With tmpCtrl
.Left = .Width * (CtrlCount - 1)
.Top = 40
End With
'Attach the Click event to the control.
Set ImgEvent = New clsLoadImage
Set ImgEvent.Img = tmpCtrl
ImageControls.Add ImgEvent
End Sub
Finally, add a second UserForm and add a single Image control named Image1 filling the form. I have left the form named as UserForm2.
To use:
Open UserForm1.
An Image control will be created for each full file path & name listed in column B of Sheet2. It will display the picture from the file path.
Pressing the button will create a blank Image control.
Clicking a blank Image control will open a dialog box asking you to select a file. The selected file will be loaded into the control and the file path added to column B on Sheet2.
Clicking an Image control that contains a picture will open UserForm2 with the image loaded into the Image control on that form.

Related

is there a way of changing the Font properties of a text box in access VBA on a continuous form?

I am writing an app that send reports to a word document, this is done by the usage of data that is displayed on a continuous form, one form has the data that display's the selected headers that will be printed onto the report, the user can then change the font style by the usage of the windows font window.
This all works fine, what I want to do now is then update the text box on the continuous form with the font styles that have been stored in the data table, so that the user can see the font and style they have selected.
I have tried various approaches to no success, the last method I have tried I will post below in code.
dim i as integer
Private Sub Form_Load()
i = 0
End Sub
Private Sub Form_Current()
i = i + 1
Me.txtchapterName.Tag = "ctrl" & i
End sub
Function SetFieldProperties()
Dim rst As Recordset
Dim ctrl As TextBox
Set rst = Me.Recordset
Set ctrl = Me.ActiveControl
If rst.RecordCount > 0 Then
If ctrl.Tag = Me.txtchapterName.Tag Then
ctrl.ForeColor = Nz(Me![TextFontColour], 0)
ctrl.FontName = Nz(Me![TextFontName], "Calibri")
ctrl.FontSize = Nz(Me![TextFontSize], 14)
ctrl.FontUnderline = Nz(Me![TextFontAlign], 0)
ctrl.FontBold = Nz(Me![TextFontBold], False)
End If
End If
End Function
Private Sub txtchapterName_AfterUpdate()
SetFieldProperties
End Sub
To some extent this works, but it will update all of the controls on the form with the font style.
For clarity my question is in the title of the post...
Thank you all in advance.
Mark.

VBa - Userform show same combobox on two pages

So I have a multipage where I want the same button to show on both pages. I could place it outside but the border from the multipage is so ugly so I tried to place everything on the pages. Unfortunately you can't name the items (such as the combobox) with the same name. Is there a workaround to remove the borders and just show the page names or have the same name on the item on two pages?
Had some fun with this goal.
Consider this UserForm in Editor with TabStrip, 2 frames and some other controls.
Frames are named from Frame0, Frame1, etc.
Assuming the Frame0 is the location reference and first to display when UserForm is displayed, code below will be what you want.
Code:
Option Explicit
Private Sub TabStrip1_Change()
Dim i As Long, lActiveTabIndex As Long
lActiveTabIndex = Me.TabStrip1.Value
For i = 0 To Me.TabStrip1.Tabs.Count - 1
Me.Controls("Frame" & i).Visible = (i = lActiveTabIndex)
Next i
End Sub
Private Sub UserForm_Initialize()
Dim i As Long
With Me
.Height = 288 ' Adjust to your desired height
' Align all FrameX top/left to same as Frame0, delete Caption and SpecialEffect
For i = 0 To Me.TabStrip1.Tabs.Count - 1
With Me.Controls("Frame" & i)
.Top = Me.Frame0.Top
.Left = Me.Frame0.Left
.Caption = ""
.SpecialEffect = fmSpecialEffectFlat
End With
Next i
End With
' Ensure frame for first tab is displayed
TabStrip1_Change
End Sub
Userform first load (didn't save screenshot, neither the workbook, sorry).
Next tab clicked:

Scroll to view buttons where there are a varying number of buttons

I have selected a Sentence.
1) The sentence can vary.
2) I have split each word of the sentence.
The code below creates a list of Word array from Selection.
Sub Seperate_Words()
Dim WrdArray() As String
WrdArray() = Split(Selection)
For i = LBound(WrdArray) To UBound(WrdArray)
strg = strg & vbNewLine & WrdArray(i)
Next i
MsgBox strg
End Sub
Now I want to add a Search button in front of each word.
In every situation, the length of a sentence would change and Userforms are Pre-specified that's why I can't use them.
Following Image shows how output should be
Now problem I am facing is adding a scroll bar in frame which dynamically changes if needed.
I have found a very interesting solution to this:
Create a UserForm (I have named mine "frmSearchForm")
Create a Frame on it (mine is "framTest")
Create a Classmodule and name it "clsUserFormEvents"
Add this Code to it:
Public WithEvents mButtonGroup As msforms.CommandButton
Private Sub mButtonGroup_Click()
'This is where you add your routine to do something when the button is pressed
MsgBox mButtonGroup.Caption & " has been pressed" 'Just Example Code
End Sub
Then in the ThisDocument Module, add this code:
Dim mcolEvents As New Collection
Sub createButtonsOnForm()
Dim Cmd As msforms.CommandButton
'create instance of class
Dim cBtnEvents As clsUserFormEvents
'array for selection
Dim wordArr() As String
'get selection into array
wordArr = Split(Selection, " ")
Dim i As Integer
'counter for the top position of buttons
Dim topcounter As Integer
topcounter = 10
'loop through array
For i = LBound(wordArr) To UBound(wordArr) Step 1
'create button
Set Cmd = frmSearchForm.framTest.Controls.Add("Forms.CommandButton.1", "Test")
'Adjust properties of it
With Cmd
.Caption = wordArr(i)
.Left = 100
.Top = topcounter
.Width = 50
.Height = 20
End With
'Instantiate Class
Set cBtnEvents = New clsUserFormEvents
'Add cmd to event in class
Set cBtnEvents.mButtonGroup = Cmd
'Add buttonevent to collection so it won't get deleted in next iteration of the loop
mcolEvents.Add cBtnEvents
'increase the top position
topcounter = topcounter + 25
Next i
'show userform
frmSearchForm.Show
End Sub
Then if you run this sub, the selection gets splitted into the array, a button for every element is created(selection part as caption) and if you press the button, the method inside the class gets called, where you can use the mButtonGroup.Caption propery to get the value of button.
Example:
I have selected the Words "Test1" and "Test2", now when I run the Sub, the Form opens with 2 Buttons(Test1 and Test2):

Name of textbox depends on where it is located in an ArrayList

I'm using VBA to code an application for an Excel file. Put simply, I need the names of my textboxes to change depending on where a certain variable is in an ArrayList.
I have one textbox to start, when someone pushes a button it should add a textbox after the first one, and do this as many times as one presses the button. So the first box should be named tbx1, the second should be tbx2, the third tbx3, and so on.
Now when they press a different button located next to any of the boxes, it deletes that box and button and all boxes after that one are named one lower to make up for it.
Any ideas how to do this? I'm only assuming ArrayList is the best tactic, please correct me if there is a better way.
Here's an example that you can hopefully modify to your needs. I have a userform named UClassList with one commandbutton, cmdAdd, and one textbox, tbxClass_1.
Private mEventButtons As Collection
Public Property Get ClassMax() As Long
ClassMax = 75
End Property
Private Sub cmdAdd_Click()
Dim i As Long
For i = 2 To Me.ClassMax
'find the first invisible control and make it visible
If Not Me.Controls("tbxClass_" & i).Visible Then
Me.Controls("tbxClass_" & i).Visible = True
Me.Controls("cmdClass_" & i).Visible = True
Exit For 'stop after one
End If
Next i
End Sub
Private Sub UserForm_Initialize()
Dim i As Long
Dim tbx As MSForms.TextBox
Dim cmd As MSForms.CommandButton
Dim clsEventClass As CEventClass
Set mEventButtons = New Collection
'Add as many textboxes and commandbuttons as you need
'or you can do this part at design time
For i = 2 To Me.ClassMax
Set tbx = Me.Controls.Add("Forms.TextBox.1", "tbxClass_" & i, False)
tbx.Top = Me.tbxClass_1.Top + ((i - 1) * 25) 'use the first textbox as the anchor
tbx.Left = Me.tbxClass_1.Left
tbx.Width = Me.tbxClass_1.Width
tbx.Height = Me.tbxClass_1.Height
'Create a delete commandbutton
Set cmd = Me.Controls.Add("Forms.CommandButton.1", "cmdClass_" & i, False)
cmd.Top = tbx.Top
cmd.Left = tbx.Left + tbx.Width + 10
cmd.Width = 20
cmd.Height = tbx.Height
cmd.Caption = "X"
'add delete commandbutton to the event class so they all share
'the same click event code
Set clsEventClass = New CEventClass
Set clsEventClass.cmdEvent = cmd
mEventButtons.Add clsEventClass
Next i
End Sub
I have a custom class named CEventClass.
Public WithEvents cmdEvent As MSForms.CommandButton
Private Sub cmdEvent_Click()
Dim i As Long
Dim lThisIndex As Long
Dim tbxThis As MSForms.TextBox
Dim tbxPrev As MSForms.TextBox
Dim uf As UClassList
Set uf = cmdEvent.Parent
'get the number that was clicked
lThisIndex = Val(Split(cmdEvent.Name, "_")(1))
'loop from the next textbox to the end
For i = lThisIndex + 1 To uf.ClassMax
Set tbxThis = uf.Controls("tbxClass_" & i)
Set tbxPrev = uf.Controls("tbxClass_" & i - 1)
'if it's not visible, clear and hide
'the previous textbox
If Not tbxThis.Visible Then
tbxPrev.Text = vbNullString
tbxPrev.Visible = False
uf.Controls("cmdClass_" & i - 1).Visible = False
Else
'if it's visible, copy it's text to the one above
tbxPrev.Text = tbxThis.Text
End If
Next i
End Sub
Instead of adding and deleting and keeping track of a bunch of textboxes, I create all 75 (or fewer) at launch (or design time). Then I just make then visible or hide them as needed.
You can see the workbook I did this on here http://dailydoseofexcel.com/excel/ControlEventClass.xlsm

Excel - Returning the caption of the selected option button

Probably a silly question with a simple answer but I am a real novice when it comes to userforms.
I have "Frame 3" with 5 different option buttons (Dest1, Dest2, Dest3, Dest4, Dest5) After an option is selected, where is the caption value of the selected option stored? How can I access that with vba.
Thank you,
Josh
Here's just some example code you can use. Add your Option Buttons to groups, and then you can go from there. I used groups since you had multiple frames, and you can check based on group, and have multiple groups, and check which one's selected for each group.
Private Sub CommandButton1_Click()
Dim x As Control
' Loop through ALL the controls on the UserForm.
For Each x In Me.Controls
' Check to see if "Option" is in the Name of each control.
If InStr(x.Name, "Option") Then
' Check Group name.
If x.GroupName = "Grp1" Then
' Check the status of the OptionButton.
If x.Value = True Then
MsgBox x.Caption
Exit For
End If
End If
End If
Next
End Sub
You can also access the option buttons through the frame-ojbect that holds them (if you have other frames and controls you don't want to go through):
Option Explicit
Sub Test()
Dim oCtrl As Control
'***** Try only controls in Frame3
For Each oCtrl In Frame3.Controls
'***** Try only option buttons
If TypeName(oCtrl) = "OptionButton" Then
'***** Which one is checked?
If oCtrl.Value = True Then
'***** What's the caption?
Debug.Print "You have checked option " & oCtrl.Caption
Exit For
End If
End If
Next
End Sub
The Label Text associated with an Option Button is obtainable by using OptionButton1.Caption
If you are using a loop, just substitute the OptionButton1 with your variable for option buttons and it will pull through the one you need when conditions are met. eg:
For xitem = 1 To 5
xFrm = "OptionButton" & xitem
For Each fItem In Me.Controls
If fItem.Name Like xFrm Then
If fItem.Value Then
k = fitem.Caption
End If
End If
Next fItem
Next xitem
In my case, I wanted the caption of the toggle that was selected in an option group to be passed on to a subform filter. e.g. choosing toggle "black" filters subform to all cars where strColour = "black".
I ended up with this:
Private Sub OptionGroupName_Click()
Dim Caption As String
Caption = OptionGroupName.Controls.Item(OptionGroupName.Value - 1).Caption
Me.SubformName.Form.Filter = "[SubformField] = """ & Caption & """"
Me.SubformName.Form.FilterOn = True
End Sub
Not to dog pile on everyone else's options but I created a function that takes the radio group name and spits out the selected radios coresponding label caption. Was using it in Access not Excel.
Only works provided you name your controls similarly....
i.e. (lblRadioButton1 & optRadioButton1)
Function GetSelectedRadioButtonCaption(ByVal optionGroupName As OptionGroup) As String
Dim oCtrl As Control
Dim oCtrl2 As Control
Dim optionLabelName As String
Dim optionLabelObject As Label
Dim optionButtonObject As OptionButton
For Each oCtrl In optionGroupName.Controls
'***** Try only option buttons
If TypeOf oCtrl Is OptionButton Then
'***** Which one is checked?
Set optionButtonObject = oCtrl
If optionButtonObject.OptionValue = optionGroupName.Value Then
'***** What's the caption?
optionLabelName = Replace(oCtrl.Name, "opt", "lbl")
For Each oCtrl2 In optionGroupName.Controls
If oCtrl2.Name = optionLabelName Then
Set optionLabelObject = oCtrl2
GetSelectedRadioButtonCaption = optionLabelObject.caption
Exit For
End If
Next
End If
If GetSelectedRadioButtonCaption <> "" Then
Exit For
End If
End If
Next
Exit_GetSelectedRadioButtonCaption:
End Function