Repository navigation
Add Form.WindowStateChanged event - #156
Merged
Merged
Conversation
This was referenced Oct 3, 2026
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.
Summary
Applications can now observe confirmed normal, minimized and maximized Form state changes through
Form.WindowStateChangedand the protectedOnWindowStateChangedhook. Immutable, platform-neutralWindowStateChangedEventArgsexposesOldStateandNewStatewithout 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
IWindowImplremains the source of truth. Form retains only a notification baseline, not another independently writable window state.OldStateis the previous confirmed state reported by this form; handlers readform.WindowState == e.NewStateafter the new state has committed. Delayed payloads that no longer match the backend are ignored.Pre-show / hidden
Setting
form.WindowState = FormWindowState.Maximizedbefore 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 recognizedSW_MINIMIZE. The minimal mapping change recognizesSW_SHOWMINIMIZEDas Minimized and preserves the other cases. The regression checks native IsIconic state andform.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.
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.