Skip to content

Add Form.WindowStateChanged event - #156

Merged
ProGraMajster merged 1 commit into
masterfrom
codex/window-state-changed
Oct 3, 2026
Merged

ProGraMajster merged 1 commit into
masterfrom
codex/window-state-changed

Conversation

@ProGraMajster

Copy link
Copy Markdown
Owner

Summary

Applications can now observe confirmed normal, minimized and maximized Form state changes through Form.WindowStateChanged and the protected OnWindowStateChanged hook. Immutable, platform-neutral WindowStateChangedEventArgs exposes OldState and NewState without cancellation or native handles.

Programmatic requests and native/system actions use the existing backend callback and one public notification path. Identical assignments and repeated backend confirmations are deduplicated. XML documentation and a small application-lifecycle example describe the contract; Designer discovers the typed event through its existing reflection path.

State contract

request / native action
→ backend state
→ confirmed state commit
→ WindowStateChanged

IWindowImpl remains the source of truth. Form retains only a notification baseline, not another independently writable window state. OldState is the previous confirmed state reported by this form; handlers read form.WindowState == e.NewState after the new state has committed. Delayed payloads that no longer match the backend are ignored.

Pre-show / hidden

Setting form.WindowState = FormWindowState.Maximized before the first Show, or while an already shown form is hidden, preserves the existing requested-state getter behavior. It configures the next display and does not itself raise a state event. The backend confirms the applied visible state during Show. Re-show confirmation also covers HWNDs that do not receive another WM_SIZE; already reported states remain deduplicated.

For example, preconfiguring Maximized produces one Normal-to-Maximized notification when applied. Returning the hidden configuration to the last confirmed state produces none. Load, VisibleChanged and one-shot Shown retain their contracts.

Native bug fix

The real HWND regression reproduced a minimized window whose getter incorrectly returned Normal: WINDOWPLACEMENT reports SW_SHOWMINIMIZED, while the existing mapping only recognized SW_MINIMIZE. The minimal mapping change recognizes SW_SHOWMINIMIZED as Minimized and preserves the other cases. The regression checks native IsIconic state and form.WindowState == e.NewState == Minimized, with both custom and system decorations.

Reentrancy / safety

State changes from handlers supersede older operations and notifications. Hide/Close stop obsolete observers and prevent an old Show from continuing; cancellation during the initial modal Show restores the owner's input. The established Hide-versus-Close ownership contract is preserved.

Observer failures do not roll back committed state. Still-current observers run before exceptions propagate, multiple failures are aggregated, and a synchronous failed Show retires its display. The existing native callback exception policy is unchanged; observer exception propagation was exercised in headless tests, without deliberately throwing unhandled exceptions across a native WndProc boundary.

Relation to geometry

State notification is independent of Resize, SizeChanged, LocationChanged and ClientSizeChanged. In particular, minimize does not require a geometry event. Windows WM_SIZE normally delivers applicable resize/layout notifications first; this PR introduces no universal geometry/state ordering or atomic geometry/state/DPI transaction. Existing geometry units and contracts are preserved.

Scope

Part of #147

This PR implements only Form.WindowStateChanged and the necessary confirmation, lifecycle and minimized-getter corrections. It adds no DPI events, Activate(), ResizeBegin/ResizeEnd, fullscreen API, Android window-state parity, ownership API, or other deferred #147 functionality. Issue #147 remains open after this PR.

Tests

Local validation completed on 2026-10-03. Final review confirmed all 14 source/test/documentation files are unchanged from that validated snapshot; the full suite was not needlessly repeated.

  • Restore: PASS.
  • Full Debug build: PASS, 0 warnings / 0 errors.
  • Full Release build: PASS, 0 warnings / 0 errors.
  • New headless WindowState cases: 31/31 PASS.
  • New Designer cases: 2/2 PASS (discovery, signature, generation, compilation, round-trip, no user-code execution).
  • Native Windows HWND state regression: PASS (real minimize/maximize/restore, custom/system decorations, reentrancy and Hide/Close).
  • Full Debug suite, including existing geometry/lifecycle regressions: 3789/3789 PASS, 0 skipped.
  • git diff --check: PASS.

Builds used sequential MSBuild (-m:1 /p:UseSharedCompilation=false); the full Debug suite used --no-build. No manual Aero Snap/DPI or physical Android-device validation is claimed.

@ProGraMajster
ProGraMajster merged commit 4b0191d into master Oct 3, 2026
1 check passed
@ProGraMajster
ProGraMajster deleted the codex/window-state-changed branch October 3, 2026 13:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant