Beginner level question
I have managed to dynamically add buttons to a form with values from an XML, as I do not knwo how many buttons or what button names will be used I am hitting the following roadblock
I can generate the buttons with each his own commond behind it but when i try to do a different action on first and second click (even or uneven) or (clicked or not clicked) the values transfer to the other buttons
Click the first button once, it performs the first action, then go to the second button, the second button thinks you already clicked it and performs the second action instead of the first action
on load i read the xml and create the buttons
Public Sub Button_Form_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'Read XML into Dataset
Try
xmlFile = XmlReader.Create(XML_file, New XmlReaderSettings())
ds.ReadXml(xmlFile)
xmlFile.Close()
Catch ex As Exception
WriteErrors(ex)
End Try
'Create buttons to add to the aray of buttons before adding them to the form in Private Sub create_button()
For i = 0 To ds.Tables(0).Rows.Count - 1
ButtonNumber = (ds.Tables(0).Rows(i).Item(0))
NaamX = (ds.Tables(0).Rows(i).Item(1)).ToString
CommandX = (ds.Tables(0).Rows(i).Item(2)).ToString
'Create buttons
Dim new_Button As New Button
new_Button.Text = NaamX
new_Button.Name = CommandX
new_Button.Location = New Point(Location_control.X + 10,
Location_control.Y)
new_Button.Width = 155
Location_control.Y += new_Button.Height + 10
'Add an action to the button
AddHandler new_Button.Click, AddressOf myButtonHandler_Click
'Add button to list of buttons
ButtonCount = ButtonCount + 1
ListOfButtons.Add(new_Button)
Next
'After creating all buttons and adding to the list of buttons, create them on the form
create_button()
'Set Form size according to ButtonCount
Dim formSize As Integer = (ButtonCount * 33) + 100
Me.Size = New Size(187, formSize)
Me.TopMost = True
Me.Refresh()
End Sub
Then I add an action to it, but the clicked values transfers over to all buttons
'Add actions to the buttons
Public Sub myButtonHandler_Click(ByVal sender As Object, ByVal e As EventArgs)
'Check if the map file exists,if not do not exit app
MapFile = sender.name
'Get file details to check the extension
FileDetail = My.Computer.FileSystem.GetFileInfo(MapFile)
Dim FoundFlag As Integer = 0
Look4File(MapFile, False, FoundFlag)
'If map file is not found do nothing on button click
If Not FoundFlag = 1 Then
'Check if the extension is MAP
If FileDetail.Extension = ".map" Then
'If map file is found
'Add to a list of maps used to detach all
Dim clicked As Boolean
If clicked = False Then
Maps2Detach.Add(MapFile)
'Gather the information to send to ExecuteTheCommand (exe, arguments, button used)
StartArgument = " ATTACH REFERENCE FILE -F " & " " & Chr(34) & MapFile & Chr(34)
ButtonNaam = sender.text
ExecuteTheCommand(CadCommandFile, StartArgument, ButtonNaam)
clicked = True
Else
Maps2Detach.Remove(MapFile)
'Gather the information to send to ExecuteTheCommand (exe, arguments, button used)
StartArgument = " detach reference file -N " & " " & Chr(34) & MapFile & Chr(34)
ButtonNaam = sender.text
ExecuteTheCommand(CadCommandFile, StartArgument, ButtonNaam)
clicked = False
End If
Else
ErrorMessage = "Not a MAP file: " & MapFile
'Write to log
Log_EveryThing(ErrorMessage)
'Show msgbox
MessageBox.Show(ErrorMessage, "Not a MAP file")
End If
Else
End If
End Sub
i am bit stuck trying to differ the click on buttons
Related
This is the situation where I'm in. I have a Form in my Microsoft Access which has one Control. This control is a ListView named lstvtest. I have populated the ListView dynamically. So when I open the Form everything is fine, I can see that the ListView has been populated successfully and I can also see the column's heading (which I have set it in the ListView setting to show the column Heading).
Now, I have a blank Report with one control. This Control is a ListView named lstvtest. I have used the exact code I used for the Form, but when I open the Report. Few things happens
I don't see the column heading
I only see one word in the ListView.
Here is my Form VBA Code:
Option Compare Database
Private myLV As MSComctlLib.ListView
Private li As ListItem
Private Sub Form_Load()
Dim i As Integer
Set myLV = lstvtest.Object
myLV.ListItems.Clear
myLV.Appearance = cc3D
'add some data
For i = 0 To 30
Set li = myLV.ListItems.Add(, "k" & i, "Test " & i)
li.ListSubItems.Add , , "Admin " & i
li.ListSubItems.Add , , "B " & i
Next
'report view
myLV.View = 3
End Sub
And here is my Report VBA Code
Option Compare Database
Private myLV As MSComctlLib.ListView
Private li As ListItem
Private Sub Report_Load()
Dim i As Integer
Set myLV = lstvtest.Object
myLV.ListItems.Clear
myLV.Appearance = cc3D
'add some data
For i = 0 To 30
Set li = myLV.ListItems.Add(, "k" & i, "Test " & i)
li.ListSubItems.Add , , "Admin " & i
li.ListSubItems.Add , , "B " & i
Next
'report view
myLV.View = 3
End Sub
Here is the screenshot of the Form when its opened
and here is the screenshot for the Report when its opened
So my question how come the ListView has not been populated in the Report? Thank you in Advance.
The code in my question in inspired by the solution in the answer provided by this question:
How to add a menu item to the default right click context menu
I have a ListBox object on a form showing a list of Actions. I want the user to be able to right click an item of this list to show a contextual menu where he can either :
open a new form where he can view and edit the action (corresponds to the execution of a double click event on the list item)
delete the item from the list
Private Sub List_actions_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single
'set up commandBar
Dim combo As CommandBarControl
'Since it may have been defined in the past, it should be deleted,
'or if it has not been defined in the past, the error should be ignored
On Error Resume Next
CommandBars("RCActionContextMenu").Delete
On Error GoTo 0
'Make this menu a popup menu
With CommandBars.Add(Name:="RCActionContextMenu", Position:=msoBarPopup)
Set combo = .Controls.Add(Type:=msoControlButton)
combo.BeginGroup = True
combo.Caption = "View action" ' Add label the user will see
combo.OnAction = "List_actions_DblClick" 'Add the name of a function to call
Set combo = .Controls.Add(Type:=msoControlButton)
combo.Caption = "Delete action"
combo.OnAction = DelAction()
End With
If Button = acRightButton Then
DoCmd.CancelEvent
CommandBars("RCActionContextMenu").ShowPopup
End If
End Sub
Public Function DelAction()
If Not IsNull(Me.Controls("RCActionContextMenu").Column(0)) Then
CurrentDb.Execute "DELETE * FROM T_ACTIONS " & _
"WHERE ID = " & List_actions.Column(9) & ";"
MsgBox "Action supprimée", vbInformation, "Information"
End If
End Function
Private Sub List_actions_DblClick(Cancel As Integer)
Dim vStatus As String
'Get the record's index of the action
rowNumber = Me.List_actions.ListIndex + 1
id_action = List_actions.Column(9, rowNumber)
vStatus = List_actions.Column(5, rowNumber)
'Open the action
DoCmd.OpenForm "F_ACTIONS", , , "[ID] = " & List_actions.Column(9)
Form_F_ACTIONS.Effective_date.Visible = Effdatefunction(vStatus)
End Sub
The problem i get is that the DelAction() function is executed before the pop-up is shown and i get a run-time error 2465 stating "Microsoft Access can't find the field 'RCActionContextMenu' referred to in your expression."
I've tried repalcing the row combo.OnAction = DelAction() by combo.OnAction = "DelAction". It results in the conextual menu showing itself but nothing happens when i click on either button.
There are a few problems here.
combo.OnAction = DelAction()
This will call the function, as you have seen. You need to set a string here.
combo.OnAction = "DelAction()"
This still won't work, since DelAction() is in your form module.
Either move the function to a public module, with parameters, or hardcoding the object names there,
combo.OnAction = "DelAction(""MyFormName"", ""List_actions"")"
or try (not sure if this works):
combo.OnAction = "Form_YourFormName_DelAction()"
It's the same with "List_actions_DblClick" - the function needs to be called "from the outside", like from the Immediate window.
If Not IsNull(Me.Controls("RCActionContextMenu").Column(0)) Then
You context menu command bar isn't a control, what you want is the list box:
If Not IsNull(Me.Controls("List_actions").Column(0)) Then
or simply
If Not IsNull(Me!List_actions.Column(0)) Then
After deleting an action, you need to requery the listbox.
CurrentDb.Execute "DELETE * FROM T_ACTIONS " ...
Me!List_actions.Requery
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.
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):
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