Property Manager - Referencing Controls by ID?

I am writing my first Property Manager Page. So far I’ve been successful getting it to do everything I need it to, but I haven’t been able to figure out one thing. I can’t figure out how to access page items by ID number.

I have 3 modules, my main module, a PMP class module, and a PMP Handler class module. When I want to reference a control in my main module, I have to do it explicitly such as:

swPage.pm_MySelectionBox1
swPage.pm_MySelectionBox2

I’d prefer to reference them by ID such as

swPage.Item(10)
swPage.Item(20)

by it doesn’t look like that’s possible, but that can’t be. As a result, I have to write duplicate functions that do the same thing, but for specific controls which is silly.

What am I missing? Is there example code that can show me how it’s done?

Thank You

If I were in your shoes (and writing in VBA) I would probably just build a Collection of the controls you’re interested in during the initialization. Then you can reference them by key or by item/index.

The fact that you want to access controls from a different module indicates a general flaw in your design. You should be thinking in terms of Model-View-Controller (MVC). A time-tested concept in software development.

The property manager page is your View. The PropertyManagerPageHandler is your controller. What you are missing is your Model.

A search for ```MVC VBA``` should lead you to multiple examples of how to do this.

Hi @loeb,

for creating PMP’s i can really recommend XCAD from @artem.
It makes it super easy to make property pages like this in a user friendly manner.

XCAD link

It is not using VBA but setting up a new project is supereasy because Artem provides templates that gives you a head start with a PMP alread defined.

Jim,

I found one example in the API help that seems to exactly follow the MVC concept. It has a 1) Main module, 2) PM Page class module, and a 3) PM Page Handler class module. I started by using that one as the basis for my first PMP project. The other examples in the API help seem to have combine the PMP and PMPHandler class modules into one.

One thing that confuses me is that ID constants for the PMP items are defined in the PMP class module. That means that the PMPhandler class module doesn’t have access to those ID constants. Since the handler routines typically have ID as one of their arguments, this doesn’t make sense to me. I moved the ID Constant declarations to my Main module so that they can be used throughout the project.

Does what I did make sense or am I missing something? Like I said, I am new to PMPs and relatively new to class modules as well.

Josh, based on your comments, I’m considering the following and look forward to your feedback. Keep in mind, I’m new class modules and PMPs.

I was thinking that in the Main module, I could define a collection (or dictionary) that stores states of things, such as checkboxes, with their PMP IDs becoming their keys. I could also include “pointers” to things such as selection boxes in the same collection/dictionary since they are variant. The handler would then update the state of things as they change.

Alternately, in the handler class module, I could define a data structure that would be used identically to the collection/dictionary idea above.

Thank You

This may totally go against good programming… I’m far from a professional. However, it sounds like you’re saying that you have multiple controls of a similar type that you’re trying to access by an index. So, on the line where you create the control, you can add a reference to the control itself into a Module level Collection object. This little block creates six NumberBox controls, and puts them all into a Collection, with Key values from “16” to “21”.

image

You can then later reference the controls either by their position in the collection (index) or Key, triggered here by pressing a button on the form

Result

Yes, exactly. Additionally, since collections/dictionaries are variant, they members don’t have to have the same value types.

You can create a separate class to contain all of your IDs. Here’s a simple MVC example. It consists of one modules and four classes:

The module, named PMP_MVC_Example, which is the macro entry point

Option Explicit
Dim swApp As SldWorks.SldWorks
Dim theView As AView
Dim theModel As AModel
Dim theController As AController
Dim options As swPropertyManagerPageOptions_e

Sub main()
    Set swApp = Application.SldWorks
    Set theModel = New AModel
    Set theView = New AView
    Set theController = New AController
    Set theController.View = theView
    Set theController.Model = theModel
    Dim errors As Long
    options = _
          swPropertyManager_OkayButton _
        + swPropertyManager_CancelButton _
        + swPropertyManagerOptions_LockedPage _
        + swPropertyManagerOptions_PushpinButton
    Set theView.Page = swApp.CreatePropertyManagerPage("MVC Test", options, theController, errors)
    theView.Show
End Sub

The first class (named ControlIDSet) holds the IDs and makes them available as read-only properties:

'Single class to hold IDs for all controls
Option Explicit
Public Property Get SOME_OPTION_CHECKBOX() As Long
    SOME_OPTION_CHECKBOX = 0
End Property
Public Property Get SOME_OTHER_OPTION_CHECKBOX() As Long
    SOME_OTHER_OPTION_CHECKBOX = 1
End Property

The second class (named AView) which is used to build the PM Page:

Option Explicit
Private ControlIDs As New ControlIDSet
Private myPage As PropertyManagerPage2
Private someOptionCheckbox As PropertyManagerPageCheckbox
Private someOtherOptionCheckbox As PropertyManagerPageCheckbox

Public Property Set Page(ByVal newPage As PropertyManagerPage2)
    Set myPage = newPage
    InitPage
End Property

'Delegates to Show2 of PropertyManagerPage2
Public Sub Show()
    myPage.Show2 0
End Sub

Private Sub InitPage()
    'First checkbox
    Set someOptionCheckbox = myPage.AddControl2( _
        ControlIDs.SOME_OPTION_CHECKBOX, _
        swPropertyManagerPageControlType_e.swControlType_Checkbox, _
        "Some option", swPropertyManagerPageControlLeftAlign_e.swControlAlign_Indent, _
        swAddControlOptions_e.swControlOptions_Enabled + swAddControlOptions_e.swControlOptions_Visible, _
        "Some option tooltip")
    'Second checkbox
    Set someOtherOptionCheckbox = myPage.AddControl2( _
    ControlIDs.SOME_OTHER_OPTION_CHECKBOX, _
    swPropertyManagerPageControlType_e.swControlType_Checkbox, _
    "Some other option", swPropertyManagerPageControlLeftAlign_e.swControlAlign_Indent, _
    swAddControlOptions_e.swControlOptions_Enabled + swAddControlOptions_e.swControlOptions_Visible, _
    "Some other option tooltip")
End Sub

The third class (named AModel) which holds the state for the view and methods to do things with the state:

Option Explicit
'Model state property variables
Private bSomeOption As Boolean
Private bSomeOtherOption As Boolean

'Model state property getter/setters
Public Property Get SomeOption() As Boolean
    SomeOption = bSomeOption
End Property

Public Property Let SomeOption(newValue As Boolean)
    bSomeOption = newValue
End Property

Public Property Let SomeOtherOption(newValue As Boolean)
    bSomeOtherOption = newValue
End Property

Public Property Get SomeOtherOption() As Boolean
    SomeOtherOption = bSomeOtherOption
End Property

'Example of model logic that does something with model state
Public Sub PrintData()
    Debug.Print "Some option value was: " & SomeOption
    Debug.Print "Some other option value was: " & SomeOtherOption
End Sub

And the final class (named AController) that handles the events from the view and changes the model state accordingly:

Option Explicit
Private ControlIDs As New ControlIDSet
Private myModel As AModel
Implements PropertyManagerPage2Handler9

Public Property Set Model(ByVal newModel As AModel)
    Set myModel = newModel
End Property

'================================= PropertyManagerPage2Handler9 Implementation ========================'
Private Sub PropertyManagerPage2Handler9_OnCheckboxCheck(ByVal id As Long, ByVal Checked As Boolean)
    Select Case id
        Case ControlIDs.SOME_OPTION_CHECKBOX
            myModel.SomeOption = Checked
        Case ControlIDs.SOME_OTHER_OPTION_CHECKBOX
            myModel.SomeOtherOption = Checked
    End Select
End Sub

Private Sub PropertyManagerPage2Handler9_OnClose(ByVal Reason As Long)
    myModel.PrintData
End Sub

'============================== Unused PropertyManagerPage2Handler9 Methods =========================='

Private Sub PropertyManagerPage2Handler9_AfterActivation()
End Sub
Private Sub PropertyManagerPage2Handler9_AfterClose()
End Sub
Private Function PropertyManagerPage2Handler9_OnHelp() As Boolean
End Function
Private Function PropertyManagerPage2Handler9_OnPreviousPage() As Boolean
End Function
Private Function PropertyManagerPage2Handler9_OnNextPage() As Boolean
End Function
Private Function PropertyManagerPage2Handler9_OnPreview() As Boolean
End Function
Private Sub PropertyManagerPage2Handler9_OnWhatsNew()
End Sub
Private Sub PropertyManagerPage2Handler9_OnUndo()
End Sub
Private Sub PropertyManagerPage2Handler9_OnRedo()
End Sub
Private Function PropertyManagerPage2Handler9_OnTabClicked(ByVal id As Long) As Boolean
End Function
Private Sub PropertyManagerPage2Handler9_OnGroupExpand(ByVal id As Long, ByVal Expanded As Boolean)
End Sub
Private Sub PropertyManagerPage2Handler9_OnGroupCheck(ByVal id As Long, ByVal Checked As Boolean)
End Sub
Private Sub PropertyManagerPage2Handler9_OnOptionCheck(ByVal id As Long)
End Sub
Private Sub PropertyManagerPage2Handler9_OnButtonPress(ByVal id As Long)
End Sub
Private Sub PropertyManagerPage2Handler9_OnTextboxChanged(ByVal id As Long, ByVal Text As String)
End Sub
Private Sub PropertyManagerPage2Handler9_OnNumberboxChanged(ByVal id As Long, ByVal Value As Double)
End Sub
Private Sub PropertyManagerPage2Handler9_OnComboboxEditChanged(ByVal id As Long, ByVal Text As String)
End Sub
Private Sub PropertyManagerPage2Handler9_OnComboboxSelectionChanged(ByVal id As Long, ByVal Item As Long)
End Sub
Private Sub PropertyManagerPage2Handler9_OnListboxSelectionChanged(ByVal id As Long, ByVal Item As Long)
End Sub
Private Sub PropertyManagerPage2Handler9_OnSelectionboxFocusChanged(ByVal id As Long)
End Sub
Private Sub PropertyManagerPage2Handler9_OnSelectionboxListChanged(ByVal id As Long, ByVal Count As Long)
End Sub
Private Sub PropertyManagerPage2Handler9_OnSelectionboxCalloutCreated(ByVal id As Long)
End Sub
Private Sub PropertyManagerPage2Handler9_OnSelectionboxCalloutDestroyed(ByVal id As Long)
End Sub
Private Function PropertyManagerPage2Handler9_OnSubmitSelection(ByVal id As Long, ByVal Selection As Object, ByVal SelType As Long, ItemText As String) As Boolean
End Function
Private Function PropertyManagerPage2Handler9_OnActiveXControlCreated(ByVal id As Long, ByVal Status As Boolean) As Long
End Function
Private Sub PropertyManagerPage2Handler9_OnSliderPositionChanged(ByVal id As Long, ByVal Value As Double)
End Sub
Private Sub PropertyManagerPage2Handler9_OnSliderTrackingCompleted(ByVal id As Long, ByVal Value As Double)
End Sub
Private Function PropertyManagerPage2Handler9_OnKeystroke(ByVal Wparam As Long, ByVal Message As Long, ByVal Lparam As Long, ByVal id As Long) As Boolean
End Function
Private Sub PropertyManagerPage2Handler9_OnPopupMenuItem(ByVal id As Long)
End Sub
Private Sub PropertyManagerPage2Handler9_OnPopupMenuItemUpdate(ByVal id As Long, retVal As Long)
End Sub
Private Sub PropertyManagerPage2Handler9_OnGainedFocus(ByVal id As Long)
End Sub
Private Sub PropertyManagerPage2Handler9_OnLostFocus(ByVal id As Long)
End Sub
Public Function PropertyManagerPage2Handler9_OnWindowFromHandleControlCreated(ByVal id As Long, ByVal Status As Boolean) As Long
End Function
Public Sub PropertyManagerPage2Handler9_OnListBoxRMBUp(ByVal id As Long, ByVal posX As Long, ByVal posY As Long)
End Sub
Public Sub PropertyManagerPage2Handler9_OnNumberboxTrackingCompleted(ByVal id As Long, ByVal Value As Double)
End Sub

As you can see, the model knows nothing about the view or the controller. The view knows nothing about the model or the controller. The controller knows about the model only.

Jim,

Thank you for your detailed example. I haven’t had time in the last week to actively pursue this project. I hope to be able to dig into your example tonight. I understand the structure and your intent. Being new to OOP, the implementation is something that will take me a bit to sift through.

Thank you again for your help. I suspect I’ll have some follow-up questions soon.

Jim, I made a project out of the modules you shared, but get the following error:

“Method or data member not found” for “theController.View”.

I see a property named “Model” is defined in “aController”, but “View” seems to be undefined.

I attached my SWP file below.

Thank You

JimScully_PMP_MVC_Example.swp (63 KB)

Gah. I made some last minute edits. You can delete the

Set theController.View = theView

line. The controller doesn’t reference the view at all, so it doesn’t need to know about it.

Jim,

I finally got around to converting my code to use the MVC structure you demonstrated. It works like a charm. Now to implement the functionality I couldn’t achieve earlier.

Thanks for your help!