2

Well, the title is because I had a hard time walking through some unbearable docs and finding what I was looking for so, if these keywords can help for other google searches...

Then, when quitting Excel, all previously marked lines of code are lost and it is very frustrating when you have to go to sleep or Excel crashes :)

So, in a moment of pure madness I thought it could be possible, and even not too hard, to just save those line bookmarks and restore them at start up...

You will tell me there are other choices: don't sleep.. or use some powerful add-ins like MZ-Tools or Rubberduck, but I would like to have a native solution and understand what the problem is.

To cut to the edge, here is the core of my problem:

'sub to move cursor to a selected line and add a line bookmark:

Public Sub AddBmkOnly(ByVal CompName As String, ByVal numLine As Long)

    Application.VBE.VBProjects("VBAProject") _
               .VBComponents(CompName).CodeModule.CodePane _
               .SetSelection numLine, 1, numLine, 1

    Application.VBE.CommandBars("Edit").Controls(18).Controls(1).Execute 'the only way I could find it to work

End Sub

What happens:

1) with only one call it works!

Public Sub test_addBmk()
    Call AddBmkOnly("module 1", 10) 
End Sub

2) once there are more, or in a loop for example:

Public Sub test_addBmk()
    Call AddBmkOnly("module 1", 10) 'cursor is just moved to selected line
    Call AddBmkOnly("module 2", 5) 'line bookmark is added only in the last opened/activated/selected/visible/shown/focused on..? codepane
'...
End Sub

Place your cursor inside the 2nd test_addBmk, run and you will see a beautiful cyan blue mark appearing in the margin of your "module 2" at line 5 but that's all, no where else.

I well tried to add this kind of lines in AddBmkOnly to keep focus/active state, but it has no effect:

With Application.VBE.VBProjects("VBAProject").VBComponents(CompName)
    .Activate
    .CodeModule.CodePane.Window.SetFocus
    .CodeModule.VBE.ActiveCodePane.Show
    '...?
end with

I tried adding some DoEvents, Debug.Print, loop to 1M or likes to see if it was due to some latency/refreshing effect, but no effect either.

It could have something to do with the active state of the module or codepane window, but I can't find a working combination (also, closing the last pane - .ActiveCodePane.Window.Close - will avoid a bookmark to be added too).

It seems also that the focus is lost before an anchor is added, whatever I try, or the 'add bookmark' menu action doesn't see where to apply.... or it is something else...

Calling test_addBmk() multiple times doesn't work neither, the only way I found is creating 'one action' buttons in an Excel sheet, as many as the number of bookmarks I needed... that's not funny.

What am I doing wrong? Is it even possible the way I'm trying? How can I add more than a single bookmark?

Mathieu Guindon
  • 69,817
  • 8
  • 107
  • 235
foxtrott
  • 55
  • 7
  • of course, I should consider to use it but that wouldn't explain what is not working.. ;) – foxtrott Feb 15 '19 at 19:44
  • can't you just add `'#1` , `#2` etc in the code and then in a new session first thing look for `#` and put the bookmarks in manually. [If the sequence `#` is not unique enough make it `##` etc, just keeping adding characters] I don't see your pain point as being that painful. – S Meaden Feb 16 '19 at 14:50

2 Answers2

2

You need to active the code pane before you invoke the menu item:

Public Sub AddBmkOnly(ByVal CompName As String, ByVal numLine As Long)
    Dim editor As VBE
    Dim project As VBProject
    Dim component As VBComponent

    Set editor = Application.VBE
    Set project = Application.VBE.VBProjects("VBAProject")
    Set component = project.VBComponents(CompName)
    component.CodeModule.CodePane.SetSelection numLine, 1, numLine, 1
    component.Activate
    Application.VBE.CommandBars("Edit").Controls("&Toggle Bookmark").Execute 'the only way I could find it to work... almost[*]
End Sub

Note that this won't work if you try to step through it in a debugger, because each time you step it sets the active pane back to the code that you're executing.

A couple other notes:

  • You should be testing the number of code lines before trying to set the selection - if numLine is higher than the lines of code in the module, that's an application error.
  • Call should be considered deprecated - there's absolutely no reason to use it.
  • You should avoid hard coding an index in the Controls collection - other add-ins can modify these, so who knows what you'll get.
  • Thanks for mentioning Rubberduck! (I'm a contributor)
Comintern
  • 21,855
  • 5
  • 33
  • 80
  • OK! Thank you, I've just understood (almost): I had already tested to .activate just before .execute, also trying to open/close each pane one after the other, but that wasn't working. So the only difference I see ith your code is that you add object variables and ``set`` them. It seems it somehow 'forces' the component to be actually active. But that's not all, I still had to add a ``DoEvents`` between the calls but now it works! About the controls, just to add that neither "&Toggle Bookmark" nor the #%*$ localized version ("&Basculer signet") as it should be, are working here. Ty again! – foxtrott Feb 15 '19 at 20:50
  • @foxtrott - Yeah, that's where I was going with the hard coded index. The best option may be to just loop through the edit menu to find it. – Comintern Feb 15 '19 at 20:59
0

This version of the above works for me.

    Public Sub test_addBmk()
    Call AddBmkOnly("module1", 10) 
    Call AddBmkOnly("module2", 5) 
    End Sub

    Public Sub AddBmkOnly(ByVal CompName As String, ByVal numLine As Long)

    Dim editor As VBE
    Dim project As VBProject
    Dim component As VBComponent

    Set editor = Application.VBE
    Set project = Application.VBE.VBProjects("VBAProject")
    Set component = project.VBComponents(CompName)
    component.CodeModule.CodePane.SetSelection numLine, 1, numLine, 1
    component.Activate: DoEvents
    editor.CommandBars("Edit").Controls("&Toggle Bookmark").Execute 
    DoEvents

    End Sub
DFoss
  • 1