Repository navigation
Plan viewer: properties panel row model, filter box, copy menu, remembered width - #4588
Merged
Merged
Conversation
…bered width
The plan viewer's properties panel built a flat tree of controls per row with
no row model, filter box, or copy menu, and reset the panel width to a fixed
320px on every node selection.
Adds a shared row model (label, value, isCode, searchable text) in
PerformanceMonitor.PlanAnalysis.PropertyRows, so the filter and the copy
menu work from recorded data instead of the visual tree. On top of that:
- A filter box under the panel header: case-insensitive on label and value;
a section with no matching rows hides; a section whose own title matches
keeps all its rows; Esc clears the filter while it has focus; the text
sticks across node selections.
- A copy menu on each value control ('Copy value', 'Copy name and value',
'Copy all properties'), replacing the stock read-only TextBox menu
(greyed-out Cut/Copy, live Paste on data nobody can edit). 'Copy all
properties' renders from the row model, so code values paste back out
verbatim. The clipboard write is guarded against the transient
CLIPBRD_E_CANT_OPEN failure another process holding the clipboard can
cause.
- The panel width is set only on the hidden-to-visible transition and
remembered for the session (default 380, clamped 280-800 while open),
instead of being reset on every selection.
Lite and the Darling Viewer both host the shared PlanViewerControl, so both
get this without any change on their side.
## CHANGELOG entry
SECTION: Changed
ENTRY: The plan viewer's properties panel now has a filter box, a copy menu
with 'Copy value' / 'Copy name and value' / 'Copy all properties', and
remembers the width you drag it to for the session. ([#PR])
[#PR]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/PR
erikdarlingdata
marked this pull request as ready for review
September 28, 2026 15:44
This was referenced Sep 28, 2026
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #4574, part of #4511.
Why
The plan viewer's properties panel built a flat tree of controls per row with no row model, no filter box, and no copy menu, and reset the panel width to a fixed 320px on every node selection. PerformanceStudio's Avalonia viewer (
erikdarlingdata/PerformanceStudio@435e12d,@e156e56,@217c580,@b5ba82e) added a row model, a filter box, a copy menu, and a remembered width; this ports that to PM's shared WPFPlanViewerControl.What changes
PerformanceMonitor.PlanAnalysis.PropertyRows:PropertyRow,PropertySection) records each row's label, value,isCode, and searchable text as the panel builds, instead of only adding it to the visual tree. Put inPerformanceMonitor.PlanAnalysis(notPerformanceMonitor.Ui, which is net10.0-windows-only) so a unit test can pin the filter and copy logic without WPF.ApplyPropertiesFilter()re-runs after each rebuild).TextBoxcontext menu (greyed-out Cut/Copy, live Paste on data nobody can edit). "Copy all properties" renders from the row model, not the visual tree, so a code value (a predicate, aCREATE INDEXstatement) pastes back out verbatim.TrySetClipboardText) against the transientCLIPBRD_E_CANT_OPENfailure another process briefly holding the clipboard can cause — the write-side sibling of PM's existing guarded-read helper (ClipboardText.cs). Kept minimal and local to this file rather than adding a new shared writer class; a later change can promote it if a second caller needs it.MinWidth/MaxWidthare cleared on close so they don't hold a strip of the panel open.Lite: gets this too (shared control). Both the Darling Viewer and Lite host
PerformanceMonitor.Ui.PlanViewerControl, so this change reaches both with no per-app work.PM-ahead / Not ported: PS's splitter hover-highlight cosmetic touch (from
@b5ba82e) is not ported — WPF'sGridSplitterstyling isn't a drop-in match for Avalonia's, and the issue text called it optional.Test plan
New file:
Darling/Darling.Tests/Viewer4574Tests.cs, 18 pins onPropertyRows/PropertyRow(filter section-title match and case-insensitivity, row matching, section-visibility, width clamp,CopyValue/CopyLabelAndValue,BuildPropertiesTextfor ordinary/code/block rows and section-skipping).PropertyRows/PropertySectiondon't exist on dev; built the same test file against a detached worktree at dev's tip (130bc6a) and gotCS0246: The type or namespace name 'PropertyRow' could not be found/ same forPropertySection.RowMatches's own-text comparison fromStringComparison.OrdinalIgnoreCaseto.Ordinal, rebuilt, reran —RowMatches_OwnTextMatch_CaseInsensitiveOnLabelAndValuefailed as expected. Reverted, rebuilt, reran green.dotnet Darling.Tests.dll -class Darling.Tests.Viewer4574Tests→Total: 18, Errors: 0, Failed: 0, Skipped: 0, Not Run: 0.Darling.Tests,PerformanceMonitor.PlanAnalysis(via that build),PerformanceMonitor.Ui,Lite/PerformanceMonitorLite.csproj,Lite.Tests,Darling/PerformanceMonitor.Darling.Viewer.Viewer4574Tests+ everyPlanSync*/Viewer*class + the namedActual*/Darling*/ShowPlanParser*/etc. classes):Total: 261, Errors: 0, Failed: 12. The 12 failures are all inDarlingAnalysisPipelineTests/DarlingMcpPlanToolsLivePostgresTests, pre-existing live-Postgres tests whose fixture couldn't connect on this rig (nopsql/pg_isreadyavailable here to stand up the store) — unrelated to this change; nothing in this PR touches that pipeline.PlanViewerBenefitDisplayTests,PlanViewerCapabilityPinTests,PlanViewerOriginNavigationTests, and the newViewer4574Tests(the classes actually exercising viewer/plan-analysis surfaces this PR touches) are all green.Lite.Testsbuilds clean but its discovery doesn't run on macOS; not exercised here — the properties panel isn't a Lite-only surface (it's the sharedPerformanceMonitor.Uicontrol, pinned viaDarling.Testsabove), so nothing Lite-specific needed a new Lite pin.warning (CA|CS|IDE)[0-9]+) was empty on every build log above.Screenshot plan (UI, not covered by the unit pins): open a plan with at least one node that has a section with several properties (e.g. a Clustered Index Scan node — "Object", "Estimated Costs", "Estimated Rows" sections). Select the node, open the Properties panel, and check:
CHANGELOG
SECTION: Changed
ENTRY: - The plan viewer's properties panel can be filtered and copied, and keeps its width ([#4588]) - The panel now has a filter box, a copy menu with "Copy value" / "Copy name and value" / "Copy all properties", and remembers the width you drag it to for the session.
REF: [#4588]: #4588