You’ve clicked inside that left-hand panel in SolidWorks thousands of times. Every time you create an extrude, add a fillet, define a mate, or configure a sheet metal feature, the Property Manager Page slides in from the left with its groups, checkboxes, selection boxes, and blue highlight fields. It looks like a standard Windows control panel. But it isn’t.
If you’ve ever tried to build your own PMP through the SolidWorks API, you’ve noticed something strange: you don’t create Windows controls. You call AddControl with a type enum and get back a COM interface. You can’t set a font. You can’t subclass the window. You can’t attach a debugger to the control’s HWND the way you would with a normal Windows button. The PMP controls don’t behave like anything else on Windows.
We decompiled the SolidWorks interop DLLs, inspected exported symbols from native C++ libraries, and examined the internal class hierarchy to understand what Property Manager Pages are actually made of — the same approach we used when investigating how SolidWorks drawing files store geometry and what happens behind the scenes when you export a DXF. Here’s what we found.
The API Surface: What You See
When you build a Property Manager Page through the SolidWorks API, you interact with a layered set of COM interfaces. The entry point is ISldWorks::CreatePropertyManagerPage, which takes a title string, option flags, and a handler object:
PropertyManagerPage2 page = swApp.ICreatePropertyManagerPage(
"My Feature",
(int)swPropertyManagerPageOptions_e.swPropertyManagerOptions_OkayButton,
handler,
out int errors
);
The handler must implement IPropertyManagerPage2Handler9 — the latest version, which defines 37 callback methods covering everything from checkbox clicks to keystroke interception to 3D selection validation. The handler versions evolved over SolidWorks releases, each adding new callbacks on top of the previous:
| Handler Version | Added Callbacks |
|---|---|
| Handler (v1) | OnClose, AfterClose, OnHelp, basic control events |
| Handler2 | OnComboboxEditChanged |
| Handler3 | AfterActivation, OnSubmitSelection, OnActiveXControlCreated |
| Handler4 | OnPreviousPage, OnNextPage, OnPreview, OnUndo, OnRedo, OnTabClicked |
| Handler5 | OnSliderPositionChanged, OnSliderTrackingCompleted |
| Handler6 | OnKeystroke(Wparam, Message, Lparam, Id), popup menu events |
| Handler7 | OnSelectionboxCalloutCreated, OnSelectionboxCalloutDestroyed |
| Handler8 | OnGainedFocus, OnLostFocus |
| Handler9 (current) | OnWindowFromHandleControlCreated, OnListboxRMBUp, OnNumberBoxTrackingCompleted |
Controls are added to the page through IPropertyManagerPage2::AddControl, which takes an integer ID, a control type from swPropertyManagerPageControlType_e, and option flags. The API provides 15 built-in control types:
| Control Type | Interface | What It Does |
|---|---|---|
| Textbox | IPropertyManagerPageTextbox | Single or multi-line text input |
| Checkbox | IPropertyManagerPageCheckbox | Boolean toggle (supports indeterminate state) |
| Numberbox | IPropertyManagerPageNumberbox | Numeric input with unit awareness and slider support |
| Combobox | IPropertyManagerPageCombobox | Dropdown selection with optional edit field |
| Listbox | IPropertyManagerPageListbox | Multi-item selection list |
| Selectionbox | IPropertyManagerPageSelectionbox | The blue highlight fields for picking geometry in the 3D viewport |
| Option | IPropertyManagerPageOption | Radio button |
| Button | IPropertyManagerPageButton | Push button |
| Label | IPropertyManagerPageLabel | Rich text with per-character formatting |
| Bitmap | IPropertyManagerPageBitmap | Static image |
| BitmapButton | IPropertyManagerPageBitmapButton | Image toggle button |
| Slider | IPropertyManagerPageSlider | Range slider control |
| Tab | IPropertyManagerPageTab | Multi-page tab container |
| ActiveX | IPropertyManagerPageActiveX | Embedded COM/ActiveX control |
| WindowFromHandle | IPropertyManagerPageWindowFromHandle | Embedded HWND — any window you create |
The last two are the escape hatches. IPropertyManagerPageActiveX lets you embed any ActiveX control by CLSID. IPropertyManagerPageWindowFromHandle goes further — you create your own Win32 window (WPF, WinForms, anything) and pass the HWND to SolidWorks, which hosts it inside the PMP. Both have 32-bit and 64-bit handle variants.
All controls inherit from IPropertyManagerPageControl, which provides common properties: Visible, Enabled, Tip, Left, Width, Top, TextColor, BackgroundColor. Controls are organized into collapsible IPropertyManagerPageGroup sections and optional IPropertyManagerPageTab containers.
This is what the API shows you. What it doesn’t show is how these controls are actually rendered.
What’s Actually Inside: The DVE System
The PMP system is internally called DVE — Dialog View Editor. We found the evidence in slduiu.dll, a native C++ library with 19,749 exported symbols that contains the core UI infrastructure.
The class hierarchy reveals that PMPs are built on MFC’s CPropertyPage — Microsoft Foundation Classes, the C++ framework that dominated Windows development in the 1990s:
CPropertyPage (MFC)
└─ uiDveDialogTemplate_c
└─ uiDveDialog_c
└─ uiDvePage_c
└─ uiDynamicDvePage_c
The constructors tell the story. uiDvePage_c takes a DLGTEMPLATE* as its first argument — a Win32 dialog template structure. This is how Windows has created dialog boxes since the early 1990s. The DLGTEMPLATE defines the layout, and MFC’s CPropertyPage wraps it in a property sheet tab.
// From exported symbol signatures in slduiu.dll
uiDvePage_c(DLGTEMPLATE*, uiModelDoc_c*, uoSelRequest_c*, blockType_e, ...)
uiDynamicDvePage_c(DLGTEMPLATE*, uiModelDoc_c*, ...)
The controls you interact with through the API are not standard Windows common controls. They’re custom MFC classes that SolidWorks draws itself:
uiDveButton_c— the PMP button controluiDveCheckBox_c— the PMP checkboxuiDveRadioButton_c— radio buttonsuiDveStaticCtrl_c— labels and static textuiDveFeaturePage_c— feature-specific PMP pagesuiDveMessageDlg_c/uiDveSketchMessageDlg_c— the yellow message panel at the top
For numeric input, SolidWorks uses custom controls from SLDMFCU.dll (the SolidWorks MFC utility layer, 9,178 exports):
CNumericControl/CNumericControlCallBack— the unit-aware numeric inputCEquationControl/CEquationNumericControl— equation-capable number fieldsCCheckBoxBitmapCombo— the combined checkbox/bitmap/combo control
The dynamic DVE system adds typed property controls:
uiDynamicDvePropertyBoolean_cuiDynamicDvePropertyColor_cuiDynamicDvePropertyDouble_cuiDynamicDvePropertyEnum_cuiDynamicDvePropertyFilePath_cuiDynamicDvePropertyInteger_cuiDynamicDvePropertyString_cuiDynamicDvePropertyDivider_c
These map to the dynamic controls that the API exposes. When you call AddControl(id, swControlType_Numberbox, ...), SolidWorks creates a uiDynamicDvePropertyDouble_c internally, wraps it in the DVE container, and hands you back a COM interface.
The Docking Framework: Codejock ToolkitPro
The PMP panel doesn’t dock itself. We found that DockingPaneHelperU.dll depends on ToolkitProVC141x64U.dll — Codejock Software’s Xtreme ToolkitPro, a commercial MFC extension library. SolidWorks uses it for:
CXTPDockingPane— the docking pane framework (how PMPs attach to the left side)CXTPCommandBar— the command/toolbar systemCXTPTaskPanel— task panel framework
SolidWorks wraps Codejock’s infrastructure with its own classes: CSWDockingPaneManager, CDockingPaneHelper, CDockerAppBridge, CDockerUIBridge. This is why the PMP docking behavior feels slightly different from Visual Studio’s docking — it’s a different library underneath.
The WPF Migration Nobody Talks About
Here’s the most surprising finding. SolidWorks is actively migrating Property Manager Pages to WPF.
We found these DLLs in the SolidWorks installation directory:
| DLL | Purpose |
|---|---|
propertiesManagerWPF.dll | WPF PropertyManager implementation (.NET assembly) |
WPFSupport.dll | C++/CLI bridge between native MFC and managed WPF |
WPFRes.dll | WPF resources and styles |
AnnotationWPF.dll | Annotation feature PMPs in WPF |
FeatureWPF.dll | General feature PMPs in WPF |
SketchWPF.dll | Sketch feature PMPs in WPF |
SheetMetalWPF.dll | Sheet metal feature PMPs in WPF |
EnvironmentWPF.dll | Environment settings PMPs in WPF |
RefPlaneWPF.dll | Reference plane PMPs in WPF |
WPFSupport.dll is the critical piece — it’s a mixed-mode C++/CLI assembly with both a native entry point and CLR metadata. It links to both mfc140u.dll and mscoree.dll (the .NET CLR host). Inside, we found WPFDve_c, the managed wrapper class that bridges the native uiDvePage_c MFC page to a managed DVEPage_c WPF page.
The bridge mechanism works through System.Windows.Interop.HwndSource:
DVEPage_cextendsSystem.Windows.Controls.Page(a standard WPF Page)- It has a
set_ParentHwnd(IntPtr)method that creates a newHwndSource - The
HwndSourcecreates a Win32 HWND from the WPF visual tree - That HWND gets parented into the native MFC DVE container
The native uiDvePage_c base class has a virtual method isWPFPage() that returns a boolean. This is the runtime switch — when SolidWorks creates a PMP page, it checks whether to instantiate an MFC-based DVE page or a WPF-based one. Both render inside the same docking container.
The WPF side has its own control vocabulary in propertiesManagerWPF.dll:
PMCheckBox, PMComboBox, PMNumeric3DCC, PMLabel3DCC, PMCheckBox3DCC, PMComboBox3DCC, PMActionButton, PMIconicActionButton, PMStyleActionButton, PMStyleToggleButton, PMIconicRadioButton, PMExpander, PMMessageExpander, PMColumn, PMControlRow, PMHeaderRow, PMIcon, PMIconLabel, PMFilter, PMDocument, PMRulesSet, PMActiveState, PMDefault, PMCase, PMCaseOperand
These are WPF-native controls that replace the MFC uiDve* controls for migrated pages. The “3DCC” suffix likely stands for “3D Context Controls” — controls that interact with the 3D viewport.
A .NET assembly called Controls.dll provides a binding/dependency system (BoolBinding, ComboBoxBinding, ColorBinding) that likely drives the data-binding for WPF PMP controls — the same MVVM pattern that WPF was designed for.
Why Not WinForms? Why Not WPF From the Start?
SolidWorks was founded in 1993. The first version shipped in November 1995. At that time, there was no .NET Framework (released 2002), no WinForms, no WPF (released 2006). MFC was the standard way to build Windows applications in C++, and SolidWorks’ founding vision was to build exclusively for Windows, leveraging standard Windows capabilities.
By the time WinForms arrived in 2002, SolidWorks had millions of lines of MFC code and a COM-based API that third-party developers had built against. The SolidWorks API uses Single-Threaded Apartment (STA) COM — all objects live in an STA, meaning only the creating thread can access them. This provides automatic synchronization without requiring components to manage thread safety, but it’s also why SolidWorks is fundamentally single-threaded for modeling operations.
WinForms was skipped entirely — it’s essentially a managed wrapper around Win32 controls, offering nothing that MFC didn’t already provide. The overhead of the .NET runtime for UI that was already working in native C++ made no sense.
WPF was more interesting but came with serious technical problems for CAD:
GPU texture churn. WPF creates a new GPU texture every frame and destroys it afterward — one of the slowest GPU operations. CAD viewports need persistent GPU resources for real-time rotation of complex assemblies.
CPU-based tessellation. WPF tessellates vector graphics on the CPU before sending them to the GPU. For complex engineering drawings with thousands of entities, this creates bottlenecks.
Multiple draw calls for simple shapes. A single stroked ellipse in WPF generates three separate draw calls. Multiply that by the thousands of geometric primitives in a CAD viewport.
Vertex buffer locking. WPF locks vertex buffers in a way that causes CPU-GPU synchronization stalls — the GPU waits for the CPU, or vice versa. CAD needs both running in parallel.
Threading model mismatch. WPF’s
Dispatchermodel dispatches work to the UI thread asynchronously. SolidWorks’ STA COM model requires that the same thread that created an object executes its methods. These two models fight each other.
None of this means WPF is bad — it’s excellent for business applications, dashboards, and data-heavy UIs. But for a CAD application that needs to render 100,000 tessellated faces at 60fps while the user drags a dimension, WPF’s rendering pipeline is the wrong tool.
What SolidWorks did instead was pragmatic: keep MFC for the 3D viewport and legacy UI, but gradually introduce WPF for the panel controls where its data-binding and styling capabilities outweigh the rendering overhead. The isWPFPage() toggle lets them migrate one PMP at a time without a rewrite.
How FreeCAD Does It Differently
FreeCAD solves the same problem — a side panel for feature parameters — using a completely different architecture. Where SolidWorks uses COM interfaces and MFC, FreeCAD uses Qt widgets inside a QDockWidget.
The framework is managed by ControlSingleton, a C++ singleton that enforces a one-dialog-per-document policy. When you double-click a feature, FreeCAD creates a TaskDialog subclass, populates it with TaskBox containers (collapsible sections, equivalent to SolidWorks groups), and slides it into the dock panel.
The key architectural differences:
Layout design: FreeCAD developers design PMP layouts visually in Qt Designer, producing .ui XML files. SolidWorks developers write API calls — AddGroupBox, AddControl, SetRange — in code. There’s no visual editor for SolidWorks PMPs.
Control ownership: In FreeCAD, you create actual Qt widgets (QComboBox, QSpinBox, QCheckBox) and hand them to the framework. In SolidWorks, you call AddControl and the framework creates the control and gives you a COM interface to it. You never touch the underlying window.
Selection integration: FreeCAD uses an Observer pattern — task panels inherit SelectionObserver and override onSelectionChanged(const SelectionChanges& msg) to receive push notifications. SolidWorks uses COM callbacks — your handler receives OnSelectionboxFocusChanged and OnSubmitSelection events. The FreeCAD approach lets the panel decide what to do with selection changes. The SolidWorks approach is more constrained but handles edge cases (like filtering by selection marks) automatically.
Extensibility: FreeCAD task panels are Python-scriptable through TaskDialogPython. SolidWorks exposes PMPs through COM, accessible from VBA, VSTA, and any .NET language. Both approaches work, but FreeCAD’s is far more transparent — you can read the source code for any built-in task panel.
The tradeoff is that FreeCAD’s Qt approach is cross-platform and fully open, while SolidWorks’ proprietary approach gives tighter integration with the rest of the application at the cost of being opaque to developers.
What This Means for Add-In Developers
If you’re building SolidWorks add-ins, understanding the PMP architecture explains some common frustrations:
Why you can’t style PMP controls. The built-in controls are custom MFC classes rendered by SolidWorks. The COM interface exposes TextColor and BackgroundColor, but that’s it. No fonts, no custom drawing, no templates. If you need custom UI, use IPropertyManagerPageWindowFromHandle to embed your own WPF or WinForms window.
Why the LockedPage option matters. The API docs warn that you must specify swPropertyManagerOptions_LockedPage when creating your PMP. Without it, if a handler callback triggers code that closes the PMP, SolidWorks can crash because the MFC dialog template gets destroyed while the callback is still on the stack. This is a classic MFC lifetime issue — the uiDvePage_c destructor runs while the handler is executing.
Why properties can only be set before Show(). The DVE system reads control properties from the DLGTEMPLATE and internal state during page creation. Once the page is displayed, the MFC dialog template is already instantiated. Changing properties after display would require recreating the native window, which the COM wrapper doesn’t support.
Why IPropertyManagerPage::GetDialogWindow returns an HWND. The original IPropertyManagerPage (v1) exposed GetDialogWindow and GetDialogWindowx64, which return the actual Win32 HWND of the dialog. This was removed from IPropertyManagerPage2, probably because exposing the raw HWND let developers make direct Win32 calls that could destabilize the DVE system. The WindowFromHandle control type is the sanctioned replacement.
The Handler9 Callback Reference
For developers implementing IPropertyManagerPage2Handler9, here’s the complete callback list with the Win32 message-level detail that the API docs don’t always make clear:
| # | Callback | When It Fires |
|---|---|---|
| 1 | AfterActivation() | Page finished opening and is fully visible |
| 2 | OnClose(int Reason) | Page closing — Reason is swPropertyManagerPageCloseReasons_e |
| 3 | AfterClose() | Page fully closed and destroyed |
| 4 | OnHelp() | Help button clicked — return true to suppress default help |
| 5-6 | OnPreviousPage() / OnNextPage() | Multi-page navigation — return true to cancel |
| 7 | OnPreview() | Preview toggled |
| 8-9 | OnWhatsNew() / OnUndo() / OnRedo() | Edit operations |
| 10 | OnTabClicked(int Id) | Tab selected — return true to cancel switch |
| 11-12 | OnGroupExpand / OnGroupCheck | Group collapsed/expanded or checked |
| 13-17 | Control value callbacks | OnCheckboxCheck, OnOptionCheck, OnButtonPress, OnTextboxChanged, OnNumberboxChanged |
| 18-20 | Combobox/Listbox | OnComboboxEditChanged, OnComboboxSelectionChanged, OnListboxSelectionChanged |
| 21-22 | Selection box | OnSelectionboxFocusChanged, OnSelectionboxListChanged |
| 23-24 | Callout | OnSelectionboxCalloutCreated / Destroyed |
| 25 | OnSubmitSelection(Id, Selection, SelType, ref ItemText) | Validate a selection — return false to reject |
| 26 | OnActiveXControlCreated(Id, Status) | ActiveX control ready |
| 27-28 | Slider | OnSliderPositionChanged, OnSliderTrackingCompleted |
| 29 | OnKeystroke(Wparam, Message, Lparam, Id) | Raw Win32 keyboard message — return true to consume |
| 30-31 | Popup menu | OnPopupMenuItem, OnPopupMenuItemUpdate |
| 32-33 | Focus | OnGainedFocus, OnLostFocus |
| 34 | OnWindowFromHandleControlCreated(Id, Status) | HWND-hosted control ready |
| 35 | OnListboxRMBUp(Id, PosX, PosY) | Right-click in listbox |
| 36 | OnNumberBoxTrackingCompleted(Id, Value) | Numberbox slider drag finished |
The OnKeystroke callback is revealing — it passes raw WM_KEYDOWN/WM_KEYUP parameters (Wparam, Message, Lparam), confirming that the control system sits directly on top of the Win32 message pump.
How CadShift Uses This Knowledge
CadShift automates SolidWorks workflows including batch DXF export and file conversions. Understanding the PMP architecture matters for our add-in development because it determines how we build our configuration interfaces.
For simple settings, we use the built-in PMP controls — they match SolidWorks’ visual language and handle keyboard navigation, DPI scaling, and theme changes automatically. For complex configuration (file mapping tables, preview panels, batch operation queues), we embed WPF controls via IPropertyManagerPageWindowFromHandle, giving us full MVVM data binding while staying inside the SolidWorks panel system.
The isWPFPage() discovery also tells us where SolidWorks is heading. As more built-in PMPs migrate to WPF, the visual language will evolve, and add-ins that embed WPF controls will blend in better over time.
Takeaways
Property Manager Pages are not standard Windows controls. They’re custom MFC classes (the “DVE” system) built on
CPropertyPageandDLGTEMPLATEstructures, with the controls owner-drawn by SolidWorks inslduiu.dll.SolidWorks is migrating PMPs to WPF, one page at a time.
WPFSupport.dllbridges MFC and WPF viaHwndSource, andisWPFPage()on the nativeuiDvePage_cclass is the runtime switch.WinForms was never used internally. SolidWorks went from MFC directly to WPF for managed UI, bypassing WinForms entirely.
The docking framework is Codejock ToolkitPro, a commercial MFC extension library — not a SolidWorks-built system.
Two escape hatches exist for custom UI:
IPropertyManagerPageActiveX(embed any COM control by CLSID) andIPropertyManagerPageWindowFromHandle(embed any HWND). If you need UI beyond the 15 built-in control types, these are your options.The 37-callback handler interface (
IPropertyManagerPage2Handler9) reveals the control system’s Win32 roots — rawWparam/Lparammessage parameters, HWND handles, and COM dispatch interfaces all the way down.