Skip to content

Add Server dialog: scroll the form, pin the buttons, clamp to the work area (#1828) - #1829

Merged
erikdarlingdata merged 1 commit into
devfrom
fix/1828-add-server-dialog-overflow
Jul 30, 2026
Merged

erikdarlingdata merged 1 commit into
devfrom
fix/1828-add-server-dialog-overflow

Conversation

@erikdarlingdata

Copy link
Copy Markdown
Owner

Summary

Fixes #1828: on a 1080p work area, selecting SQL auth, Service Principal, or Managed Identity grew the Add Server dialog (SizeToContent="Height" + ResizeMode="NoResize", no scroll, no MaxHeight) past the screen bottom, taking Test Connection / Save / Cancel with it — reachable only by blind tabbing, with resize disabled as the escape hatch. The reporter's screenshot shows the dialog at the full 1080px screen height. Both copies with the defect are fixed — Lite (reported) and the Darling Viewer (identical structure); the deprecated Dashboard's twin already had the correct shape and supplied the pattern.

  • Form scrolls, footer doesn't: outer grid becomes */Auto; the form lives in a ScrollViewer in the star row; the status line + buttons are pinned outside the scroll so they are always visible at any window height.
  • Window clamps and resizes: MaxHeight bound to SystemParameters.WorkArea.Height, ResizeMode="CanResizeWithGrip" (matching AddMultipleServersDialog), MinWidth/MinHeight floors. SizeToContent still auto-sizes compact dialogs; the scrollbar engages only when clamped.
  • Position, not just size: SizeToContent growth is top-anchored, so mid-session growth could still slide the footer past the screen edge without ever reaching the clamp (ui-reviewer caught this residual — it reproduces in the most common flow: open compact, then pick a tall auth mode). A SizeChanged handler pulls the window up when its bottom crosses the work area — the SizeToContent-friendly form of the Dashboard's SizeToWorkArea() top-pinning.
  • Keyboard: Alt mnemonics on all three buttons (the reporter explicitly looked for Alt+ options) and IsCancel on Cancel so Esc closes the dialog, matching EditCredentialProfileDialog / AddMultipleServersDialog. Save deliberately does NOT gain IsDefault — Enter-to-save from a half-filled form is a behavior change beyond this bug.

EditCredentialProfileDialog shares the window shape but its fully-expanded content tops out around 550px, so it cannot overflow any real work area — deliberately left untouched.

Test plan

  • Lite + Darling Viewer build: 0 warnings, 0 errors
  • ui-reviewer pass: control/x:Name/handler/Grid.Row inventory identical before and after in both files; no orphaned row indexes; no access-key collisions; IsCancel verified safe against both CancelButton_Click handlers (guarded same-value DialogResult, no double-close); scrollbar gutter arithmetic checked (widest row 350px vs ~401px available when clamped)
  • Visual pass on a 1080p display (Erik) — dialog opens compact, grows until clamped, scrolls beyond that, buttons always visible

🤖 Generated with Claude Code

…k area (#1828)

SizeToContent="Height" + ResizeMode="NoResize" with no scroll meant the
dialog grew wherever its content took it: on a 1080p work area, selecting
SQL auth, Service Principal, or Managed Identity pushed Test Connection /
Save / Cancel off the bottom of the screen, reachable only by blind
tabbing, with resize disabled as the escape hatch.

Both copies (Lite and the Darling Viewer) now mirror the deprecated
Dashboard twin's proven shape: the form scrolls in a star row, the status
line and buttons are pinned OUTSIDE the scroll so they are always visible,
MaxHeight clamps the window to the primary work area, and the window is
resizable with a grip (MinWidth/MinHeight floors). Because SizeToContent
growth is top-anchored, MaxHeight alone still let mid-session growth
slide the footer past the screen edge without hitting the clamp - a
SizeChanged handler pulls the window up when its bottom crosses the work
area, the SizeToContent-friendly form of the Dashboard's SizeToWorkArea
top-pinning.

The buttons gain Alt mnemonics (the reporter explicitly looked for them)
and Cancel gains IsCancel so Esc closes the dialog, matching
EditCredentialProfileDialog and AddMultipleServersDialog.

ui-reviewer pass: control/name/handler inventory identical before and
after, no orphaned Grid.Row indexes, no access-key collisions, IsCancel
verified safe against both CancelButton_Click handlers (guarded same-value
DialogResult, no double-close).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@claude

claude Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review

Overview: Fixes #1828 by reworking AddServerDialog in both Lite and the Darling Viewer identically — the form now lives in a ScrollViewer inside a star row, the status line + buttons are pinned in an Auto row outside the scroll, MaxHeight clamps to the work area, ResizeMode changes to CanResizeWithGrip, and a new Dialog_SizeChanged handler pulls the window up if mid-session growth (e.g. switching to a taller auth mode) pushes the footer past the bottom of the screen. Buttons get Alt mnemonics and IsCancel on Cancel. CHANGELOG entry included.

Parity: Lite and Darling changes are structurally identical (same row restructuring, same MaxHeight/SizeChanged additions, same mnemonics/IsCancel pattern) — no drift between the two copies. The deprecated/Dashboard twin was left untouched, which the PR description explains is intentional (it already has the correct shape and was the source pattern).

Correctness: The row re-indexing looks consistent — inner grid still has 10 Auto rows matching the header/fields, and the four mutually-exclusive credential panels correctly still share Grid.Row="7" (only one is ever visible). The outer grid's */Auto split combined with SizeToContent="Height" is the right WPF idiom here (star rows measure like Auto under infinite height, which is why the dialog still auto-sizes compact and only scrolls once MaxHeight actually constrains it). EditCredentialProfileDialog/AddMultipleServersDialog were checked and don't currently use the _Mnemonic convention this PR introduces on buttons — first use of that pattern in these two apps, worth being aware of for future dialogs.

Minor finding: left as inline comments — SystemParameters.WorkArea reflects the primary monitor's work area, not necessarily the monitor the dialog is actually on, which affects both the MaxHeight binding and the new Dialog_SizeChanged logic on multi-monitor setups. Low severity (inherited from the Dashboard's original pattern, not a regression) but worth a note since the whole point of this fix is screen-overflow correctness.

No security-relevant surface here (no new input handling, network, or process use), no SQL touched, no missing-index folklore to worry about. No test coverage added, but that matches the rest of the codebase — WPF dialogs aren't unit tested here.

Comment on lines +654 to +659
private void Dialog_SizeChanged(object sender, SizeChangedEventArgs e)
{
var workArea = SystemParameters.WorkArea;
if (Top + ActualHeight > workArea.Bottom)
{
Top = Math.Max(workArea.Top, workArea.Bottom - ActualHeight);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Minor: SystemParameters.WorkArea always reflects the primary monitor's work area, not necessarily the monitor this window is actually on. On a multi-monitor setup where the dialog opens on a secondary monitor with a different resolution/work area than the primary, this can clamp/reposition based on the wrong monitor's bounds — in the worst case (secondary monitor shorter than primary), the exact off-screen-footer bug this PR fixes could reproduce, since the MaxHeight binding (line 6 in the .xaml) is also computed once against the primary monitor and never re-evaluated.

Not a regression introduced here — it mirrors the Dashboard's SizeToWorkArea() pattern, which has the same limitation — but worth flagging since screen-overflow correctness is the whole point of this fix. A per-monitor-aware alternative (e.g. System.Windows.Forms.Screen.FromHandle via the window handle, or P/Invoke MonitorFromWindow) would close this gap if it's worth the added complexity.

Comment on lines +606 to +611
private void Dialog_SizeChanged(object sender, SizeChangedEventArgs e)
{
var workArea = SystemParameters.WorkArea;
if (Top + ActualHeight > workArea.Bottom)
{
Top = Math.Max(workArea.Top, workArea.Bottom - ActualHeight);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Same note as the Lite copy: SystemParameters.WorkArea reflects the primary monitor, not necessarily the monitor this dialog is on, so both this handler and the MaxHeight binding (line 6 in the .xaml) can clamp/reposition against the wrong monitor's bounds on a multi-monitor setup. Inherited from the Dashboard's original pattern rather than a new regression, just flagging since it's the exact class of bug this PR is fixing.

@erikdarlingdata
erikdarlingdata merged commit 6c31d3e into dev Jul 30, 2026
4 checks passed
@erikdarlingdata
erikdarlingdata deleted the fix/1828-add-server-dialog-overflow branch July 30, 2026 01:46
ianwalkeruk pushed a commit to ianwalkeruk/PerformanceMonitor that referenced this pull request Jul 31, 2026
erikdarlingdata#1892, erikdarlingdata#1911

erikdarlingdata#1891 - the Add Server dialogs clamped against SystemParameters.WorkArea,
which always reports the PRIMARY monitor. On a secondary monitor shorter
than the primary that reproduced the very off-screen-footer bug erikdarlingdata#1828/erikdarlingdata#1829
fixed. The clamp and the height cap now resolve the actual monitor via
MonitorFromWindow + GetMonitorInfo, converted from physical pixels to WPF
units through the window's own presentation source - an unconverted work
area on a 150%-scaled screen would be a worse bug than the one being fixed.
Re-evaluated on SourceInitialized and LocationChanged, because the cap it
replaced was a one-shot XAML binding read once at load, so a dialog dragged
to a shorter screen kept the taller screen's cap forever. Every path that
cannot get a real answer falls back to the old behaviour, so single-monitor
is unchanged. Win32 rather than WinForms: neither app references WinForms
and one lookup is not worth a second UI framework. Shared between both apps
so the twins cannot drift.

erikdarlingdata#1892 - the Query Store slicer and duration trend filtered the window on
collection_time while keying the bars on the interval start. Once erikdarlingdata#1841 made
those different instants the chart disagreed with its own range at both
edges: an interval starting before the range but collected inside it drew a
bar dated before the range began, and the range's final interval - still
open when the range ended - was dropped entirely. Both reads now window on
the expression they place on, in both apps.

A bare collection_time floor stays and is NOT a filter: query_store_stats is
a hypertable partitioned on that column and Lite's view unions the parquet
archive, so without it Postgres decompresses every chunk and DuckDB cannot
skip row groups. An interval is always collected after it starts, so the
floor is provably implied and can never decide membership - the same shape
as the drill-down collector's last_execution_time bound. Deliberately no
upper twin: collection_time can exceed the interval start without bound if
the collector was down when the interval closed, so a ceiling would silently
drop exactly the rows a restarted collector just caught up on.

erikdarlingdata#1911 - Lite's MCP get_alert_settings did not report cpu.mode at all.
Reporting the raw enum name would have traded erikdarlingdata#1895's key-level mismatch for
a value-level one, since Darling emits "sql"/"total" and validates writes
against exactly those. Darling's vocabulary wins as the older public
surface; the cross-app literals are pinned by reading Darling's own source,
because asserting Lite's constants alone would keep passing the day Darling
changed.

Verified live against PostgreSQL 18.4 + TimescaleDB 2.28.1 (4002 passed, 10
skipped - the gated live suite genuinely ran). Every new pin red-watched,
including the live one. The erikdarlingdata#1902 ratchet correctly caught the new live test
using the old teardown idiom; converted to LiveStoreCleanup rather than
raising the ceiling.

Filed rather than folded in: erikdarlingdata#1921 (the item-timeline overlay still plots at
collection_time while the bars it annotates are keyed on the interval start -
a product decision about what the overlay is for) and erikdarlingdata#1922 (the live
fixture keeps using a connection that CREATE EXTENSION timescaledb killed,
masking the real cause).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant