extract _WPFMixin, bring WPFPanel to feature parity with WPFWindow#3177
extract _WPFMixin, bring WPFPanel to feature parity with WPFWindow#3177jmcouffin merged 9 commits intopyrevitlabs:developfrom
Conversation
…with WPFWindow
- Add _WPFMixin with shared logic: setup_resources, merge_resource_dict,
get_locale_string, set_image_source, hide/show/toggle/disable/enable_element,
dispatch, pyrevit_version, handle_url_click
- Extract _resolve_xaml_source() as module-level helper (full locale-fallback
chain: .{locale}.xaml > .en_us.xaml > ResourceDictionary variants)
- WPFWindow and WPFPanel both inherit _WPFMixin; no public API changes
- WPFPanel: add load_xaml() with literal_string and locale support
- Fix setup_resources() call order — must precede LoadComponent in both classes
to avoid StaticResourceExtension resolution errors at parse time
- Improve toggle_dockable_panel() error message when pane is registered
but not yet created (guides developer to startup.py)
- Add DeveloperSamplePanel.smartbutton: exercises all WPFPanel/mixin features,
smartbutton icon reflects open/closed state
- Document startup.py registration requirement in WPFPanel docstring and sample
- remove dockable register as global (was cleared on engine restart), go via hostapp instead
There was a problem hiding this comment.
PR Summary:
- Extracts
_WPFMixinwith shared WPF helpers (dispatch, locale, visibility, enable) inherited by bothWPFWindowandWPFPanel - Introduces
_resolve_xaml_sourceas a module-level function replacing the old_determine_xamlinstance method - Fixes resource-setup ordering:
setup_resourcesnow runs beforeLoadComponentin both classes - Adds a
DeveloperSamplePanelwith a smartbutton to exercise all new features get_dockable_panelis refactored to query Revit directly viaGetDockablePaneinstead of a module-level registry
Review Summary:
The refactoring is structurally clean and the _WPFMixin extraction eliminates real duplication. However, the PR introduces several functional regressions in the dockable panel helpers. get_dockable_panel now raises PyRevitException when the panel is not registered, but its docstring still advertises None as a return value — a silent API contract break for all existing callers. The toggle_dockable_panel guard (if dockable_panel:) becomes dead code as a result. The sample smartbutton script calls get_dockable_panel at module level with no exception guard, so it will crash on every button load if startup.py hasn't run. WPFPanel also sets thread_id after load_xaml returns, leaving dispatch() unavailable during any subclass __init__ code that runs before the assignment, unlike WPFWindow which correctly sets it inside load_xaml. The issubclass call in get_dockable_panel lost its isinstance(…, type) guard, so passing a panel instance instead of the class raises a raw TypeError instead of the descriptive PyRevitException.
Follow-up suggestions:
@devloaifix the identified issues: updateget_dockable_paneldocstring and exception contract, add a try/except guard around the module-level call inDockablePane.smartbutton/script.py, movethread_idassignment intoWPFPanel.load_xaml, and restore theisinstance(…, type)guard on theissubclassbranch.
...evTools.extension/pyRevitDev.tab/Developer Examples.panel/DockablePane.smartbutton/script.py
Outdated
Show resolved
Hide resolved
- docstring + leftovers - safeguard non-class inputs - thread_id inside load_xaml for safe dispatch access
There was a problem hiding this comment.
Pull request overview
Refactors pyRevit’s IronPython WPF helpers by extracting shared WPFWindow/WPFPanel behavior into a common mixin, and updates the dockable panel implementation and devtools samples to exercise the expanded feature set.
Changes:
- Introduces
_WPFMixinand_resolve_xaml_sourceto centralize resources, locale/resource-dictionary loading, dispatcher helpers, and UI element helpers. - Updates
WPFWindow.load_xamland addsWPFPanel.load_xamlwith consistent locale/resource-dictionary handling and correct resource-merge ordering (beforeLoadComponent). - Extends pyRevitDevTools startup with a new
DeveloperSamplePaneland adds accompanying XAML + ResourceDictionary sample files.
Reviewed changes
Copilot reviewed 6 out of 8 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
pyrevitlib/pyrevit/forms/_ipy.py |
Extracts shared WPF behavior into _WPFMixin, adds _resolve_xaml_source, and updates dockable panel helpers/implementation. |
extensions/pyRevitDevTools.extension/startup.py |
Adds an extended dockable panel sample (DeveloperSamplePanel) demonstrating mixin/panel features. |
extensions/pyRevitDevTools.extension/SamplePanel.xaml |
New sample panel UI exercising visibility/enabled/dispatch/url-click patterns. |
extensions/pyRevitDevTools.extension/SamplePanel.ResourceDictionary.en_us.xaml |
New sample ResourceDictionary used to demonstrate locale resource merging. |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
- docstring - try/catch for GUID
…Revit into improve-panel-lib
…el script - Updated the panel source path in DeveloperSamplePanel to use a dynamic path. - Enhanced error handling in the dockable pane script to alert users if the panel is not registered. - Cleaned up code formatting and improved readability in the forms module.
|
@Wurschdhaud you may want to double check the changes I introduced, not sure they don't break the toggle button. I may have messed up my clone at some point while reviewing and testing way too many PR |
|
📦 New work-in-progress (wip) builds are available for 6.1.0.26083+2130-wip |
|
📦 New work-in-progress (wip) builds are available for 6.1.0.26086+2004-wip |
|
📦 New work-in-progress (wip) builds are available for 6.1.0.26088+1318-wip |
|
📦 New work-in-progress (wip) builds are available for 6.1.0.26089+1231-wip |
|
📦 New work-in-progress (wip) builds are available for 6.1.0.26090+0549-wip |
|
📦 New work-in-progress (wip) builds are available for 6.1.0.26090+1533-wip |
|
📦 New work-in-progress (wip) builds are available for 6.1.0.26090+1536-wip |
|
📦 New work-in-progress (wip) builds are available for 6.1.0.26090+1540-wip |
|
📦 New work-in-progress (wip) builds are available for 6.1.0.26090+1540-wip |
|
📦 New work-in-progress (wip) builds are available for 6.1.0.26090+1556-wip |
|
📦 New public release are available for 6.2.0.26090+1754 |
Description
WPFPanelwas a thin wrapper that delegated everything back toWPFWindowstatics. This PR brings it to full feature parity.Shared behaviour (dispatch, locale support, resource helpers, element visibility/enabled helpers) is extracted into a new
_WPFMixinthat both classes inherit — no duplication, no breaking changes to existing call sites._resolve_xaml_sourcereplacesWPFWindow._determine_xamlwith a module-level function bothload_xamlimplementations call.A fix is included for a latent ordering bug: setup_resources must run before LoadComponent in both classes, otherwise any XAML referencing {StaticResource pyRevit...} throws on parse.
toggle_dockable_panel gets a clearer exception when the pane is registered but not yet created, pointing developers toward the real fix: registration must happen in startup.py on UIControlledApplication, not from a button script.
A new DeveloperSamplePanel.smartbutton exercises every feature with inline comments explaining the lifecycle.
Checklist
Before submitting your pull request, ensure the following requirements are met:
pipenv run black {source_file_or_directory}