Skip to content

Result DataGrids: last column does not fill remaining width until window resize #3416

@BornToBeRoot

Description

@BornToBeRoot

Result DataGrids: main column does not fill remaining width until window resize

Background

The DNSLookupView originally rendered the Result column at its MinWidth and only filled the remaining width after the user resized the window. Root cause is a known WPF behaviour: when a DataGrid is initially empty and HorizontalScrollBarVisibility="Auto" (default), the inner ScrollViewer measures with infinite width. Star-sized columns (Width="*") then collapse to their MinWidth. A re-measure is only triggered by a window resize — adding rows alone is not enough.

The fix in DNSLookupView was:

  1. Last (or otherwise designated) column gets Width="*" while all earlier columns keep MinWidth only.
  2. HorizontalScrollBarVisibility stays at the default (Auto) so the horizontal scrollbar still appears when the sum of MinWidth values exceeds the viewport.
  3. A one-shot LoadingRow handler in code-behind resets star columns once on the first rendered row, forcing the recompute. The handler unsubscribes itself.

Reference files:

  • Source/NETworkManager/Views/DNSLookupView.xaml — column definition with Width="*", LoadingRow wired up.
  • Source/NETworkManager/Views/DNSLookupView.xaml.csDataGridResults_LoadingRow handler. Includes using System.Windows.Threading; for DispatcherPriority.

Goal

Apply the same pattern to other result-style views so their main "content" column fills the remaining width on first paint and the scrollbar still appears when the window is too narrow. Decide per view whether enabling TextWrapping="Wrap" on the star column is desirable.

Views to audit

Result views (start empty, populated by user action) — primary candidates:

  • IPScannerView.xaml (uses MultiSelectDataGrid)
  • PortScannerView.xaml (uses MultiSelectDataGrid)
  • TracerouteView.xaml (check control type)
  • SNMPView.xaml
  • SNTPLookupView.xaml
  • WiFiView.xaml
  • LookupPortLookupView.xaml
  • LookupOUILookupView.xaml
  • SubnetCalculatorSubnettingView.xaml

Possibly affected (auto-load on view open, but the bug can still bite if first load is async):

  • ConnectionsView.xaml
  • ListenersView.xaml
  • NeighborTableView.xaml
  • FirewallView.xaml

Not in scope (data is configured, so the DataGrid usually has content right at first measure):

  • AboutView.xaml (already uses Width="*", works because data is synchronous on load)
  • All *SettingsView.xaml (DNSLookup, IPScanner, PortScanner, SNMP, SNTP)
  • ProfilesView.xaml, SettingsProfilesView.xaml
  • Child windows (PortProfilesChildWindow, SNMPOIDProfilesChildWindow, ServerConnectionInfoProfileChildWindow)

If during the audit one of these turns out to also start empty, apply the fix there too.

Per-view steps

For each view in scope:

  1. Identify the "main" column. Usually the rightmost, content-heavy column (e.g., Hostname, Status, Result, Description). For columns where text length varies a lot, this is the right star candidate. Avoid putting Width="*" on numeric/short fixed-shape columns (TTL, Port, Class).
  2. Set Width="*" on that column, keep MinWidth as is. Ensure all other columns keep MinWidth (no Width="*") so the horizontal scrollbar can do its job when the window narrows.
  3. Decide on TextWrapping case by case:
    • Apply TextBlock.TextWrapping="Wrap" via <DataGridTextColumn.ElementStyle> only if the column carries variable-length, prose-like content (descriptions, error messages, multi-value DNS results).
    • Skip wrapping for columns that should stay single-line for scannability (IP addresses, hostnames, statuses, timestamps).
  4. Wire the LoadingRow handler following the DNSLookupView template:
    • Add x:Name="..." to the DataGrid/MultiSelectDataGrid if it doesn't have one.
    • Add LoadingRow="<Name>_LoadingRow" to the XAML element.
    • Implement the handler in code-behind (copy the body from DNSLookupView.xaml.cs, change the field name). Make sure using System.Windows.Threading; is present.
    • Handler unsubscribes itself; no per-view bool guards needed.
  5. Test:
    • Open the view, run an action that produces results → main column should fill remaining width on the first row appearing, no window resize required.
    • Shrink the window below the sum of MinWidth values → horizontal scrollbar appears.
    • Close and re-open the view → behaviour is reproduced (handler re-attaches via XAML on the new instance).

Notes / gotchas

  • Don't switch HorizontalScrollBarVisibility to Disabled — it would hide the scrollbar entirely, contrary to the requirement.
  • The LoadingRow handler only fires when a row actually loads. If a view never has rows (still empty), the column stays at MinWidth, which is acceptable.
  • The handler resets all star columns on the grid, so it generalises if a view has more than one star column later.
  • If you find a view where the visible result is rendered with something other than a DataGrid/MultiSelectDataGrid (e.g., ItemsControl, ListView), the WPF bug doesn't apply — adjust column/element widths in the relevant template instead and skip the code-behind workaround.

Acceptance criteria

  • All in-scope result views fill the designated star column on first paint without a window resize.
  • Horizontal scrollbar still appears when the viewport is narrower than the sum of column MinWidth values.
  • For each view, a deliberate decision is made about TextWrapping (Wrap or no wrap), recorded in the PR description.
  • No regressions in keyboard navigation, sorting, grouping, copy/paste, context menus.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions