vba button - find which was clicked - vba

I have assigned macro to few buttons.
How can I find out inside macro which button was clicked?
I am doing as user form, where he can input peoples from family:
name1:
surname1:
name2:
surname2: |add next member|
I wish button to appear always in last row of the last added person.
For simplicity I think it is better to have like 100 empty forms in the
sheet but all invisible at the begining.
Then when user clicks add next member I simply make next rows visible,
and move button to next person. But to do that I need to know my current position.
Similar with deletion I would make rows invisible when remove button is clicked.
name1:
surname1: [remove]
name2:
surname2: [remove]
name3:
surname3: |add next member|
I need to know which remove button was clicked.
EDIT:
Found in web - what do you think, seems to be best /way
Dim r As Range
Set r = ActiveSheet.Buttons(Application.Caller).TopLeftCell
Range(Cells(r.Row, r.Column), Cells(r.Row, r.Column)).Select

I always write wrappers for each button that then call the macro in question.
Like so:
Public Sub StoreButton_Click()
Call StoreTransValues(ActiveSheet)
End Sub
If you have only one button for any one page, you can just get the ActiveSheet property, and it will be the button on that page.
Edit:
Here's the code to get and use the name of the calling button:
Dim ButtonText As String
ButtonText = Application.Caller
ActiveSheet.Shapes(ButtonText).Delete
You would use the .Move method to move the button.

I finally found the solution for determining which button in a Worksheet was pushed. Credit is due to Derk at http://www.ozgrid.com/forum/showthread.php?t=33351.
My final example code:
Sub HereIAm()
Dim b As Object
Dim cs, rs As Integer
Dim ss, ssv As String
Set b = ActiveSheet.Buttons(Application.Caller)
With b.TopLeftCell
rs = .Row
cs = .Column
End With
ss = Left(Cells(1, cs).Address(False, False), 1 - (ColNumber > 26)) & rs
ssv = Range(ss).Value
MsgBox "Row Number " & rs & " Column Number " & cs & vbNewLine & _
"Cell " & ss & " Content " & ssv
End Sub
If you don't need the cells label, Cells(rs,cs).Value works as well.

Dim button as a string:
button = ActiveSheet.Shapes(Application.Caller).Name

Since you have a macro wired to your button(s), I assume you know which button it is that was clicked. To get the location of the button, use this:
ActiveSheet.Shapes("ButtonName").TopLeftCell.Address
To move a button to a new location, use this:
Dim NewAddress as Range
NewAddress = ActiveSheet.Cells(5, 5) 'Or where ever you need it to go
ActiveSheet.Shapes("ButtonName").Left = NewAddress.Left
ActiveSheet.Shapes("ButtonName").Top = NewAddress.Top

Related

Populating header of Listbox from column header

1. Background & purpose
I'm creating a userform to display data from the Excel sheet("DATA") with table ("Tab1") of multi-columns like below picture.
In my form ("TaskMngUserForm"), after clicking on "Task List" button, all data from Tab1 will be displayed on Listbox1 as follows:
Column header in Tab1 will be displayed on Listbox1 as Header.
Data from 2nd row to end in Tab1 will be diplay on Listbox1 corresponding to each columns.
Also I'm adding an event for action "Listbox1_Click()" that returns "Data" sheet row corresponding to the selected Index, from the second column of the selected ListBox1 row.
UserForm and Listbox
2. Code
'4. Event for "Tasks List" button
Private Sub Button_TaskList_Click()
ListBox1.ColumnWidths = "20;100;80;100;60;100;80;80;80;200;200;200"
ListBox1.ColumnCount = 12
With ListBox1
'.ColumnHeads = True
.List = Sheets("DATA").Range("B2").CurrentRegion.Value
.RemoveItem (0)
.ColumnCount = Sheets("DATA").Cells(2, 2).CurrentRegion.Columns.Count
End With
Application.ScreenUpdating = True
Label25.Caption = "Total Tasks: " & (Worksheets("DATA").UsedRange.Rows.Count - 1)
End Sub
'6. Event for "Click Listbox" Action
Private Sub ListBox1_Click()
Dim strAddress As String
Dim dataSht As Worksheet
With Me
If .ListBox1.ListIndex <> -1 Then
Set dataSht = Sheets("DATA")
If IsNull(Me.ListBox1.Value) Then
Call MsgBox("You are selecting on blank row item" & vbNewLine & "Be careful!", vbInformation, "Notification")
Button_TaskList_Click
Else
strAddress = GetIndexRow(.ListBox1.List(.ListBox1.ListIndex, 0), dataSht.Columns("A"))
'<~~ GetIndexRow returns "Data" sheet row corresponding to the selected Index, which is got from the 2nd column of the selected ListBox row
TaskMngUserForm.txtIndex.Value = dataSht.Range("A" & strAddress).Value
TaskMngUserForm.cmbSource.Value = dataSht.Range("B" & strAddress).Value
TaskMngUserForm.cmbType.Value = dataSht.Range("C" & strAddress).Value
TaskMngUserForm.cmbCategory.Value = dataSht.Range("D" & strAddress).Value
TaskMngUserForm.cmbPriority.Value = dataSht.Range("E" & strAddress).Value
TaskMngUserForm.cmbTaskOwner.Value = dataSht.Range("F" & strAddress).Value
TaskMngUserForm.cmbStatus.Value = dataSht.Range("G" & strAddress).Value
TaskMngUserForm.txtOpenDate.Value = dataSht.Range("H" & strAddress).Value
TaskMngUserForm.txtCloseDate.Value = dataSht.Range("I" & strAddress).Value
TaskMngUserForm.txtSubject.Value = dataSht.Range("J" & strAddress).Value
TaskMngUserForm.txtDescription.Value = dataSht.Range("K" & strAddress).Value
TaskMngUserForm.txtSolution.Value = dataSht.Range("L" & strAddress).Value
End If
' TaskMngUserForm.Show
End If
End With
Application.ScreenUpdating = True
Label25.Caption = "Check in Task.No: " & txtIndex.Text
End Sub
3. Problem
I can load data from Tab1 to Listbox1 but I cannot populate column header from Tab1 to Header in Listbox1.
I recently coded a UserForm to include headers and I can answer this for you.
There is only 1 way to populate the headers on a ListBox and that is when you use the ListBox1.RowSource property. In the RowSource property you must assign a Range, this is one example:
UserForm1.ListBox1.RowSource = "Sheet1!A2:H20"
This will populate the data from A2 to H20 on ListBox1 and if the ListBox1 ColumnHeaders property is set to True then anything on Sheet1!A1:H1 will become the headers. This is the only way.
The reason that many users will tell you to just add text labels on top of the ListBox to make it easier is because when you do your list using RowSource, you must always find out what is the last Row used on your Range before you assign the Range to avoid Empty lines on your ListBox. What this means is that if you have 20 rows of data and you assign a range that contains 50 rows, the listbox will populate 50 rows, the last 30 will be empty.
Don't need Code or Formulas. Include the headers as part of the define factor for the data page, mine is named RecordsGoodCharacters, the name of the worksheet.
Highlight the Sheet Cells & Columns required. Include the headers as part of the define factor. Mine is RecordsLanguages for this Worksheet, which is the worksheets name.
Type in the name top left, to DEFINE the highlighted areas and then press ENTER on the keyboard, if you don’t use the keyboard, it won’t work.
Once defined, open your VBA Userform
Click on the ListBox
In it properties on the left of the display, in the RowSource area, Type the defined name used.
The list box will show the list including the headers.

VBA loop through textboxes and insert each box on different row

I have much of this form
coded to what I want but I'm having difficulty with the most significant part of it. As shown in the image, the frame in the form with 30 textboxes is designed to have names entered in it. Each box has a different name. When I click "save data" button I want the names in the textboxes to be entered on the next available row on the worksheet, also in the image.
So, if the form has Bob, Joe, and Jane in the first three boxes, I'd want rows A:2-4 in the worksheet to be populated with each name respectively.
If you can't (or don't want to) rely on textbox names, there are two possible ways out:
exploit TabIndex
if your textboxes inside "Individuals" frame have same TabIndex order as the cells you want to write their content into, then you could go as follows:
Dim i As Long
Dim strng As String
With Me.Frame1 '<--| change "Frame1" to your actual "Individuals" frame name
For i = 0 To .Controls.Count - 1
strng = strng & GetTextBox(i).Value & " "
Next
Cells(Rows.Count, 1).End(xlUp).Offset(1).Resize(.Controls.Count) = Application.Transpose(Split(Trim(strng), " "))
End With
where you exploit the following Function:
Function GetTextBox(tabId As Long) As Control
Dim ctrl As Control
For Each ctrl In Me.Frame1.Controls
If ctrl.TabIndex = tabId Then Exit For
Next
Set GetTextBox = ctrl
End Function
exploit Top and Left control properties
if your textboxes are properly vertically aligned (i.e. all texboxes in the same row share the very same Top property), then you could go as follows:
Dim dict As Object
Dim ctrl As Control
Set dict = CreateObject("Scripting.Dictionary")
With dict
For Each ctrl In Me.Frame1.Controls
.Item(Format(ctrl.Top, "000") & "-" & Format(ctrl.Left, "000")) = ctrl
Next
End With
SortDictionary dict
Cells(Rows.Count, 1).End(xlUp).Offset(1).Resize(Me.Frame1.Controls.Count) = Application.Transpose(dict.items)
where you exploit the following Function:
Sub SortDictionary(dict As Object)
Dim i As Long
Dim key As Variant
With CreateObject("System.Collections.SortedList")
For Each key In dict
.Add key, dict(key)
Next
dict.RemoveAll
For i = 0 To .Keys.Count - 1
dict.Add .GetKey(i), .Item(.GetKey(i))
Next
End With
End Sub

VBA - Error While Programming a Class to Operate all Checkboxes on Userform

Here is a bit of background on what I'm trying to do: I'm creating a userform to track Inventory items and prices, using checkboxes in a multipage object. The clerk checks off everything put into an order and uses a submit button, which will take some actions.
In order for the project not to require a coding person every time Inventory items change, the checkboxes are being dynamically generated when the userform is activated, from cell values on an Inventory sheet. The clerks just adjust the Inventory sheet and the form automatically adjusts for them.
This is my code to dynamically create all the checkboxes (currently this form can accommodate up to 160 possible checkboxes), in case this is effecting my issue (side note, each tab on the multipage has a frame on it, and all checkboxes are within the frame, so I could change background colors, the frame in this example being titled "frmreg"):
Sub StoreFrmRegCheckboxGenerator()
'Works with the store userform
Dim curColumn As Long
Dim LastRow As Long
Dim i As Long
Dim chkBox As msforms.CheckBox
'This sub dynamically creates checkboxes on the Regular Items tab based
'on values in Column A of the Inventory sheet
curColumn = 1 'Set your column index here
LastRow = Worksheets("Inventory").Cells(Rows.Count, curColumn).End(xlUp).Row
For i = 2 To 9
If Worksheets("Inventory").Cells(i, curColumn).Value <> "" Then
Set chkBox = store.frmreg.Controls.Add("Forms.CheckBox.1", "CheckBox_" & i)
chkBox.Caption = Worksheets("Inventory").Cells(i, curColumn).Value & " - $" & Worksheets("Inventory").Cells(i, curColumn).Offset(0, 1).Value
chkBox.AutoSize = True
chkBox.WordWrap = True
chkBox.Left = 5
chkBox.Top = 1 + ((i - 1) * 25)
End If
Next i
'Cut some code out here identical to this previous section, but for the rest of the cells in column A up to Row 33, in blocks of 8
End Sub
The above code is in the Userform_Initialize sub, and it works perfectly.
However, since the number of checkboxes is not static, and can be as many as 160, I'm trying to write one sub to take the same set of actions any time any checkbox is clicked.
The closest solution I've found is from this question: Excel Macro Userform - single code handling multiple checkboxes, from sous2817.
Here is his code that I'm trying to use:
In a new class module:
Option Explicit
Public WithEvents aCheckBox As msforms.CheckBox
Private Sub aCheckBox_Click()
MsgBox aCheckBox.Name & " was clicked" & vbCrLf & vbCrLf & _
"Its Checked State is currently " & aCheckBox.Value, vbInformation + vbOKOnly, _
"Check Box # & State"
End Sub
The "store" userform, at the top, right under Option Explicit:
Dim myCheckBoxes() As clsUFCheckBox
At the bottom of the Userform_Initialize sub, AFTER I call the all the subs that dynamically create all the checkboxes:
Dim ctl As Object, pointer As Long
ReDim myCheckBoxes(1 To Me.Controls.Count)
For Each ctl In Me.Controls
If TypeName(ctl) = "CheckBox" Then
pointer = pointer + 1
Set myCheckBoxes(pointer) = New clsUFCheckBox
Set myCheckBoxes(pointer).aCheckBox = ctl
End If
Next ctl
ReDim Preserve myCheckBoxes(1 To pointer)
When I try to open the userform I get this error:
"Compile Error: User-defined type not defined"
Pointing to this line:
Dim myCheckBoxes() As clsUFCheckBox
Am I missing a library reference? I haven't been able to figure this out.

Running Macro script from another sheet resulting in error

I have some code which is intended to find a button inside another worksheet via some VBA script, if the buttons text contains "Hide all Rows", then execute code, else execute other code.
The if statement works and recognizes the button text, but it doesn't seem to want to recognize the macro name in the targetworkbook. I get the error 'The item with the specified name wasn't found'
I checked the targetworkbook macro name and it is correct,
code is below, am I doing something wrong here?
Sub MapValues(targetworkbook As Workbook, TargetSheet As Worksheet)
Dim shp as shape
Set shp = TargetSheet.Shapes("Button13" & TargetSheet.Name)
With targetworkbook
If shp.TextFrame.Characters.Text = "Hide All Rows" Then
targetworkbook.Application.Run "'" & targetworkbook.Name & "'!showAllRows"
Else
targetworkbook.Application.Run "'" & targetworkbook.Name & "!hideAllRows"
targetworkbook.Application.Run "'" & targetworkbook.Name & "!showAllRows"
End If
End With
End Sub()
It looks like the problem is with the text "Button13". Double check that it's not really something like "Button 13". The easiest way to do that is in the immediate window type
?sheet1.Shapes(1).name
and continue with 2,3,4 etc. until you get your name.

Add Menu Action Programatically to Visio

I'm creating a macro to add a menu button to a selected Visio shape object, so whenever the user right-clicks on the box, an option will appear and will call a macro. I created a couple of properties to the object, which will be used by the action to be called.
I can do it (SUCCESSFULLY) manually by using the ShapeSheet editor -> View Sections -> Actions -> and configuring the action with the Action value of
=CALLTHIS("ThisDocument.myFunction",,Prop.IPAddress)
sub myFunction (shpObj as Visio.shape, strIPAddress as String)
'working code with the functionsI want it to do. here I use the strIPAddress passed as an argument
What I'm trying to do is automate this by creating a macro that does the same:
Public Sub AddActionToShape()
Dim vsoShape1 As Visio.Shape
Dim intActionRow As Integer
'Performs this action to the selected item
Set vsoShape1 = Application.ActiveWindow.Selection(1)
'create row in the action section (http://office.microsoft.com/en-gb/visio-help/HV080902125.aspx)
intActionRow = vsoShape1.AddRow(visSectionAction, visRowLast, visTagDefault)
'add action to the row (http://msdn.microsoft.com/en-us/library/office/ff765539(v=office.15).aspx)
'HERE IS THE PROBLEM
**vsoShape1.CellsSRC(visSectionAction, intActionRow, visActionAction).FormulaU = """myFunction(vsoShape1, vsoShape1.Prop.IPAddress)"""**
vsoShape1.CellsSRC(visSectionAction, intActionRow, visActionMenu).FormulaU = """My Function"""
End Sub
My question:
What value should I put on the FormulaU to refer to a sub routine defined in my macro, while passing parameters. If I should not be using this FormulaU attribute, please point me to the correct one.
I ended up doing it like this and it works just fine.
Dim formula As String
formula = "=CALLTHIS([MODULE],,[ARG1],[ARG2])"
formula = Replace(formula, "[MODULE]", Chr(34) & "ThisDocument.myFunction" & Chr(34))
formula = Replace(formula, "[ARG1]", "Prop.IPAddress")
formula = Replace(formula, "[ARG2]", "Prop.Username")
'After the formula has been created, apply it to the row
shape.CellsSRC(visSectionAction, rowBeingEdited, visActionAction).formula = formula
You should set FormulaU exactly to the content to which you set it manually. That is, to
CALLTHIS("ThisDocument.myFunction",,Prop.IPAddress)
Try:
vsoShape1.CellsSRC(visSectionAction, intActionRow, visActionMenu).FormulaU = _
"CALLTHIS(""ThisDocument.myFunction"",,Prop.IPAddress)"