Skip to content

extract _WPFMixin, bring WPFPanel to feature parity with WPFWindow#3177

Merged
jmcouffin merged 9 commits intopyrevitlabs:developfrom
Wurschdhaud:improve-panel-lib
Mar 24, 2026
Merged

extract _WPFMixin, bring WPFPanel to feature parity with WPFWindow#3177
jmcouffin merged 9 commits intopyrevitlabs:developfrom
Wurschdhaud:improve-panel-lib

Conversation

@Wurschdhaud
Copy link
Copy Markdown
Contributor

@Wurschdhaud Wurschdhaud commented Mar 15, 2026

Description

WPFPanel was a thin wrapper that delegated everything back to WPFWindow statics. This PR brings it to full feature parity.
Shared behaviour (dispatch, locale support, resource helpers, element visibility/enabled helpers) is extracted into a new _WPFMixin that both classes inherit — no duplication, no breaking changes to existing call sites. _resolve_xaml_source replaces WPFWindow._determine_xaml with a module-level function both load_xaml implementations 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:

  • Code follows the PEP 8 style guide.
  • Code has been formatted with Black using the command:
    pipenv run black {source_file_or_directory}
  • Changes are tested and verified to work as expected.

…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
Copy link
Copy Markdown
Contributor

@devloai devloai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Summary:

  • Extracts _WPFMixin with shared WPF helpers (dispatch, locale, visibility, enable) inherited by both WPFWindow and WPFPanel
  • Introduces _resolve_xaml_source as a module-level function replacing the old _determine_xaml instance method
  • Fixes resource-setup ordering: setup_resources now runs before LoadComponent in both classes
  • Adds a DeveloperSamplePanel with a smartbutton to exercise all new features
  • get_dockable_panel is refactored to query Revit directly via GetDockablePane instead 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:

  • @devloai fix the identified issues: update get_dockable_panel docstring and exception contract, add a try/except guard around the module-level call in DockablePane.smartbutton/script.py, move thread_id assignment into WPFPanel.load_xaml, and restore the isinstance(…, type) guard on the issubclass branch.

- docstring + leftovers
- safeguard non-class inputs
- thread_id inside load_xaml for safe dispatch access
@jmcouffin jmcouffin requested a review from Copilot March 15, 2026 20:14
@jmcouffin jmcouffin self-assigned this Mar 15, 2026
@jmcouffin jmcouffin added the Enhancement Enhancement request [class->Improved #{number}: {title}] label Mar 15, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 _WPFMixin and _resolve_xaml_source to centralize resources, locale/resource-dictionary loading, dispatcher helpers, and UI element helpers.
  • Updates WPFWindow.load_xaml and adds WPFPanel.load_xaml with consistent locale/resource-dictionary handling and correct resource-merge ordering (before LoadComponent).
  • Extends pyRevitDevTools startup with a new DeveloperSamplePanel and 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.

Wurschdhaud and others added 4 commits March 15, 2026 22:04
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
- docstring
- try/catch for GUID
tay0thman and others added 3 commits March 20, 2026 01:01
…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.
@jmcouffin jmcouffin merged commit 9cca5a5 into pyrevitlabs:develop Mar 24, 2026
@jmcouffin
Copy link
Copy Markdown
Contributor

@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

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26083+2130-wip

@Wurschdhaud Wurschdhaud deleted the improve-panel-lib branch March 25, 2026 18:39
@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26086+2004-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26088+1318-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26089+1231-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26090+0549-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26090+1533-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26090+1536-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26090+1540-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26090+1540-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26090+1556-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New public release are available for 6.2.0.26090+1754

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Enhancement Enhancement request [class->Improved #{number}: {title}]

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants