UserForm - Detect if document changed or if MultiPage regains focus?

I have a userform that displays information about the active doc (dwg, assy, or part). I would like it to update that information automatically if the user switches to a different doc.

The only solution I have found is to require the user to click on a “refresh” button to update the info, but until the user does that, the information displayed is incorrect and that can create problems. I was hoping that there was an event that would be triggered when the userform regains focus after the user changes to a different doc, but it seems none exists.

I appreciate any ideas you all have. Thank You.

Use events. Here’s a (hastily thrown together) simple example. You will have to add a class module along with your user form and the SW macro. The compelte macro is attached below:

'SW Macro Code
Dim swApp As SldWorks.SldWorks
Dim handler As EventHandler
Dim form As MyUserForm

Sub main()
    Set swApp = Application.SldWorks
    Dim mDoc As ModelDoc2
    Set mDoc = swApp.ActiveDoc
    Set handler = New EventHandler
    Set form = New MyUserForm
    handler.SetForm form
    If Not mDoc Is Nothing Then
        form.docTitle = mDoc.GetTitle
    End If
    form.Show vbModeless
End Sub
'Event Handler class module code
Dim WithEvents swApp As SldWorks.SldWorks
Dim WithEvents pDoc As PartDoc
Dim WithEvents aDoc As AssemblyDoc
Dim WithEvents dDoc As DrawingDoc
Dim myForm As MyUserForm

Private Sub Class_Initialize()
    Set swApp = Application.SldWorks
End Sub


Public Sub SetForm(form As MyUserForm)
    Set myForm = form
End Sub

Private Function pDoc_DestroyNotify2(ByVal DestroyType As Long) As Long
    updateForm
End Function

Private Function aDoc_DestroyNotify2(ByVal DestroyType As Long) As Long
    updateForm
End Function

Private Function dDoc_DestroyNotify2(ByVal DestroyType As Long) As Long
    updateForm
End Function


Private Function swApp_ActiveDocChangeNotify() As Long
    Dim mDoc As ModelDoc2
    Set mDoc = swApp.ActiveDoc
    myForm.docTitle = mDoc.GetTitle
    Dim docType As swDocumentTypes_e
    docType = mDoc.GetType
    Select Case docType
        Case swDocPART
            Set pDoc = mDoc
            Set aDoc = Nothing
            Set dDoc = Nothing
        Case swDocASSEMBLY
            Set pDoc = Nothing
            Set aDoc = mDoc
            Set dDod = Nothing
        Case swDocDRAWING
            Set pDoc = Nothing
            Set aDoc = Nothing
            Set dDoc = mDoc
        Case Else
            Set pDoc = Nothing
            Set aDoc = Nothing
            Set dDoc = Nothing
    End Select
End Function

Sub updateForm()
    If swApp.GetDocumentCount = 1 Then 'Last document is closing
        myForm.docTitle = "No active document"
    End If
End Sub

FormUpdate.swp (66 KB)

3 Likes

Jim,

Thank you for the quick and complete response. Can you give me a little help understanding how to implement this class module? I’ve been writing macros for 6 years, but this is new to me. My interpretation is that if a document is closed (destroyed), it will run the “updateForm” sub and that’s where I would run a public function/sub in another module? Will it work if the user neither opens or closes a document but just switches (alt-tab for example) between docs?

Thank you again!

To add a class module:

The class that I posted (the second code snippet in my previous post) is listening to 4 events:

SldWorks::ActiveDocChangeNotify
PartDoc::DestroyNotify2
AssemblyDoc::DestroyNotify2
DrawingDoc::DestroyNotify2

Any time the active document changes (switching windows, opening/closing documents) , SOLIDWORKS will call the swApp_ActiveDocChangeNotify function.

Every time a document is closed, the corresponding DestroyNotify2 function will be called. The only reason the example code is listening to the three DestroyNotify2 events is so that if no more documents are open, the user form can be updated to say ‘no active document’. Here’s an animated GIF showing it in action:
SLDWORKS_RwYC7OAJHU

3 Likes

Jim,

I was able to implement your code and it works great. I did have to manually create the project because it would not compile, asking for 2025 add-ins (I’m at 2024). But after transposing the code, it works great.

Now to integrate it into my project.

Thank you again. This really helps. In the past, I was hoping to implement mouse handlers, but couldn’t figure it out. I feel that with this example, I’m one step closer to figuring that out too.

Thank you again!

-Loeb

FYI: You can use the Tools…References menu item to change from the 2025 libraries to the ones you have.

1 Like

You can declare variables WithEvents in a form as well as a class module. Any advantage to using a separate class module to monitor the active document vs just using events in the form itself?

1 Like

Just Separation of Concerns really. Let the form do form things and the event handler class do event handler things.

2 Likes

Isn’t it a heck of a lot more work though? Based on the assumption that all the model interaction is already being done in code residing in the form, swApp is probably already delcared in the form’s code anyway. If you just change it to WithEvents , all you need to do is trigger the form update on ActiveDocChangeNotify.

I also don’t understand why you’re triggering the form update on destroy of the previously active document instead of in the ActiveDocChangeNotify. Unless I’m reading incorrectly, this code will only update the form when the previously active document is closed, right? I belive OP wants to update the form when the active document changes, whether the current doc was closed or not.

So… Really all they need to do is declare swApp WithEvents, then add this one function somewhere in the form code. Or am I missing something?

Private Function swApp_ActiveDocChangeNotify() As Long
    If not swApp.ActiveDoc is nothing then 
        [whatever function/sub currently updates the form]
    end if
End Function
1 Like

No. It updates the form in swApp_ActiveDocChangeNotify as well:

Private Function swApp_ActiveDocChangeNotify() As Long
    Dim mDoc As ModelDoc2
    Set mDoc = swApp.ActiveDoc
    myForm.docTitle = mDoc.GetTitle

ActiveDocChangeNotify isn’t fired when the last open document is closed. That little surprise is why I added the listeners for DestroyNotify2 for parts/assemblies/drawings so the form could reflect the fact that nothing is open.

1 Like

Ah, sorry, I didn’t look at the details of your post close enough (sorta skimming bc I’ve been out of the country for 2 weeks). I see now that changing the title of the form is the only updating that the example does. I was looking for a call to updateForm in the ActiveDocChange, but didn’t notice that updateForm actually does nothing unless nothing is open.

While it’s indeed thorough, IMHO the benefit of showing in the form that no document is open is more trouble than I’d usually go to. :smiley:

1 Like

Yeah. It bothered me that after closing the last document the form still listed the last title. This was all just a quickly thrown together example while sitting at home half-delirious with the flu. Not my best work.

1 Like

I am also interested in implementing event handlers in macros where there is no form. Is that possible?

The SW API help doesn’t document very well the methods/functions in the code shared here. I imagine that these are a standard part of VB (MS Forms 2.0?) Can you recommend a reference where I can learn more about this topic? I’m finding references that are specific to Excel and Access, but there seems to be significant differences between the API for those apps and the SW API implementation.

The event handlers don’t require forms at all. The complete list of events can be found here:

Scroll toward the bottom to the ‘Delegates’ section. Also, once you adorn a variable with the WithEvents keyword, when you select the variable in the left drop down at the top of the VBA code editor, the right drop down will list all the available event methods.

1 Like

Thank you Jim and Josh. I’m gaining a much better understanding of event handlers. This is really going to expand the kinds of productivity enhancers I can create for my team.

You may also want to consider creating a custom Task Pane View instead of a separate floating form. It can make your tools look more integrated with SW itself.

2 Likes

I had a Property Manager on my radar, but not Task Panes. I’ll have to look for example code for that. Thanks!

Jim,
I’m successfully using the handlers you wrote, but stuck on something. When I have closed the only remaining doc that was open and that handler fires, when I ask what is the current FilePathName, I get the FilePathName of the file that was last open. Also, GetDocumentCount returns a non-zero value.

I tried inserting
Set swModel = swApp.ActiveDoc
to get it to forget the last file and hopefully return ‘nothing’, but that doesn’t work.

Not sure what to do.

Thank You

Hi all,

I enjoyed reading through this topic :smiley:

I don’t know what info you want to show in your scenario, but if you want it to be visible all the time, you could add to the status bar also.
The solidworks api let’s us users add 5 statusbar zones that will appear on the right of the statusbar next to the default information.
SolidWorks API documentation

2 Likes

Eddy,

Thank you for the tip. I can totally use that functionality to let the user know when a macro that takes a long time to complete is busy. In the spirit of giving back, here’s some prototype code I managed to get working.

'https://help.solidworks.com/2024/English/api/sldworksapi/SOLIDWORKS.Interop.sldworks~SOLIDWORKS.Interop.sldworks.IFrame~GetStatusBarPane.html
'https://help.solidworks.com/2024/English/api/sldworksapi/SolidWorks.Interop.sldworks~SolidWorks.Interop.sldworks.IFrame_members.html

Public MyStatusIFrame    As SldWorks.Frame
Public MyStatusBarPane   As StatusBarPane

Set MyStatusIFrame = swApp.Frame
MyStatusIFrame.SetStatusBarText ("Something")

Set MyStatusBarPane = MyStatusIFrame.GetStatusBarPane()
MyStatusBarPane.Visible = True
MyStatusBarPane.text = "Something"

MsgBox "Pause"

MyStatusBarPane.Visible = False
Set MyStatusBarPane = Nothing
Set MyStatusIFrame = Nothing
1 Like