1

The code I have written automatically put the button where needed and assign a macro to it:

Sub CreateButton(a, b, c, d As Double, s As String)
ActiveSheet.Buttons.Add(a, b, c, d).Select
Selection.name = s
Selection.OnAction = "Button_ACTION"
Selection.Characters.Text = s
End Sub

The whole macro proceed as follow, firstly delete all buttons on the sheet, than it insert buttons where needed and assign macro to it:

Private Sub Button_ACTION()
Dim o As Object
Dim r, i, c As Integer
Set o = ActiveSheet.Buttons(Application.Caller)
With o.TopLeftCell
    r = .Row
    c = .Column
End With
...

Macro determines the position of button and manipulate data around. The problem is that no matter which button I am clicking the data changes only around the first one in the column. Please, advise what is the problem and what can be the solution?

Pᴇʜ
  • 56,719
  • 10
  • 49
  • 73
vlad.lisnyi
  • 325
  • 2
  • 12
  • Have you tried debugging it to see what `o` is set to? – SteveES Mar 09 '17 at 12:10
  • Your code works fine. Just make sure that if you create a button, the name you give as parameter `s` must be unique. Don't use the same name again. – Pᴇʜ Mar 09 '17 at 12:45

3 Answers3

1

The issue is that you don't have unique names for your buttons.

I changed your CreateButton() sub so it can take a name and a caption as parameter. Name is what Application.Caller uses and caption is what is written on the button. Name must be unique, caption can be the same on all buttons.

I also added a check so that only unique button names are accepted on creation.

Sub test()
    CreateButton 200, 100, 100, 25, "test1", "Test"
    CreateButton 50, 50, 100, 25, "test2", "Test"
    CreateButton 500, 500, 100, 25, "test3", "Test"
End Sub


Sub CreateButton(left As Double, top As Double, width As Double, height As Double, name As String, caption As String)
    On Error Resume Next
        ActiveSheet.Buttons(name).name = name
        If Err.Number = 0 Then
            MsgBox "Name has to be unique"
            Exit Sub
        End If
    On Error GoTo 0
    'this part above assures that the name for the button is unique.

    With ActiveSheet.Buttons.Add(left, top, width, height)
        .name = name
        .caption = caption
        .OnAction = "Button_ACTION"
    End With
End Sub

I highly recommend using readable variable names instead of a, b, c and o!


And be aware that

Dim r, i, c As Integer 'r and i are of type variant here only c is integer

CreateButton(a, b, c, d As Double, s As String)
'a, b and c are of type variant. Only d is double and s is string.

is not the same as

Dim r As Integer, i As Integer, c As Integer

CreateButton(a As Double, b As Double, c As Double, d As Double, s As String)
Pᴇʜ
  • 56,719
  • 10
  • 49
  • 73
1

First of all - try not to use selection!

Second - why you need thoose integers? Use Longs!

Your problem (I assume) is because of you give same name over and over, so with Set o = ActiveSheet.Buttons(Application.Caller) line you pick your first button with given name!

Since you don't mentioned where(and how) your function-like sub CreateButton are called from there's only one way to reproduct your problem for me - calling sub multiple time with same s parameter.

Note: Application.Caller is a string with name of a button in your case.

Try to give something unique!

Sub CreateButton(a, b, c, d As Double, s As String)
    Dim NewButton As Button
    Static Counter As Long

    Set NewButton = ActiveSheet.Buttons.Add(a, b, c, d)
    NewButton.Name = s & Counter
    NewButton.OnAction = "Button_ACTION"
    NewButton.Characters.Text = s
    Counter = Counter + 1
End Sub

But I suggest that you work on the uniqueness of your names. Static counter is ok if you are not a perfectionist, you are good with Long type limitations and all of your buttons are deleted upon quiting excel.

I advise you not to hope for it - when you need something unique - always use dictionary.

With dictionary it's easy to answer a question "Is that string are really unique?":

Public Function IsUnique(ByVal str As String) As Boolean
    Static UniqueDict As New Dictionary

    If UniqueDict.Exists(str) Then _
            Exit Function

    Call UniqueDict.Add(Key:=str, Item:=str)
    IsUnique = True
End Function

So you can edit string or add something to it only when it's really necessary. And if you don't bother deleting buttons upon quit - you can fill that dict with names on startup! Marvelous isnt it?


To use a dictionary object you need reference to Microsoft Scripting Runtime!

Community
  • 1
  • 1
CommonSense
  • 4,232
  • 2
  • 14
  • 38
  • 1
    Firstly, I agree that using `select` is bad practice. Secondly, you are only correct if CreateButton is called with the same `s` every time. – SteveES Mar 09 '17 at 12:27
  • @SteveES, I'm agree with you, but just try to reproduct OP's problem - there's one way to do this in my opinion(in a loop). It's unclear where `CreateButton` are called from so no straight answer. – CommonSense Mar 09 '17 at 12:34
  • Does the Button type is your own? cant find it in my EXCEL – vlad.lisnyi Mar 09 '17 at 12:49
  • @V. Lesnoy, No, there are `button` and `buttons` for collection of buttons(and some attributes). You can check it with `? TypeName(ThisWorkbook.Sheets(1).Buttons)` in immediate window. Anyway, feel free to declare button as `object` if something wrong, but this types are here in my 07's. – CommonSense Mar 09 '17 at 12:55
  • Thanks, It works) But how can I refer to the clicked button? How to get the name? Again ApplicationCaller or there is other way to do so? – vlad.lisnyi Mar 09 '17 at 12:59
  • I also thought to set o= nothing each time. But again it returns only the first button. ...hm. The strange thing: after some time I would left macro to stay for couple of minutes it begin to works. – vlad.lisnyi Mar 09 '17 at 13:02
  • @V. Lesnoy, As I see (and understand) your algorithm, there are not so many options here for you. As an alternative, you can get active button by your cursor position, but you need a set of API functions in exchange. – CommonSense Mar 09 '17 at 13:21
  • @V. Lesnoy, but you can do some work on the uniqueness, so I slighty updated my unswer. – CommonSense Mar 09 '17 at 14:11
0

why do you use Application.Caller on your code...? will it return the caller Shape object..? Try to debug it...., I think Application.Caller will fail to return the caller Shape (the Shape object where your macro assigned). You can give a unique name for each Shapes object you've created and access it by its name, here an example hope it useful.

Option Explicit

    Public Sub AddButtonWithLoop()
        Dim btn As Shape
        'Use for each loop to Create 3 Shapes, assign name and macro to each Shape
        Dim i As Integer
        For i = 1 To 3
            Set btn = Worksheets(1).Shapes.AddShape(msoShapeBevel, 10, 70 * i, 70, 50)
            'set some properties
            With btn
            .Name = "MyButton" & i
            .TextFrame2.TextRange.Characters.Text = "Button " & i
            .OnAction = "'SayHello""" & btn.Name & """'"
        End With
    Next i
End Sub

Public Sub SayHello(shapeName As String)
    'Create Shape object using ShapeName
    Dim s As Shape: Set s = ActiveSheet.Shapes(shapeName)

    With s.TopLeftCell
        MsgBox .Row
        MsgBox .Column
    End With
End Sub
  • The number of buttons is not fixed. It could deviate from 2 to twenty. Is it necessary to give a unique names to buttons?!(I would need to store names somewhere). How can I get the name of the clicked button, to operate with it? I am afraid there is would be such problem as before that only first button in the row will be taken in action – vlad.lisnyi Mar 09 '17 at 12:43
  • Application caller, I supposed It would return the information of the clicked button. At least I have seen it was working – vlad.lisnyi Mar 09 '17 at 12:50
  • You can use looping to add any numbers of Shapes with flexibility. Replace the AddButton() method with code below... – Cheries Mewengkang Mar 09 '17 at 13:26