From e79fd826919b9f4260869b29dc9d577971992f8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaus=20L=C3=B6ffelmann?= Date: Tue, 19 May 2026 15:11:44 -0700 Subject: [PATCH 01/10] Move component code files to component folder. --- src/System.Windows.Forms.Primitives/src/NativeMethods.txt | 3 +++ .../Forms/{ => Components}/ErrorProvider/ErrorBlinkStyle.cs | 0 .../Forms/{ => Components}/ErrorProvider/ErrorIconAlignment.cs | 0 .../ErrorProvider.ControlItem.ControlItemAccessibleObject.cs | 0 .../ErrorProvider/ErrorProvider.ControlItem.cs | 0 .../ErrorProvider/ErrorProvider.ErrorProviderStates.cs | 0 .../ErrorProvider.ErrorWindow.ErrorWindowAccessibleObject.cs | 0 .../ErrorProvider/ErrorProvider.ErrorWindow.cs | 0 .../{ => Components}/ErrorProvider/ErrorProvider.IconRegion.cs | 0 .../Forms/{ => Components}/ErrorProvider/ErrorProvider.cs | 0 .../System/Windows/Forms/{Help => Components}/HelpProvider.cs | 0 .../Forms/{Controls => Components}/ImageList/ColorDepth.cs | 0 .../ImageList/ImageList.ImageCollection.ImageInfo.cs | 0 .../ImageList/ImageList.ImageCollection.cs | 0 .../{Controls => Components}/ImageList/ImageList.Indexer.cs | 0 .../ImageList/ImageList.NativeImageList.cs | 0 .../{Controls => Components}/ImageList/ImageList.Original.cs | 0 .../ImageList/ImageList.OriginalOptions.cs | 0 .../Forms/{Controls => Components}/ImageList/ImageList.cs | 0 .../{Controls => Components}/ImageList/ImageListConverter.cs | 0 .../{Controls => Components}/ImageList/ImageListStreamer.cs | 0 .../ImageList/RelatedImageListAttribute.cs | 0 .../System/Windows/Forms/{ => Components}/Timer.cs | 0 23 files changed, 3 insertions(+) rename src/System.Windows.Forms/System/Windows/Forms/{ => Components}/ErrorProvider/ErrorBlinkStyle.cs (100%) rename src/System.Windows.Forms/System/Windows/Forms/{ => Components}/ErrorProvider/ErrorIconAlignment.cs (100%) rename src/System.Windows.Forms/System/Windows/Forms/{ => Components}/ErrorProvider/ErrorProvider.ControlItem.ControlItemAccessibleObject.cs (100%) rename src/System.Windows.Forms/System/Windows/Forms/{ => Components}/ErrorProvider/ErrorProvider.ControlItem.cs (100%) rename src/System.Windows.Forms/System/Windows/Forms/{ => Components}/ErrorProvider/ErrorProvider.ErrorProviderStates.cs (100%) rename src/System.Windows.Forms/System/Windows/Forms/{ => Components}/ErrorProvider/ErrorProvider.ErrorWindow.ErrorWindowAccessibleObject.cs (100%) rename src/System.Windows.Forms/System/Windows/Forms/{ => Components}/ErrorProvider/ErrorProvider.ErrorWindow.cs (100%) rename src/System.Windows.Forms/System/Windows/Forms/{ => Components}/ErrorProvider/ErrorProvider.IconRegion.cs (100%) rename src/System.Windows.Forms/System/Windows/Forms/{ => Components}/ErrorProvider/ErrorProvider.cs (100%) rename src/System.Windows.Forms/System/Windows/Forms/{Help => Components}/HelpProvider.cs (100%) rename src/System.Windows.Forms/System/Windows/Forms/{Controls => Components}/ImageList/ColorDepth.cs (100%) rename src/System.Windows.Forms/System/Windows/Forms/{Controls => Components}/ImageList/ImageList.ImageCollection.ImageInfo.cs (100%) rename src/System.Windows.Forms/System/Windows/Forms/{Controls => Components}/ImageList/ImageList.ImageCollection.cs (100%) rename src/System.Windows.Forms/System/Windows/Forms/{Controls => Components}/ImageList/ImageList.Indexer.cs (100%) rename src/System.Windows.Forms/System/Windows/Forms/{Controls => Components}/ImageList/ImageList.NativeImageList.cs (100%) rename src/System.Windows.Forms/System/Windows/Forms/{Controls => Components}/ImageList/ImageList.Original.cs (100%) rename src/System.Windows.Forms/System/Windows/Forms/{Controls => Components}/ImageList/ImageList.OriginalOptions.cs (100%) rename src/System.Windows.Forms/System/Windows/Forms/{Controls => Components}/ImageList/ImageList.cs (100%) rename src/System.Windows.Forms/System/Windows/Forms/{Controls => Components}/ImageList/ImageListConverter.cs (100%) rename src/System.Windows.Forms/System/Windows/Forms/{Controls => Components}/ImageList/ImageListStreamer.cs (100%) rename src/System.Windows.Forms/System/Windows/Forms/{Controls => Components}/ImageList/RelatedImageListAttribute.cs (100%) rename src/System.Windows.Forms/System/Windows/Forms/{ => Components}/Timer.cs (100%) diff --git a/src/System.Windows.Forms.Primitives/src/NativeMethods.txt b/src/System.Windows.Forms.Primitives/src/NativeMethods.txt index b1a36cfacdd..9828b18cad8 100644 --- a/src/System.Windows.Forms.Primitives/src/NativeMethods.txt +++ b/src/System.Windows.Forms.Primitives/src/NativeMethods.txt @@ -517,6 +517,9 @@ PD_RESULT_* PostQuitMessage PostQuitMessage PostThreadMessage +PowerCreateRequest +PowerClearRequest +PowerSetRequest PRF_* PROGRESS_CLASS PROPERTYKEY diff --git a/src/System.Windows.Forms/System/Windows/Forms/ErrorProvider/ErrorBlinkStyle.cs b/src/System.Windows.Forms/System/Windows/Forms/Components/ErrorProvider/ErrorBlinkStyle.cs similarity index 100% rename from src/System.Windows.Forms/System/Windows/Forms/ErrorProvider/ErrorBlinkStyle.cs rename to src/System.Windows.Forms/System/Windows/Forms/Components/ErrorProvider/ErrorBlinkStyle.cs diff --git a/src/System.Windows.Forms/System/Windows/Forms/ErrorProvider/ErrorIconAlignment.cs b/src/System.Windows.Forms/System/Windows/Forms/Components/ErrorProvider/ErrorIconAlignment.cs similarity index 100% rename from src/System.Windows.Forms/System/Windows/Forms/ErrorProvider/ErrorIconAlignment.cs rename to src/System.Windows.Forms/System/Windows/Forms/Components/ErrorProvider/ErrorIconAlignment.cs diff --git a/src/System.Windows.Forms/System/Windows/Forms/ErrorProvider/ErrorProvider.ControlItem.ControlItemAccessibleObject.cs b/src/System.Windows.Forms/System/Windows/Forms/Components/ErrorProvider/ErrorProvider.ControlItem.ControlItemAccessibleObject.cs similarity index 100% rename from src/System.Windows.Forms/System/Windows/Forms/ErrorProvider/ErrorProvider.ControlItem.ControlItemAccessibleObject.cs rename to src/System.Windows.Forms/System/Windows/Forms/Components/ErrorProvider/ErrorProvider.ControlItem.ControlItemAccessibleObject.cs diff --git a/src/System.Windows.Forms/System/Windows/Forms/ErrorProvider/ErrorProvider.ControlItem.cs b/src/System.Windows.Forms/System/Windows/Forms/Components/ErrorProvider/ErrorProvider.ControlItem.cs similarity index 100% rename from src/System.Windows.Forms/System/Windows/Forms/ErrorProvider/ErrorProvider.ControlItem.cs rename to src/System.Windows.Forms/System/Windows/Forms/Components/ErrorProvider/ErrorProvider.ControlItem.cs diff --git a/src/System.Windows.Forms/System/Windows/Forms/ErrorProvider/ErrorProvider.ErrorProviderStates.cs b/src/System.Windows.Forms/System/Windows/Forms/Components/ErrorProvider/ErrorProvider.ErrorProviderStates.cs similarity index 100% rename from src/System.Windows.Forms/System/Windows/Forms/ErrorProvider/ErrorProvider.ErrorProviderStates.cs rename to src/System.Windows.Forms/System/Windows/Forms/Components/ErrorProvider/ErrorProvider.ErrorProviderStates.cs diff --git a/src/System.Windows.Forms/System/Windows/Forms/ErrorProvider/ErrorProvider.ErrorWindow.ErrorWindowAccessibleObject.cs b/src/System.Windows.Forms/System/Windows/Forms/Components/ErrorProvider/ErrorProvider.ErrorWindow.ErrorWindowAccessibleObject.cs similarity index 100% rename from src/System.Windows.Forms/System/Windows/Forms/ErrorProvider/ErrorProvider.ErrorWindow.ErrorWindowAccessibleObject.cs rename to src/System.Windows.Forms/System/Windows/Forms/Components/ErrorProvider/ErrorProvider.ErrorWindow.ErrorWindowAccessibleObject.cs diff --git a/src/System.Windows.Forms/System/Windows/Forms/ErrorProvider/ErrorProvider.ErrorWindow.cs b/src/System.Windows.Forms/System/Windows/Forms/Components/ErrorProvider/ErrorProvider.ErrorWindow.cs similarity index 100% rename from src/System.Windows.Forms/System/Windows/Forms/ErrorProvider/ErrorProvider.ErrorWindow.cs rename to src/System.Windows.Forms/System/Windows/Forms/Components/ErrorProvider/ErrorProvider.ErrorWindow.cs diff --git a/src/System.Windows.Forms/System/Windows/Forms/ErrorProvider/ErrorProvider.IconRegion.cs b/src/System.Windows.Forms/System/Windows/Forms/Components/ErrorProvider/ErrorProvider.IconRegion.cs similarity index 100% rename from src/System.Windows.Forms/System/Windows/Forms/ErrorProvider/ErrorProvider.IconRegion.cs rename to src/System.Windows.Forms/System/Windows/Forms/Components/ErrorProvider/ErrorProvider.IconRegion.cs diff --git a/src/System.Windows.Forms/System/Windows/Forms/ErrorProvider/ErrorProvider.cs b/src/System.Windows.Forms/System/Windows/Forms/Components/ErrorProvider/ErrorProvider.cs similarity index 100% rename from src/System.Windows.Forms/System/Windows/Forms/ErrorProvider/ErrorProvider.cs rename to src/System.Windows.Forms/System/Windows/Forms/Components/ErrorProvider/ErrorProvider.cs diff --git a/src/System.Windows.Forms/System/Windows/Forms/Help/HelpProvider.cs b/src/System.Windows.Forms/System/Windows/Forms/Components/HelpProvider.cs similarity index 100% rename from src/System.Windows.Forms/System/Windows/Forms/Help/HelpProvider.cs rename to src/System.Windows.Forms/System/Windows/Forms/Components/HelpProvider.cs diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/ImageList/ColorDepth.cs b/src/System.Windows.Forms/System/Windows/Forms/Components/ImageList/ColorDepth.cs similarity index 100% rename from src/System.Windows.Forms/System/Windows/Forms/Controls/ImageList/ColorDepth.cs rename to src/System.Windows.Forms/System/Windows/Forms/Components/ImageList/ColorDepth.cs diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/ImageList/ImageList.ImageCollection.ImageInfo.cs b/src/System.Windows.Forms/System/Windows/Forms/Components/ImageList/ImageList.ImageCollection.ImageInfo.cs similarity index 100% rename from src/System.Windows.Forms/System/Windows/Forms/Controls/ImageList/ImageList.ImageCollection.ImageInfo.cs rename to src/System.Windows.Forms/System/Windows/Forms/Components/ImageList/ImageList.ImageCollection.ImageInfo.cs diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/ImageList/ImageList.ImageCollection.cs b/src/System.Windows.Forms/System/Windows/Forms/Components/ImageList/ImageList.ImageCollection.cs similarity index 100% rename from src/System.Windows.Forms/System/Windows/Forms/Controls/ImageList/ImageList.ImageCollection.cs rename to src/System.Windows.Forms/System/Windows/Forms/Components/ImageList/ImageList.ImageCollection.cs diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/ImageList/ImageList.Indexer.cs b/src/System.Windows.Forms/System/Windows/Forms/Components/ImageList/ImageList.Indexer.cs similarity index 100% rename from src/System.Windows.Forms/System/Windows/Forms/Controls/ImageList/ImageList.Indexer.cs rename to src/System.Windows.Forms/System/Windows/Forms/Components/ImageList/ImageList.Indexer.cs diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/ImageList/ImageList.NativeImageList.cs b/src/System.Windows.Forms/System/Windows/Forms/Components/ImageList/ImageList.NativeImageList.cs similarity index 100% rename from src/System.Windows.Forms/System/Windows/Forms/Controls/ImageList/ImageList.NativeImageList.cs rename to src/System.Windows.Forms/System/Windows/Forms/Components/ImageList/ImageList.NativeImageList.cs diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/ImageList/ImageList.Original.cs b/src/System.Windows.Forms/System/Windows/Forms/Components/ImageList/ImageList.Original.cs similarity index 100% rename from src/System.Windows.Forms/System/Windows/Forms/Controls/ImageList/ImageList.Original.cs rename to src/System.Windows.Forms/System/Windows/Forms/Components/ImageList/ImageList.Original.cs diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/ImageList/ImageList.OriginalOptions.cs b/src/System.Windows.Forms/System/Windows/Forms/Components/ImageList/ImageList.OriginalOptions.cs similarity index 100% rename from src/System.Windows.Forms/System/Windows/Forms/Controls/ImageList/ImageList.OriginalOptions.cs rename to src/System.Windows.Forms/System/Windows/Forms/Components/ImageList/ImageList.OriginalOptions.cs diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/ImageList/ImageList.cs b/src/System.Windows.Forms/System/Windows/Forms/Components/ImageList/ImageList.cs similarity index 100% rename from src/System.Windows.Forms/System/Windows/Forms/Controls/ImageList/ImageList.cs rename to src/System.Windows.Forms/System/Windows/Forms/Components/ImageList/ImageList.cs diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/ImageList/ImageListConverter.cs b/src/System.Windows.Forms/System/Windows/Forms/Components/ImageList/ImageListConverter.cs similarity index 100% rename from src/System.Windows.Forms/System/Windows/Forms/Controls/ImageList/ImageListConverter.cs rename to src/System.Windows.Forms/System/Windows/Forms/Components/ImageList/ImageListConverter.cs diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/ImageList/ImageListStreamer.cs b/src/System.Windows.Forms/System/Windows/Forms/Components/ImageList/ImageListStreamer.cs similarity index 100% rename from src/System.Windows.Forms/System/Windows/Forms/Controls/ImageList/ImageListStreamer.cs rename to src/System.Windows.Forms/System/Windows/Forms/Components/ImageList/ImageListStreamer.cs diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/ImageList/RelatedImageListAttribute.cs b/src/System.Windows.Forms/System/Windows/Forms/Components/ImageList/RelatedImageListAttribute.cs similarity index 100% rename from src/System.Windows.Forms/System/Windows/Forms/Controls/ImageList/RelatedImageListAttribute.cs rename to src/System.Windows.Forms/System/Windows/Forms/Components/ImageList/RelatedImageListAttribute.cs diff --git a/src/System.Windows.Forms/System/Windows/Forms/Timer.cs b/src/System.Windows.Forms/System/Windows/Forms/Components/Timer.cs similarity index 100% rename from src/System.Windows.Forms/System/Windows/Forms/Timer.cs rename to src/System.Windows.Forms/System/Windows/Forms/Components/Timer.cs From 4ac93fc75de634bca350d533069830896c9bccee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaus=20L=C3=B6ffelmann?= Date: Tue, 19 May 2026 15:12:12 -0700 Subject: [PATCH 02/10] Introduce KioskModeManager component. * Refactor KioskModeManager component and introduce WakeUp-Events incl.. new WakeUpEventArgs. --- src/BuildAssist/BuildAssist.msbuildproj | 2 +- .../src/NativeMethods.txt | 2 + .../PublicAPI.Unshipped.txt | 41 + src/System.Windows.Forms/Resources/SR.resx | 41 +- .../Resources/xlf/SR.cs.xlf | 65 + .../Resources/xlf/SR.de.xlf | 65 + .../Resources/xlf/SR.es.xlf | 65 + .../Resources/xlf/SR.fr.xlf | 65 + .../Resources/xlf/SR.it.xlf | 65 + .../Resources/xlf/SR.ja.xlf | 65 + .../Resources/xlf/SR.ko.xlf | 65 + .../Resources/xlf/SR.pl.xlf | 65 + .../Resources/xlf/SR.pt-BR.xlf | 65 + .../Resources/xlf/SR.ru.xlf | 65 + .../Resources/xlf/SR.tr.xlf | 65 + .../Resources/xlf/SR.zh-Hans.xlf | 65 + .../Resources/xlf/SR.zh-Hant.xlf | 65 + .../KioskModeManager/KioskModeManager.cs | 1145 +++++++++++++++++ .../KioskModeWakeupEventArgs.cs | 36 + .../KioskModeWakeupEventHandler.cs | 14 + .../KioskModeManager/KioskModeWakeupSource.cs | 35 + .../Windows/Forms/KioskModeManagerTests.cs | 423 ++++++ 22 files changed, 2582 insertions(+), 2 deletions(-) create mode 100644 src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeManager.cs create mode 100644 src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeWakeupEventArgs.cs create mode 100644 src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeWakeupEventHandler.cs create mode 100644 src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeWakeupSource.cs create mode 100644 src/test/unit/System.Windows.Forms/System/Windows/Forms/KioskModeManagerTests.cs diff --git a/src/BuildAssist/BuildAssist.msbuildproj b/src/BuildAssist/BuildAssist.msbuildproj index 2e188f76ff6..400c78f44e6 100644 --- a/src/BuildAssist/BuildAssist.msbuildproj +++ b/src/BuildAssist/BuildAssist.msbuildproj @@ -37,7 +37,7 @@ void virtual System.Windows.Forms.Control.VisualStylesMode.get -> System.Windows.Forms.VisualStylesMode virtual System.Windows.Forms.Control.VisualStylesMode.set -> void +System.Windows.Forms.KioskModeManager +System.Windows.Forms.KioskModeManager.ContainerControl.get -> System.Windows.Forms.ContainerControl? +System.Windows.Forms.KioskModeManager.ContainerControl.set -> void +System.Windows.Forms.KioskModeManager.ContainerControlChanged -> System.EventHandler? +override System.Windows.Forms.KioskModeManager.Dispose(bool disposing) -> void +System.Windows.Forms.KioskModeManager.EscapeExitsFullScreen.get -> bool +System.Windows.Forms.KioskModeManager.EscapeExitsFullScreen.set -> void +System.Windows.Forms.KioskModeManager.FullScreen.get -> bool +System.Windows.Forms.KioskModeManager.FullScreen.set -> void +System.Windows.Forms.KioskModeManager.FullScreenChanged -> System.EventHandler? +System.Windows.Forms.KioskModeManager.HideTaskbar.get -> bool +System.Windows.Forms.KioskModeManager.HideTaskbar.set -> void +System.Windows.Forms.KioskModeManager.KioskModeManager() -> void +System.Windows.Forms.KioskModeManager.KioskModeManager(System.ComponentModel.IContainer! container) -> void +System.Windows.Forms.KioskModeManager.MousePointerAutoHideDelay.get -> int +System.Windows.Forms.KioskModeManager.MousePointerAutoHideDelay.set -> void +virtual System.Windows.Forms.KioskModeManager.OnContainerControlChanged(System.EventArgs! e) -> void +virtual System.Windows.Forms.KioskModeManager.OnFullScreenChanged(System.EventArgs! e) -> void +virtual System.Windows.Forms.KioskModeManager.OnWakeup(System.Windows.Forms.KioskModeWakeupEventArgs! e) -> void +override System.Windows.Forms.KioskModeManager.Site.get -> System.ComponentModel.ISite? +override System.Windows.Forms.KioskModeManager.Site.set -> void +System.Windows.Forms.KioskModeManager.SuppressPowerSaving.get -> bool +System.Windows.Forms.KioskModeManager.SuppressPowerSaving.set -> void +System.Windows.Forms.KioskModeManager.ToggleFullScreen() -> void +System.Windows.Forms.KioskModeManager.ToggleFullScreenKey.get -> System.Windows.Forms.Keys +System.Windows.Forms.KioskModeManager.ToggleFullScreenKey.set -> void +System.Windows.Forms.KioskModeManager.TopMostInFullScreen.get -> bool +System.Windows.Forms.KioskModeManager.TopMostInFullScreen.set -> void +System.Windows.Forms.KioskModeManager.Wakeup -> System.Windows.Forms.KioskModeWakeupEventHandler? +System.Windows.Forms.KioskModeManager.WakeUpCommand.get -> System.Windows.Input.ICommand? +System.Windows.Forms.KioskModeManager.WakeUpCommand.set -> void +System.Windows.Forms.KioskModeWakeupEventArgs +System.Windows.Forms.KioskModeWakeupEventArgs.KioskModeWakeupEventArgs(System.Windows.Forms.KioskModeWakeupSource source) -> void +System.Windows.Forms.KioskModeWakeupEventArgs.Source.get -> System.Windows.Forms.KioskModeWakeupSource +System.Windows.Forms.KioskModeWakeupEventHandler +virtual System.Windows.Forms.KioskModeWakeupEventHandler.Invoke(object? sender, System.Windows.Forms.KioskModeWakeupEventArgs! e) -> void +System.Windows.Forms.KioskModeWakeupSource +System.Windows.Forms.KioskModeWakeupSource.Keyboard = 0 -> System.Windows.Forms.KioskModeWakeupSource +System.Windows.Forms.KioskModeWakeupSource.Mouse = 1 -> System.Windows.Forms.KioskModeWakeupSource +System.Windows.Forms.KioskModeWakeupSource.PowerResume = 2 -> System.Windows.Forms.KioskModeWakeupSource +System.Windows.Forms.KioskModeWakeupSource.Session = 3 -> System.Windows.Forms.KioskModeWakeupSource diff --git a/src/System.Windows.Forms/Resources/SR.resx b/src/System.Windows.Forms/Resources/SR.resx index d3a89671107..651f8585703 100644 --- a/src/System.Windows.Forms/Resources/SR.resx +++ b/src/System.Windows.Forms/Resources/SR.resx @@ -2691,6 +2691,45 @@ To replace this default dialog please handle the DataError event. Manages a collection of images that are typically used by other controls such as ListView, TreeView, or ToolStrip. + + Manages common fullscreen, input, cursor, and power behavior for kiosk-style WinForms applications. + + + The container whose containing form is controlled by the kiosk mode manager. + + + Occurs when the ContainerControl property value changes. + + + Indicates whether pressing Escape exits fullscreen mode. + + + Gets or sets a value indicating whether the resolved form is currently fullscreen. + + + Occurs when the fullscreen state changes. + + + Indicates whether fullscreen mode covers the Windows taskbar. + + + The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. + + + Indicates whether Windows should keep the display and system awake while this component is active. + + + The key that toggles between fullscreen and restored mode. + + + Indicates whether the form is topmost while fullscreen. + + + Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. + + + The command that is executed whenever the kiosk experience is woken. + Provides run-time information or descriptive text for a control. @@ -7075,4 +7114,4 @@ Stack trace where the illegal operation occurred was: The FormScreenCaptureMode property can only be changed on top-level Forms with their TopLevel property set to true. - \ No newline at end of file + diff --git a/src/System.Windows.Forms/Resources/xlf/SR.cs.xlf b/src/System.Windows.Forms/Resources/xlf/SR.cs.xlf index 3516b3584de..fb383e46989 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.cs.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.cs.xlf @@ -4549,6 +4549,11 @@ Chcete-li nahradit toto výchozí dialogové okno, nastavte popisovač události Spravuje kolekci obrázků, které standardně používají ovládací prvky, jako například ListView, TreeView nebo ToolStrip. + + Manages common fullscreen, input, cursor, and power behavior for kiosk-style WinForms applications. + Manages common fullscreen, input, cursor, and power behavior for kiosk-style WinForms applications. + + Provides run-time information or descriptive text for a control. Poskytuje běhové informace nebo popisný text pro ovládací prvek. @@ -6144,6 +6149,66 @@ Trasování zásobníku, kde došlo k neplatné operaci: Kombinace klíčů je neplatná. + + Occurs when the ContainerControl property value changes. + Occurs when the ContainerControl property value changes. + + + + The container whose containing form is controlled by the kiosk mode manager. + The container whose containing form is controlled by the kiosk mode manager. + + + + Indicates whether pressing Escape exits fullscreen mode. + Indicates whether pressing Escape exits fullscreen mode. + + + + Gets or sets a value indicating whether the resolved form is currently fullscreen. + Gets or sets a value indicating whether the resolved form is currently fullscreen. + + + + Occurs when the fullscreen state changes. + Occurs when the fullscreen state changes. + + + + Indicates whether fullscreen mode covers the Windows taskbar. + Indicates whether fullscreen mode covers the Windows taskbar. + + + + The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. + The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. + + + + Indicates whether Windows should keep the display and system awake while this component is active. + Indicates whether Windows should keep the display and system awake while this component is active. + + + + The key that toggles between fullscreen and restored mode. + The key that toggles between fullscreen and restored mode. + + + + Indicates whether the form is topmost while fullscreen. + Indicates whether the form is topmost while fullscreen. + + + + Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. + Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. + + + + The command that is executed whenever the kiosk experience is woken. + The command that is executed whenever the kiosk experience is woken. + + Enables the automatic handling of text that extends beyond the width of the label control. Umožňuje automatické zpracování textu, který je delší než šířka ovládacího prvku popisku. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.de.xlf b/src/System.Windows.Forms/Resources/xlf/SR.de.xlf index 1b1a7896269..a50f783a344 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.de.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.de.xlf @@ -4549,6 +4549,11 @@ Behandeln Sie das DataError-Ereignis, um dieses Standarddialogfeld zu ersetzen.< Verwaltet eine Sammlung von Bildern, die in der Regel von anderen Steuerelementen wie ListView, TreeView oder ToolStrip verwendet werden. + + Manages common fullscreen, input, cursor, and power behavior for kiosk-style WinForms applications. + Manages common fullscreen, input, cursor, and power behavior for kiosk-style WinForms applications. + + Provides run-time information or descriptive text for a control. Stellt Laufzeitinformationen oder deskriptiven Text für ein Steuerelement bereit. @@ -6144,6 +6149,66 @@ Stapelüberwachung, in der der unzulässige Vorgang auftrat: Ungültige Schlüsselkombination. + + Occurs when the ContainerControl property value changes. + Occurs when the ContainerControl property value changes. + + + + The container whose containing form is controlled by the kiosk mode manager. + The container whose containing form is controlled by the kiosk mode manager. + + + + Indicates whether pressing Escape exits fullscreen mode. + Indicates whether pressing Escape exits fullscreen mode. + + + + Gets or sets a value indicating whether the resolved form is currently fullscreen. + Gets or sets a value indicating whether the resolved form is currently fullscreen. + + + + Occurs when the fullscreen state changes. + Occurs when the fullscreen state changes. + + + + Indicates whether fullscreen mode covers the Windows taskbar. + Indicates whether fullscreen mode covers the Windows taskbar. + + + + The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. + The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. + + + + Indicates whether Windows should keep the display and system awake while this component is active. + Indicates whether Windows should keep the display and system awake while this component is active. + + + + The key that toggles between fullscreen and restored mode. + The key that toggles between fullscreen and restored mode. + + + + Indicates whether the form is topmost while fullscreen. + Indicates whether the form is topmost while fullscreen. + + + + Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. + Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. + + + + The command that is executed whenever the kiosk experience is woken. + The command that is executed whenever the kiosk experience is woken. + + Enables the automatic handling of text that extends beyond the width of the label control. Aktiviert die automatische Behandlung von Text, der über die Breite des Bezeichnungssteuerelements hinausgeht. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.es.xlf b/src/System.Windows.Forms/Resources/xlf/SR.es.xlf index 650b5dc8f1c..9ac01c89344 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.es.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.es.xlf @@ -4549,6 +4549,11 @@ Para reemplazar este cuadro de diálogo predeterminado controle el evento DataEr Controla una colección de imágenes que suelen utilizar otros controles como ListView, TreeView o ToolStrip. + + Manages common fullscreen, input, cursor, and power behavior for kiosk-style WinForms applications. + Manages common fullscreen, input, cursor, and power behavior for kiosk-style WinForms applications. + + Provides run-time information or descriptive text for a control. Proporciona información en tiempo de ejecución o texto descriptivo para un control. @@ -6144,6 +6149,66 @@ El seguimiento de la pila donde tuvo lugar la operación no válida fue: La combinación de teclas no es válida. + + Occurs when the ContainerControl property value changes. + Occurs when the ContainerControl property value changes. + + + + The container whose containing form is controlled by the kiosk mode manager. + The container whose containing form is controlled by the kiosk mode manager. + + + + Indicates whether pressing Escape exits fullscreen mode. + Indicates whether pressing Escape exits fullscreen mode. + + + + Gets or sets a value indicating whether the resolved form is currently fullscreen. + Gets or sets a value indicating whether the resolved form is currently fullscreen. + + + + Occurs when the fullscreen state changes. + Occurs when the fullscreen state changes. + + + + Indicates whether fullscreen mode covers the Windows taskbar. + Indicates whether fullscreen mode covers the Windows taskbar. + + + + The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. + The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. + + + + Indicates whether Windows should keep the display and system awake while this component is active. + Indicates whether Windows should keep the display and system awake while this component is active. + + + + The key that toggles between fullscreen and restored mode. + The key that toggles between fullscreen and restored mode. + + + + Indicates whether the form is topmost while fullscreen. + Indicates whether the form is topmost while fullscreen. + + + + Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. + Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. + + + + The command that is executed whenever the kiosk experience is woken. + The command that is executed whenever the kiosk experience is woken. + + Enables the automatic handling of text that extends beyond the width of the label control. Permite el control automático del texto que se extiende más allá del ancho del control de la etiqueta. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.fr.xlf b/src/System.Windows.Forms/Resources/xlf/SR.fr.xlf index 762393d64b1..7b8995760d4 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.fr.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.fr.xlf @@ -4549,6 +4549,11 @@ Pour remplacer cette boîte de dialogue par défaut, traitez l'événement DataE Gère une collection d'images qui sont généralement utilisées par d'autres contrôles tels que ListView, TreeView et ToolStrip. + + Manages common fullscreen, input, cursor, and power behavior for kiosk-style WinForms applications. + Manages common fullscreen, input, cursor, and power behavior for kiosk-style WinForms applications. + + Provides run-time information or descriptive text for a control. Fournit des informations d'exécution ou un texte descriptif pour un contrôle. @@ -6144,6 +6149,66 @@ Cette opération non conforme s'est produite sur la trace de la pile : La combinaison de touches n'est pas valide. + + Occurs when the ContainerControl property value changes. + Occurs when the ContainerControl property value changes. + + + + The container whose containing form is controlled by the kiosk mode manager. + The container whose containing form is controlled by the kiosk mode manager. + + + + Indicates whether pressing Escape exits fullscreen mode. + Indicates whether pressing Escape exits fullscreen mode. + + + + Gets or sets a value indicating whether the resolved form is currently fullscreen. + Gets or sets a value indicating whether the resolved form is currently fullscreen. + + + + Occurs when the fullscreen state changes. + Occurs when the fullscreen state changes. + + + + Indicates whether fullscreen mode covers the Windows taskbar. + Indicates whether fullscreen mode covers the Windows taskbar. + + + + The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. + The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. + + + + Indicates whether Windows should keep the display and system awake while this component is active. + Indicates whether Windows should keep the display and system awake while this component is active. + + + + The key that toggles between fullscreen and restored mode. + The key that toggles between fullscreen and restored mode. + + + + Indicates whether the form is topmost while fullscreen. + Indicates whether the form is topmost while fullscreen. + + + + Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. + Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. + + + + The command that is executed whenever the kiosk experience is woken. + The command that is executed whenever the kiosk experience is woken. + + Enables the automatic handling of text that extends beyond the width of the label control. Active la gestion automatique du texte qui dépasse les limites de largeur du contrôle label. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.it.xlf b/src/System.Windows.Forms/Resources/xlf/SR.it.xlf index 3c34571e6df..65721d5111d 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.it.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.it.xlf @@ -4549,6 +4549,11 @@ Per sostituire questa finestra di dialogo, gestisci l'evento DataError. Gestisce una raccolta di immagini utilizzate in genere da altri controlli, quali ListView, TreeView o ToolStrip. + + Manages common fullscreen, input, cursor, and power behavior for kiosk-style WinForms applications. + Manages common fullscreen, input, cursor, and power behavior for kiosk-style WinForms applications. + + Provides run-time information or descriptive text for a control. Fornisce informazioni di runtime o testo descrittivo per un controllo. @@ -6144,6 +6149,66 @@ Analisi dello stack dove si è verificata l'operazione non valida: Combinazione di tasti non valida. + + Occurs when the ContainerControl property value changes. + Occurs when the ContainerControl property value changes. + + + + The container whose containing form is controlled by the kiosk mode manager. + The container whose containing form is controlled by the kiosk mode manager. + + + + Indicates whether pressing Escape exits fullscreen mode. + Indicates whether pressing Escape exits fullscreen mode. + + + + Gets or sets a value indicating whether the resolved form is currently fullscreen. + Gets or sets a value indicating whether the resolved form is currently fullscreen. + + + + Occurs when the fullscreen state changes. + Occurs when the fullscreen state changes. + + + + Indicates whether fullscreen mode covers the Windows taskbar. + Indicates whether fullscreen mode covers the Windows taskbar. + + + + The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. + The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. + + + + Indicates whether Windows should keep the display and system awake while this component is active. + Indicates whether Windows should keep the display and system awake while this component is active. + + + + The key that toggles between fullscreen and restored mode. + The key that toggles between fullscreen and restored mode. + + + + Indicates whether the form is topmost while fullscreen. + Indicates whether the form is topmost while fullscreen. + + + + Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. + Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. + + + + The command that is executed whenever the kiosk experience is woken. + The command that is executed whenever the kiosk experience is woken. + + Enables the automatic handling of text that extends beyond the width of the label control. Consente la gestione automatica del testo che non è contenuto all'interno della larghezza del controllo etichetta. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.ja.xlf b/src/System.Windows.Forms/Resources/xlf/SR.ja.xlf index 05f5f03a962..d8f67155273 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.ja.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.ja.xlf @@ -4549,6 +4549,11 @@ To replace this default dialog please handle the DataError event. ListView、TreeView、または ToolStrip のような他のコントロールが通常使用するイメージのコレクションを管理します。 + + Manages common fullscreen, input, cursor, and power behavior for kiosk-style WinForms applications. + Manages common fullscreen, input, cursor, and power behavior for kiosk-style WinForms applications. + + Provides run-time information or descriptive text for a control. コントロールの実行時の情報または説明用のテキストを提供します。 @@ -6144,6 +6149,66 @@ Stack trace where the illegal operation occurred was: キーの組み合わせが有効ではありません。 + + Occurs when the ContainerControl property value changes. + Occurs when the ContainerControl property value changes. + + + + The container whose containing form is controlled by the kiosk mode manager. + The container whose containing form is controlled by the kiosk mode manager. + + + + Indicates whether pressing Escape exits fullscreen mode. + Indicates whether pressing Escape exits fullscreen mode. + + + + Gets or sets a value indicating whether the resolved form is currently fullscreen. + Gets or sets a value indicating whether the resolved form is currently fullscreen. + + + + Occurs when the fullscreen state changes. + Occurs when the fullscreen state changes. + + + + Indicates whether fullscreen mode covers the Windows taskbar. + Indicates whether fullscreen mode covers the Windows taskbar. + + + + The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. + The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. + + + + Indicates whether Windows should keep the display and system awake while this component is active. + Indicates whether Windows should keep the display and system awake while this component is active. + + + + The key that toggles between fullscreen and restored mode. + The key that toggles between fullscreen and restored mode. + + + + Indicates whether the form is topmost while fullscreen. + Indicates whether the form is topmost while fullscreen. + + + + Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. + Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. + + + + The command that is executed whenever the kiosk experience is woken. + The command that is executed whenever the kiosk experience is woken. + + Enables the automatic handling of text that extends beyond the width of the label control. ラベル コントロールの幅を越えるテキストの自動処理を有効にします。 diff --git a/src/System.Windows.Forms/Resources/xlf/SR.ko.xlf b/src/System.Windows.Forms/Resources/xlf/SR.ko.xlf index 06b9e480dcb..e4209c74262 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.ko.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.ko.xlf @@ -4549,6 +4549,11 @@ To replace this default dialog please handle the DataError event. ListView, TreeView 또는 ToolStrip과 같은 다른 컨트롤에서 일반적으로 사용하는 이미지 컬렉션을 관리합니다. + + Manages common fullscreen, input, cursor, and power behavior for kiosk-style WinForms applications. + Manages common fullscreen, input, cursor, and power behavior for kiosk-style WinForms applications. + + Provides run-time information or descriptive text for a control. 컨트롤에 대한 설명 텍스트나 런타임 정보를 제공합니다. @@ -6144,6 +6149,66 @@ Stack trace where the illegal operation occurred was: 키 조합이 잘못되었습니다. + + Occurs when the ContainerControl property value changes. + Occurs when the ContainerControl property value changes. + + + + The container whose containing form is controlled by the kiosk mode manager. + The container whose containing form is controlled by the kiosk mode manager. + + + + Indicates whether pressing Escape exits fullscreen mode. + Indicates whether pressing Escape exits fullscreen mode. + + + + Gets or sets a value indicating whether the resolved form is currently fullscreen. + Gets or sets a value indicating whether the resolved form is currently fullscreen. + + + + Occurs when the fullscreen state changes. + Occurs when the fullscreen state changes. + + + + Indicates whether fullscreen mode covers the Windows taskbar. + Indicates whether fullscreen mode covers the Windows taskbar. + + + + The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. + The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. + + + + Indicates whether Windows should keep the display and system awake while this component is active. + Indicates whether Windows should keep the display and system awake while this component is active. + + + + The key that toggles between fullscreen and restored mode. + The key that toggles between fullscreen and restored mode. + + + + Indicates whether the form is topmost while fullscreen. + Indicates whether the form is topmost while fullscreen. + + + + Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. + Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. + + + + The command that is executed whenever the kiosk experience is woken. + The command that is executed whenever the kiosk experience is woken. + + Enables the automatic handling of text that extends beyond the width of the label control. 레이블 컨트롤의 너비보다 큰 텍스트를 자동으로 처리합니다. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.pl.xlf b/src/System.Windows.Forms/Resources/xlf/SR.pl.xlf index 5f0edef73e8..aa90fb4b0c6 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.pl.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.pl.xlf @@ -4549,6 +4549,11 @@ Aby zamienić to domyślne okno dialogowe, obsłuż zdarzenie DataError.Zarządza kolekcją obrazów, które są zazwyczaj używane przez inne formanty, takie jak ListView, TreeView lub ToolStrip. + + Manages common fullscreen, input, cursor, and power behavior for kiosk-style WinForms applications. + Manages common fullscreen, input, cursor, and power behavior for kiosk-style WinForms applications. + + Provides run-time information or descriptive text for a control. Wyświetla informacje o wykonaniu lub tekst opisowy dla formantu. @@ -6144,6 +6149,66 @@ Stos śledzenia, w którym wystąpiła zabroniona operacja: Kombinacja kluczy jest nieprawidłowa. + + Occurs when the ContainerControl property value changes. + Occurs when the ContainerControl property value changes. + + + + The container whose containing form is controlled by the kiosk mode manager. + The container whose containing form is controlled by the kiosk mode manager. + + + + Indicates whether pressing Escape exits fullscreen mode. + Indicates whether pressing Escape exits fullscreen mode. + + + + Gets or sets a value indicating whether the resolved form is currently fullscreen. + Gets or sets a value indicating whether the resolved form is currently fullscreen. + + + + Occurs when the fullscreen state changes. + Occurs when the fullscreen state changes. + + + + Indicates whether fullscreen mode covers the Windows taskbar. + Indicates whether fullscreen mode covers the Windows taskbar. + + + + The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. + The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. + + + + Indicates whether Windows should keep the display and system awake while this component is active. + Indicates whether Windows should keep the display and system awake while this component is active. + + + + The key that toggles between fullscreen and restored mode. + The key that toggles between fullscreen and restored mode. + + + + Indicates whether the form is topmost while fullscreen. + Indicates whether the form is topmost while fullscreen. + + + + Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. + Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. + + + + The command that is executed whenever the kiosk experience is woken. + The command that is executed whenever the kiosk experience is woken. + + Enables the automatic handling of text that extends beyond the width of the label control. Włącza automatyczną obsługę tekstu, który mieści się w szerokości formantu etykiety. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.pt-BR.xlf b/src/System.Windows.Forms/Resources/xlf/SR.pt-BR.xlf index 94b1dd779a0..1b9639fc1b6 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.pt-BR.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.pt-BR.xlf @@ -4549,6 +4549,11 @@ Para substituir a caixa de diálogo padrão, manipule o evento DataError.Gerencia uma coleção de imagens geralmente usadas por outros controles, como ListView, TreeView ou ToolStrip. + + Manages common fullscreen, input, cursor, and power behavior for kiosk-style WinForms applications. + Manages common fullscreen, input, cursor, and power behavior for kiosk-style WinForms applications. + + Provides run-time information or descriptive text for a control. Fornece informações em tempo de execução ou texto descritivo para um controle. @@ -6144,6 +6149,66 @@ O rastreamento de pilha em que a operação ilegal ocorreu foi: A combinação de chave não é válida. + + Occurs when the ContainerControl property value changes. + Occurs when the ContainerControl property value changes. + + + + The container whose containing form is controlled by the kiosk mode manager. + The container whose containing form is controlled by the kiosk mode manager. + + + + Indicates whether pressing Escape exits fullscreen mode. + Indicates whether pressing Escape exits fullscreen mode. + + + + Gets or sets a value indicating whether the resolved form is currently fullscreen. + Gets or sets a value indicating whether the resolved form is currently fullscreen. + + + + Occurs when the fullscreen state changes. + Occurs when the fullscreen state changes. + + + + Indicates whether fullscreen mode covers the Windows taskbar. + Indicates whether fullscreen mode covers the Windows taskbar. + + + + The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. + The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. + + + + Indicates whether Windows should keep the display and system awake while this component is active. + Indicates whether Windows should keep the display and system awake while this component is active. + + + + The key that toggles between fullscreen and restored mode. + The key that toggles between fullscreen and restored mode. + + + + Indicates whether the form is topmost while fullscreen. + Indicates whether the form is topmost while fullscreen. + + + + Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. + Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. + + + + The command that is executed whenever the kiosk experience is woken. + The command that is executed whenever the kiosk experience is woken. + + Enables the automatic handling of text that extends beyond the width of the label control. Permite a manipulação automática de texto que ultrapassa a largura do controle de rótulo. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.ru.xlf b/src/System.Windows.Forms/Resources/xlf/SR.ru.xlf index 31f2fc77cf5..47b1e48e15c 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.ru.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.ru.xlf @@ -4549,6 +4549,11 @@ To replace this default dialog please handle the DataError event. Управляет коллекцией изображений, которые обычно используются другими элементами управления (например, ListView, TreeView или ToolStrip). + + Manages common fullscreen, input, cursor, and power behavior for kiosk-style WinForms applications. + Manages common fullscreen, input, cursor, and power behavior for kiosk-style WinForms applications. + + Provides run-time information or descriptive text for a control. Предоставляет элементу управления текст описания либо информацию во время выполнения. @@ -6144,6 +6149,66 @@ Stack trace where the illegal operation occurred was: Недопустимая комбинация ключа. + + Occurs when the ContainerControl property value changes. + Occurs when the ContainerControl property value changes. + + + + The container whose containing form is controlled by the kiosk mode manager. + The container whose containing form is controlled by the kiosk mode manager. + + + + Indicates whether pressing Escape exits fullscreen mode. + Indicates whether pressing Escape exits fullscreen mode. + + + + Gets or sets a value indicating whether the resolved form is currently fullscreen. + Gets or sets a value indicating whether the resolved form is currently fullscreen. + + + + Occurs when the fullscreen state changes. + Occurs when the fullscreen state changes. + + + + Indicates whether fullscreen mode covers the Windows taskbar. + Indicates whether fullscreen mode covers the Windows taskbar. + + + + The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. + The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. + + + + Indicates whether Windows should keep the display and system awake while this component is active. + Indicates whether Windows should keep the display and system awake while this component is active. + + + + The key that toggles between fullscreen and restored mode. + The key that toggles between fullscreen and restored mode. + + + + Indicates whether the form is topmost while fullscreen. + Indicates whether the form is topmost while fullscreen. + + + + Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. + Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. + + + + The command that is executed whenever the kiosk experience is woken. + The command that is executed whenever the kiosk experience is woken. + + Enables the automatic handling of text that extends beyond the width of the label control. Включает автоматическую обработку текста, выходящего за пределы ширины элемента управления с подписью. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.tr.xlf b/src/System.Windows.Forms/Resources/xlf/SR.tr.xlf index 2388df399e0..44d944c7eac 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.tr.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.tr.xlf @@ -4549,6 +4549,11 @@ Bu varsayılan iletişim kutusunu değiştirmek için, lütfen DataError olayın Genellikle ListView, TreeView veya ToolStrip gibi diğer denetimler tarafından kullanılan resim koleksiyonunu düzenler. + + Manages common fullscreen, input, cursor, and power behavior for kiosk-style WinForms applications. + Manages common fullscreen, input, cursor, and power behavior for kiosk-style WinForms applications. + + Provides run-time information or descriptive text for a control. Bir denetim için çalışma zamanı bilgileri veya açıklayıcı metin sağlar. @@ -6144,6 +6149,66 @@ Geçersiz işlemin gerçekleştiği yığın izi: Anahtar birleşimi geçerli değil. + + Occurs when the ContainerControl property value changes. + Occurs when the ContainerControl property value changes. + + + + The container whose containing form is controlled by the kiosk mode manager. + The container whose containing form is controlled by the kiosk mode manager. + + + + Indicates whether pressing Escape exits fullscreen mode. + Indicates whether pressing Escape exits fullscreen mode. + + + + Gets or sets a value indicating whether the resolved form is currently fullscreen. + Gets or sets a value indicating whether the resolved form is currently fullscreen. + + + + Occurs when the fullscreen state changes. + Occurs when the fullscreen state changes. + + + + Indicates whether fullscreen mode covers the Windows taskbar. + Indicates whether fullscreen mode covers the Windows taskbar. + + + + The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. + The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. + + + + Indicates whether Windows should keep the display and system awake while this component is active. + Indicates whether Windows should keep the display and system awake while this component is active. + + + + The key that toggles between fullscreen and restored mode. + The key that toggles between fullscreen and restored mode. + + + + Indicates whether the form is topmost while fullscreen. + Indicates whether the form is topmost while fullscreen. + + + + Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. + Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. + + + + The command that is executed whenever the kiosk experience is woken. + The command that is executed whenever the kiosk experience is woken. + + Enables the automatic handling of text that extends beyond the width of the label control. Etiket denetimi genişliğini aşan metnin otomatik işlenmesini sağlar. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.zh-Hans.xlf b/src/System.Windows.Forms/Resources/xlf/SR.zh-Hans.xlf index 3f81edc8ec1..773978a1eb4 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.zh-Hans.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.zh-Hans.xlf @@ -4549,6 +4549,11 @@ To replace this default dialog please handle the DataError event. 管理通常由其他控件(如 ListView、TreeView 或 ToolStrip)使用的图像集合。 + + Manages common fullscreen, input, cursor, and power behavior for kiosk-style WinForms applications. + Manages common fullscreen, input, cursor, and power behavior for kiosk-style WinForms applications. + + Provides run-time information or descriptive text for a control. 为控件提供运行时信息或说明性文字。 @@ -6144,6 +6149,66 @@ Stack trace where the illegal operation occurred was: 组合键无效。 + + Occurs when the ContainerControl property value changes. + Occurs when the ContainerControl property value changes. + + + + The container whose containing form is controlled by the kiosk mode manager. + The container whose containing form is controlled by the kiosk mode manager. + + + + Indicates whether pressing Escape exits fullscreen mode. + Indicates whether pressing Escape exits fullscreen mode. + + + + Gets or sets a value indicating whether the resolved form is currently fullscreen. + Gets or sets a value indicating whether the resolved form is currently fullscreen. + + + + Occurs when the fullscreen state changes. + Occurs when the fullscreen state changes. + + + + Indicates whether fullscreen mode covers the Windows taskbar. + Indicates whether fullscreen mode covers the Windows taskbar. + + + + The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. + The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. + + + + Indicates whether Windows should keep the display and system awake while this component is active. + Indicates whether Windows should keep the display and system awake while this component is active. + + + + The key that toggles between fullscreen and restored mode. + The key that toggles between fullscreen and restored mode. + + + + Indicates whether the form is topmost while fullscreen. + Indicates whether the form is topmost while fullscreen. + + + + Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. + Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. + + + + The command that is executed whenever the kiosk experience is woken. + The command that is executed whenever the kiosk experience is woken. + + Enables the automatic handling of text that extends beyond the width of the label control. 启用对扩展到标签控件宽度以外的文本的自动处理。 diff --git a/src/System.Windows.Forms/Resources/xlf/SR.zh-Hant.xlf b/src/System.Windows.Forms/Resources/xlf/SR.zh-Hant.xlf index 72b32f99c14..6d1fb1b4638 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.zh-Hant.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.zh-Hant.xlf @@ -4549,6 +4549,11 @@ To replace this default dialog please handle the DataError event. 管理通常由其他控制項 (例如 ListView、TreeView 或 ToolStrip) 使用的影像集合。 + + Manages common fullscreen, input, cursor, and power behavior for kiosk-style WinForms applications. + Manages common fullscreen, input, cursor, and power behavior for kiosk-style WinForms applications. + + Provides run-time information or descriptive text for a control. 提供控制項的執行階段資訊或描述文字。 @@ -6144,6 +6149,66 @@ Stack trace where the illegal operation occurred was: 組合鍵無效。 + + Occurs when the ContainerControl property value changes. + Occurs when the ContainerControl property value changes. + + + + The container whose containing form is controlled by the kiosk mode manager. + The container whose containing form is controlled by the kiosk mode manager. + + + + Indicates whether pressing Escape exits fullscreen mode. + Indicates whether pressing Escape exits fullscreen mode. + + + + Gets or sets a value indicating whether the resolved form is currently fullscreen. + Gets or sets a value indicating whether the resolved form is currently fullscreen. + + + + Occurs when the fullscreen state changes. + Occurs when the fullscreen state changes. + + + + Indicates whether fullscreen mode covers the Windows taskbar. + Indicates whether fullscreen mode covers the Windows taskbar. + + + + The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. + The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. + + + + Indicates whether Windows should keep the display and system awake while this component is active. + Indicates whether Windows should keep the display and system awake while this component is active. + + + + The key that toggles between fullscreen and restored mode. + The key that toggles between fullscreen and restored mode. + + + + Indicates whether the form is topmost while fullscreen. + Indicates whether the form is topmost while fullscreen. + + + + Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. + Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. + + + + The command that is executed whenever the kiosk experience is woken. + The command that is executed whenever the kiosk experience is woken. + + Enables the automatic handling of text that extends beyond the width of the label control. 啟用自動處理,以處理超出標籤控制項寬度的文件。 diff --git a/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeManager.cs b/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeManager.cs new file mode 100644 index 00000000000..30bf82ff0b8 --- /dev/null +++ b/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeManager.cs @@ -0,0 +1,1145 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Windows.Input; +using Windows.Win32.System.Power; +using Windows.Win32.System.Threading; + +namespace System.Windows.Forms; + +#if NET11_0_OR_GREATER +/// +/// Manages common kiosk-mode behavior for a WinForms form. +/// +/// +/// +/// is a component that can be dropped on a +/// form or on a user control at design time. Set +/// to the owning container and the component resolves the containing +/// when fullscreen behavior is needed. +/// +/// +/// The component can make the resolved form fullscreen, optionally cover the +/// taskbar, keep the form topmost while fullscreen, suppress display and +/// system sleep, hide the mouse pointer after inactivity, and notify the +/// application when user or system activity wakes the kiosk experience. +/// +/// +/// +/// +/// public partial class MainForm : Form +/// { +/// private readonly KioskModeManager _kioskModeManager; +/// +/// public MainForm() +/// { +/// InitializeComponent(); +/// +/// _kioskModeManager = new KioskModeManager +/// { +/// ContainerControl = this, +/// HideTaskbar = true, +/// TopMostInFullScreen = true, +/// MousePointerAutoHideDelay = 3000, +/// SuppressPowerSaving = true +/// }; +/// +/// _kioskModeManager.Wakeup += (sender, e) => +/// { +/// if (e.Source == KioskModeWakeupSource.Mouse) +/// { +/// // Refresh the kiosk surface after mouse activity. +/// } +/// }; +/// +/// if (!_kioskModeManager.FullScreen) +/// { +/// _kioskModeManager.ToggleFullScreen(); +/// } +/// } +/// } +/// +/// +[DefaultProperty(nameof(ToggleFullScreenKey))] +[ToolboxItemFilter("System.Windows.Forms")] +[SRDescription(nameof(SR.DescriptionKioskModeManager))] +public class KioskModeManager : Component, ISupportInitialize +{ + private const uint PowerRequestContextVersion = 0; + private const POWER_REQUEST_CONTEXT_FLAGS PowerRequestContextSimpleString = (POWER_REQUEST_CONTEXT_FLAGS)0x00000001; + private const uint NotifyForThisSession = 0; + private const nint PbtApmResumeSuspend = 0x0007; + private const nint PbtApmResumeAutomatic = 0x0012; + private const nint WtsConsoleConnect = 0x0001; + private const nint WtsRemoteConnect = 0x0003; + private const nint WtsSessionLogon = 0x0005; + private const nint WtsSessionUnlock = 0x0008; + private const string PowerRequestReason = "WinForms KioskModeManager: Preventing screen saver and sleep"; + + private static readonly object s_containerControlChangedEvent = new(); + private static readonly object s_fullScreenChangedEvent = new(); + private static readonly object s_wakeupEvent = new(); + + private ContainerControl? _containerControl; + private bool _containerControlExplicitlySet; + private Form? _targetForm; + private bool _isFullScreen; + private bool _pendingFullScreen; + private bool _initializing; + private bool _isCursorHidden; + private KioskModeMessageFilter? _messageFilter; + private KioskModeFormObserver? _formObserver; + private Timer? _mousePointerAutoHideTimer; + + private FormBorderStyle _savedBorderStyle; + private FormWindowState _savedWindowState; + private Rectangle _savedBounds; + private bool _savedTopMost; + + private bool _hideTaskbar; + private bool _topMostInFullScreen; + private Keys _toggleFullScreenKey = Keys.F11; + private bool _escapeExitsFullScreen = true; + private bool _suppressPowerSaving; + private int _mousePointerAutoHideDelay; + private ICommand? _wakeUpCommand; + + private HANDLE _powerRequestHandle; + + /// + /// Initializes a new instance of the class. + /// + public KioskModeManager() + { + } + + /// + /// Initializes a new instance of the class + /// and adds it to the specified container. + /// + /// The container that owns the component. + /// + /// is . + /// + public KioskModeManager(IContainer container) + { + ArgumentNullException.ThrowIfNull(container); + container.Add(this); + } + + /// + /// Gets or sets the associated with the component. + /// + /// + /// + /// When the component is sited at design time and + /// has not been set explicitly, the component resolves the root component of the + /// designer host (typically the owning or ) + /// and assigns it as the . An explicitly assigned + /// is never overwritten. + /// + /// + public override ISite? Site + { + get => base.Site; + set + { + base.Site = value; + + if (value is not null + && !_containerControlExplicitlySet + && value.GetService(typeof(IDesignerHost)) is IDesignerHost host + && host.RootComponent is ContainerControl root) + { + SetContainerControl(root, isExplicitAssignment: false); + } + } + } + + /// + /// Gets or sets the container whose containing form is controlled by this + /// component. + /// + /// + /// A that is either a or + /// a child container that can resolve a parent form; otherwise, + /// . + /// + /// + /// + /// This property intentionally uses rather + /// than . A can be placed + /// on a at design time, and that user control + /// can later be hosted by a form. Restricting the property to + /// would make that design-time scenario inconsistent. + /// + /// + [SRCategory(nameof(SR.CatBehavior))] + [SRDescription(nameof(SR.KioskModeManagerContainerControlDescr))] + [DefaultValue(null)] + public ContainerControl? ContainerControl + { + get => _containerControl; + set => SetContainerControl(value, isExplicitAssignment: true); + } + + /// + /// Occurs when the value of changes. + /// + [SRCategory(nameof(SR.CatPropertyChanged))] + [SRDescription(nameof(SR.KioskModeManagerContainerControlChangedDescr))] + public event EventHandler? ContainerControlChanged + { + add => Events.AddHandler(s_containerControlChangedEvent, value); + remove => Events.RemoveHandler(s_containerControlChangedEvent, value); + } + + /// + /// Raises the event. + /// + /// An that contains the event data. + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnContainerControlChanged(EventArgs e) + { + if (Events[s_containerControlChangedEvent] is EventHandler handler) + { + handler(this, e); + } + } + + /// + /// Gets or sets a value indicating whether fullscreen mode covers the + /// Windows taskbar. + /// + /// + /// to size the form to the complete screen bounds; + /// to use normal maximized form behavior. + /// The default is . + /// + /// + /// + /// When this property is , the form is maximized + /// and Windows keeps the taskbar available according to the user's shell + /// settings. When this property is , the form is + /// sized to the screen bounds so the kiosk surface covers the taskbar. + /// + /// + [SRCategory(nameof(SR.CatBehavior))] + [SRDescription(nameof(SR.KioskModeManagerHideTaskbarDescr))] + [DefaultValue(false)] + public bool HideTaskbar + { + get => _hideTaskbar; + set + { + if (_hideTaskbar == value) + { + return; + } + + _hideTaskbar = value; + + if (_isFullScreen && _targetForm is not null) + { + ApplyFullScreen(_targetForm); + } + } + } + + /// + /// Gets or sets a value indicating whether the form is topmost while + /// fullscreen. + /// + /// + /// to set while + /// fullscreen; otherwise, . The default is + /// . + /// + [SRCategory(nameof(SR.CatBehavior))] + [SRDescription(nameof(SR.KioskModeManagerTopMostInFullScreenDescr))] + [DefaultValue(false)] + public bool TopMostInFullScreen + { + get => _topMostInFullScreen; + set + { + if (_topMostInFullScreen == value) + { + return; + } + + _topMostInFullScreen = value; + + if (_isFullScreen && _targetForm is not null) + { + _targetForm.TopMost = value; + } + } + } + + /// + /// Gets or sets the key that toggles fullscreen mode. + /// + /// + /// A value that is handled without modifiers. The + /// default is . + /// + [SRCategory(nameof(SR.CatBehavior))] + [SRDescription(nameof(SR.KioskModeManagerToggleFullScreenKeyDescr))] + [DefaultValue(Keys.F11)] + public Keys ToggleFullScreenKey + { + get => _toggleFullScreenKey; + set + { + if (_toggleFullScreenKey == value) + { + return; + } + + _toggleFullScreenKey = value; + } + } + + /// + /// Gets or sets a value indicating whether pressing Escape exits + /// fullscreen mode. + /// + /// + /// to exit fullscreen mode when Escape is pressed + /// without modifiers; otherwise, . The default is + /// . + /// + [SRCategory(nameof(SR.CatBehavior))] + [SRDescription(nameof(SR.KioskModeManagerEscapeExitsFullScreenDescr))] + [DefaultValue(true)] + public bool EscapeExitsFullScreen + { + get => _escapeExitsFullScreen; + set + { + if (_escapeExitsFullScreen == value) + { + return; + } + + _escapeExitsFullScreen = value; + } + } + + /// + /// Gets or sets a value indicating whether the component requests that + /// Windows keep the display and system awake. + /// + /// + /// to request that Windows suppress display sleep + /// and system sleep; otherwise, . The default is + /// . + /// + /// + /// + /// This property uses the Windows power request APIs. It prevents ordinary + /// display and system idle sleep while enabled, but it does not override + /// every system policy and does not configure wake timers, Wake-on-LAN, or + /// voice activation. + /// + /// + /// + /// Windows could not create or activate the power request. + /// + [SRCategory(nameof(SR.CatBehavior))] + [SRDescription(nameof(SR.KioskModeManagerSuppressPowerSavingDescr))] + [DefaultValue(false)] + public bool SuppressPowerSaving + { + get => _suppressPowerSaving; + set + { + if (_suppressPowerSaving == value) + { + return; + } + + _suppressPowerSaving = value; + UpdatePowerRequest(); + } + } + + /// + /// Gets or sets the amount of time, in milliseconds, before the mouse + /// pointer is hidden while fullscreen. + /// + /// + /// The inactivity delay in milliseconds. A value of 0 disables automatic + /// pointer hiding. The default is 0. + /// + /// + /// + /// The timer is active only while the component is in fullscreen mode. + /// When the user moves the mouse after the component hid the pointer, the + /// pointer is shown immediately and the timer starts again. + /// + /// + /// + /// is less than 0. + /// + [SRCategory(nameof(SR.CatBehavior))] + [SRDescription(nameof(SR.KioskModeManagerMousePointerAutoHideDelayDescr))] + [DefaultValue(0)] + public int MousePointerAutoHideDelay + { + get => _mousePointerAutoHideDelay; + set + { + ArgumentOutOfRangeException.ThrowIfNegative(value); + + if (_mousePointerAutoHideDelay == value) + { + return; + } + + _mousePointerAutoHideDelay = value; + + if (value == 0) + { + StopMousePointerAutoHideTimer(); + ShowMousePointerIfHidden(); + } + else if (_isFullScreen) + { + RestartMousePointerAutoHideTimer(); + } + } + } + + /// + /// Gets or sets a value indicating whether the resolved form is currently + /// fullscreen. + /// + /// + /// if the component has placed the form in + /// fullscreen mode; otherwise, . + /// + /// + /// + /// Setting this property to enters fullscreen mode and + /// setting it to restores the previously saved form + /// state. The property is bindable so it can participate in two-way data + /// binding. + /// + /// + [SRCategory(nameof(SR.CatBehavior))] + [SRDescription(nameof(SR.KioskModeManagerFullScreenDescr))] + [Bindable(true)] + [DefaultValue(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public bool FullScreen + { + get => _pendingFullScreen || _isFullScreen; + set + { + if (!value) + { + _pendingFullScreen = false; + ExitFullScreen(); + } + else if (_initializing || DesignMode || !EnterFullScreen()) + { + _pendingFullScreen = true; + } + else + { + _pendingFullScreen = false; + } + } + } + + /// + /// Occurs when the fullscreen state changes. + /// + [SRCategory(nameof(SR.CatPropertyChanged))] + [SRDescription(nameof(SR.KioskModeManagerFullScreenChangedDescr))] + public event EventHandler? FullScreenChanged + { + add => Events.AddHandler(s_fullScreenChangedEvent, value); + remove => Events.RemoveHandler(s_fullScreenChangedEvent, value); + } + + /// + /// Raises the event. + /// + /// An that contains the event data. + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnFullScreenChanged(EventArgs e) + { + if (Events[s_fullScreenChangedEvent] is EventHandler handler) + { + handler(this, e); + } + } + + /// + /// Occurs when keyboard, mouse, power resume, or session activity wakes + /// the kiosk experience. + /// + /// + /// + /// The event reports activity that the component can observe on the UI + /// thread or through Windows power/session notifications. It does not mean + /// that the component caused the computer to wake from sleep, and it does + /// not configure voice wake, network wake, or wake timers. + /// + /// + [SRCategory(nameof(SR.CatAction))] + [SRDescription(nameof(SR.KioskModeManagerWakeupDescr))] + public event KioskModeWakeupEventHandler? Wakeup + { + add => Events.AddHandler(s_wakeupEvent, value); + remove => Events.RemoveHandler(s_wakeupEvent, value); + } + + /// + /// Raises the event. + /// + /// A that contains the event data. + [EditorBrowsable(EditorBrowsableState.Advanced)] + protected virtual void OnWakeup(KioskModeWakeupEventArgs e) + { + if (Events[s_wakeupEvent] is KioskModeWakeupEventHandler handler) + { + handler(this, e); + } + + if (_wakeUpCommand is not null) + { + // The wakeup source is passed as its string name so the bound command stays + // independent of the WinForms-specific enum type. + string parameter = e.Source.ToString(); + if (_wakeUpCommand.CanExecute(parameter)) + { + _wakeUpCommand.Execute(parameter); + } + } + } + + /// + /// Gets or sets the command that is executed whenever the + /// event is raised. + /// + /// + /// An to execute on every wakeup, or + /// to execute no command. The default is . + /// + /// + /// + /// The command is executed with the wakeup source name (the result of + /// .) as the command + /// parameter, keeping the command independent of the WinForms-specific enum type. + /// + /// + [SRCategory(nameof(SR.CatAction))] + [SRDescription(nameof(SR.KioskModeManagerWakeUpCommandDescr))] + [Bindable(true)] + [DefaultValue(null)] + public ICommand? WakeUpCommand + { + get => _wakeUpCommand; + set => _wakeUpCommand = value; + } + + /// + /// Places the resolved form in fullscreen mode. + /// + /// + /// +/// The component saves the form's border style, window state, bounds, and +/// topmost state before applying fullscreen mode. Call +/// again to restore the saved state. + /// + /// + private bool EnterFullScreen() + { + if (_isFullScreen || _initializing || DesignMode) + { + return _isFullScreen; + } + + Form? targetForm = ResolveTargetForm(); + if (targetForm is null) + { + return false; + } + + _savedBorderStyle = targetForm.FormBorderStyle; + _savedWindowState = targetForm.WindowState; + _savedBounds = targetForm.WindowState == FormWindowState.Normal + ? targetForm.Bounds + : targetForm.RestoreBounds; + _savedTopMost = targetForm.TopMost; + + ApplyFullScreen(targetForm); + SetFullScreenState(true); + _pendingFullScreen = false; + RestartMousePointerAutoHideTimer(); + return true; + } + + /// + /// Restores the form state that was saved when fullscreen mode was + /// entered. + /// + private void ExitFullScreen() + { + if (!_isFullScreen || _targetForm is null) + { + return; + } + + StopMousePointerAutoHideTimer(); + ShowMousePointerIfHidden(); + + _targetForm.FormBorderStyle = _savedBorderStyle; + _targetForm.TopMost = _savedTopMost; + _targetForm.WindowState = FormWindowState.Normal; + _targetForm.Bounds = _savedBounds; + _targetForm.WindowState = _savedWindowState; + + SetFullScreenState(false); + } + + /// + /// Updates the fullscreen state and raises + /// when the value changes. + /// + /// The new fullscreen state. + private void SetFullScreenState(bool value) + { + if (_isFullScreen == value) + { + return; + } + + _isFullScreen = value; + OnFullScreenChanged(EventArgs.Empty); + } + + /// + /// Toggles the resolved form between fullscreen and restored mode. + /// + /// + /// + /// Use to determine the current state before + /// calling this method when the application needs an explicit target + /// state. + /// + /// + public void ToggleFullScreen() + => FullScreen = !FullScreen; + + void ISupportInitialize.BeginInit() + { + _initializing = true; + } + + void ISupportInitialize.EndInit() + { + _initializing = false; + + AttachToForm(); + UpdatePowerRequest(); + + if (_pendingFullScreen) + { + FullScreen = true; + } + } + + /// + protected override void Dispose(bool disposing) + { + if (disposing) + { + _pendingFullScreen = false; + ExitFullScreen(); + DetachFromForm(); + _mousePointerAutoHideTimer?.Dispose(); + _mousePointerAutoHideTimer = null; + ReleasePowerRequest(); + } + + base.Dispose(disposing); + } + + private void SetContainerControl(ContainerControl? value, bool isExplicitAssignment) + { + if (_containerControl == value) + { + if (isExplicitAssignment) + { + _containerControlExplicitlySet = true; + } + + return; + } + + if (_isFullScreen) + { + _pendingFullScreen = false; + ExitFullScreen(); + } + + if (value is null) + { + _pendingFullScreen = false; + } + + DetachFromForm(); + _containerControl = value; + if (isExplicitAssignment) + { + _containerControlExplicitlySet = true; + } + + AttachToForm(); + OnContainerControlChanged(EventArgs.Empty); + } + + private void AttachToForm() + { + if (_initializing || DesignMode || _containerControl is null) + { + return; + } + + EnsureMessageMonitoring(); + ResolveTargetForm(); + if (_pendingFullScreen && _targetForm is not null) + { + EnterFullScreen(); + } + } + + private void DetachFromForm() + { + if (_messageFilter is not null) + { + Application.RemoveMessageFilter(_messageFilter); + _messageFilter = null; + } + + _formObserver?.Detach(); + StopMousePointerAutoHideTimer(); + ShowMousePointerIfHidden(); + _targetForm = null; + } + + private Form? ResolveTargetForm() + { + // ContainerControl intentionally supports both Form and UserControl + // design-time placement. FindForm handles the UserControl case. + Form? targetForm = _containerControl as Form ?? _containerControl?.FindForm(); + if (!ReferenceEquals(_targetForm, targetForm)) + { + _targetForm = targetForm; + UpdateFormObserver(); + } + + return _targetForm; + } + + private void EnsureMessageMonitoring() + { + // IMessageFilter observes input for child controls without changing + // Form.KeyPreview or installing global keyboard/mouse hooks. + if (_messageFilter is null) + { + _messageFilter = new KioskModeMessageFilter(this); + Application.AddMessageFilter(_messageFilter); + } + } + + private void ApplyFullScreen(Form form) + { + // Apply fullscreen from a normal state so bounds and border changes do + // not operate on Windows' maximized window rectangle. + form.WindowState = FormWindowState.Normal; + form.FormBorderStyle = FormBorderStyle.None; + + if (_topMostInFullScreen) + { + form.TopMost = true; + } + + if (_hideTaskbar) + { + Screen screen = Screen.FromControl(form); + form.Bounds = screen.Bounds; + } + else + { + form.WindowState = FormWindowState.Maximized; + } + } + + private void UpdateFormObserver() + { + if (DesignMode) + { + return; + } + + _formObserver ??= new KioskModeFormObserver(this); + _formObserver.Attach(_targetForm); + } + + private void UpdatePowerRequest() + { + if (_initializing || DesignMode) + { + return; + } + + if (_suppressPowerSaving) + { + CreatePowerRequest(); + } + else + { + ReleasePowerRequest(); + } + } + + private void ProcessPowerBroadcast(WPARAM powerEvent) + { + nint value = (nint)powerEvent.Value; + if (value is PbtApmResumeAutomatic or PbtApmResumeSuspend) + { + RaiseWakeup(KioskModeWakeupSource.PowerResume); + } + } + + private void ProcessSessionChange(WPARAM sessionEvent) + { + nint value = (nint)sessionEvent.Value; + if (value is WtsConsoleConnect or WtsRemoteConnect or WtsSessionLogon or WtsSessionUnlock) + { + RaiseWakeup(KioskModeWakeupSource.Session); + } + } + + private void RestartMousePointerAutoHideTimer() + { + // Use a WinForms timer so cursor changes happen on the UI thread that + // receives the input messages restarting this timer. + if (!_isFullScreen || _mousePointerAutoHideDelay == 0) + { + return; + } + + _mousePointerAutoHideTimer ??= new Timer(); + _mousePointerAutoHideTimer.Stop(); + _mousePointerAutoHideTimer.Interval = _mousePointerAutoHideDelay; + _mousePointerAutoHideTimer.Tick -= OnMousePointerAutoHideTimerTick; + _mousePointerAutoHideTimer.Tick += OnMousePointerAutoHideTimerTick; + _mousePointerAutoHideTimer.Start(); + } + + private void StopMousePointerAutoHideTimer() + { + _mousePointerAutoHideTimer?.Stop(); + } + + private void OnMousePointerAutoHideTimerTick(object? sender, EventArgs e) + { + StopMousePointerAutoHideTimer(); + + if (!_isCursorHidden) + { + Cursor.Hide(); + _isCursorHidden = true; + } + } + + private void ProcessKeyboardActivity(Keys keyData, Keys modifiers) + { + // Any keyboard input wakes the kiosk experience, even when it does not + // match one of the configured fullscreen control keys. + RaiseWakeup(KioskModeWakeupSource.Keyboard); + + Keys keyCode = keyData & Keys.KeyCode; + + if (keyCode == _toggleFullScreenKey && modifiers == Keys.None) + { + ToggleFullScreen(); + } + else if (keyCode == Keys.Escape && modifiers == Keys.None + && _escapeExitsFullScreen && _isFullScreen) + { + ExitFullScreen(); + } + } + + private void ProcessMouseActivity() + { + ShowMousePointerIfHidden(); + RestartMousePointerAutoHideTimer(); + RaiseWakeup(KioskModeWakeupSource.Mouse); + } + + private void ShowMousePointerIfHidden() + { + // Cursor.Hide and Cursor.Show are counter-based. Only balance hides + // performed by this component. + if (!_isCursorHidden) + { + return; + } + + Cursor.Show(); + _isCursorHidden = false; + } + + private void RaiseWakeup(KioskModeWakeupSource source) + => OnWakeup(new KioskModeWakeupEventArgs(source)); + + private unsafe void CreatePowerRequest() + { + // PowerClearRequest clears the active request, but only CloseHandle + // releases the native HANDLE returned by PowerCreateRequest. + if (!_powerRequestHandle.IsNull) + { + return; + } + + REASON_CONTEXT context = new() + { + Version = PowerRequestContextVersion, + Flags = PowerRequestContextSimpleString, + }; + + nint reasonString = Marshal.StringToHGlobalUni(PowerRequestReason); + + try + { + context.Reason.SimpleReasonString = (PWSTR)(char*)reasonString; + _powerRequestHandle = PInvoke.PowerCreateRequest(in context); + } + finally + { + Marshal.FreeHGlobal(reasonString); + } + + if (_powerRequestHandle.IsNull) + { + throw new Win32Exception(); + } + + try + { + if (!PInvoke.PowerSetRequest(_powerRequestHandle, POWER_REQUEST_TYPE.PowerRequestDisplayRequired) + || !PInvoke.PowerSetRequest(_powerRequestHandle, POWER_REQUEST_TYPE.PowerRequestSystemRequired)) + { + throw new Win32Exception(); + } + } + catch + { + ReleasePowerRequest(); + throw; + } + } + + private void ReleasePowerRequest() + { + if (_powerRequestHandle.IsNull) + { + return; + } + + PInvoke.PowerClearRequest(_powerRequestHandle, POWER_REQUEST_TYPE.PowerRequestDisplayRequired); + PInvoke.PowerClearRequest(_powerRequestHandle, POWER_REQUEST_TYPE.PowerRequestSystemRequired); + PInvoke.CloseHandle(_powerRequestHandle); + _powerRequestHandle = default; + } + + private bool IsMessageFromMonitoredControl(HWND hwnd) + { + if (_targetForm is not null + && !_targetForm.IsDisposed + && (hwnd == _targetForm.HWND || PInvoke.IsChild(_targetForm, hwnd))) + { + return true; + } + + return _containerControl is not null + && !_containerControl.IsDisposed + && _containerControl.IsHandleCreated + && (hwnd == _containerControl.HWND || PInvoke.IsChild(_containerControl, hwnd)); + } + + private bool ProcessMessage(Message message) + { + if (_containerControl is null || DesignMode) + { + return false; + } + + if (_targetForm is null || _targetForm.IsDisposed) + { + ResolveTargetForm(); + if (_pendingFullScreen && _targetForm is not null) + { + EnterFullScreen(); + } + } + + if (!IsMessageFromMonitoredControl(message.HWND)) + { + return false; + } + + if (message.MsgInternal == PInvokeCore.WM_KEYDOWN + || message.MsgInternal == PInvokeCore.WM_SYSKEYDOWN) + { + ProcessKeyboardActivity((Keys)(nint)message.WParamInternal, Control.ModifierKeys & Keys.Modifiers); + } + else if (message.MsgInternal >= PInvokeCore.WM_MOUSEFIRST + && message.MsgInternal <= PInvokeCore.WM_MOUSELAST) + { + ProcessMouseActivity(); + } + + return false; + } + + /// + /// Observes keyboard and mouse messages that belong to the target form. + /// + private sealed class KioskModeMessageFilter : IMessageFilter + { + private readonly KioskModeManager _owner; + + public KioskModeMessageFilter(KioskModeManager owner) + { + _owner = owner; + } + + public bool PreFilterMessage(ref Message m) + => _owner.ProcessMessage(m); + } + + /// + /// Observes power and session messages that belong to the target form. + /// + private sealed class KioskModeFormObserver : NativeWindow + { + private readonly KioskModeManager _owner; + private Form? _form; + private bool _sessionNotificationsRegistered; + + public KioskModeFormObserver(KioskModeManager owner) + { + _owner = owner; + } + + public void Attach(Form? form) + { + if (ReferenceEquals(_form, form)) + { + return; + } + + Detach(); + + if (form is null || form.IsDisposed) + { + return; + } + + _form = form; + form.HandleCreated += OnFormHandleCreated; + form.HandleDestroyed += OnFormHandleDestroyed; + + if (form.IsHandleCreated) + { + OnFormHandleCreated(form, EventArgs.Empty); + } + } + + public void Detach() + { + if (_form is not null) + { + _form.HandleCreated -= OnFormHandleCreated; + _form.HandleDestroyed -= OnFormHandleDestroyed; + _form = null; + } + + UnregisterSessionNotifications(); + if (!HWND.IsNull) + { + ReleaseHandle(); + } + } + + private void OnFormHandleCreated(object? sender, EventArgs e) + { + if (_form is null || _form.IsDisposed || !_form.IsHandleCreated) + { + return; + } + + AssignHandle(_form.HWND); + RegisterSessionNotifications(); + } + + private void OnFormHandleDestroyed(object? sender, EventArgs e) + { + UnregisterSessionNotifications(); + if (!HWND.IsNull) + { + ReleaseHandle(); + } + } + + private void RegisterSessionNotifications() + { + if (_sessionNotificationsRegistered || HWND.IsNull) + { + return; + } + + _sessionNotificationsRegistered = PInvoke.WTSRegisterSessionNotification(HWND, NotifyForThisSession); + } + + private void UnregisterSessionNotifications() + { + if (!_sessionNotificationsRegistered) + { + return; + } + + PInvoke.WTSUnRegisterSessionNotification(HWND); + _sessionNotificationsRegistered = false; + } + + protected override void WndProc(ref Message m) + { + if (m.MsgInternal == PInvokeCore.WM_POWERBROADCAST) + { + _owner.ProcessPowerBroadcast(m.WParamInternal); + } + else if (m.MsgInternal == PInvokeCore.WM_WTSSESSION_CHANGE) + { + _owner.ProcessSessionChange(m.WParamInternal); + } + + base.WndProc(ref m); + } + } +} +#endif diff --git a/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeWakeupEventArgs.cs b/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeWakeupEventArgs.cs new file mode 100644 index 00000000000..1ad14d4fcc2 --- /dev/null +++ b/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeWakeupEventArgs.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Windows.Forms; + +#if NET11_0_OR_GREATER +/// +/// Provides data for the event. +/// +/// +/// +/// The property identifies the kind of activity the +/// component observed. Applications can use this value to distinguish direct +/// user activity, such as mouse or keyboard input, from system activity such +/// as power resume or session unlock. +/// +/// +public class KioskModeWakeupEventArgs : EventArgs +{ + /// + /// Initializes a new instance of the + /// class. + /// + /// The source of the wakeup notification. + public KioskModeWakeupEventArgs(KioskModeWakeupSource source) + { + SourceGenerated.EnumValidator.Validate(source, nameof(source)); + Source = source; + } + + /// + /// Gets the source of the wakeup notification. + /// + public KioskModeWakeupSource Source { get; } +} +#endif diff --git a/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeWakeupEventHandler.cs b/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeWakeupEventHandler.cs new file mode 100644 index 00000000000..b2784bb3b3c --- /dev/null +++ b/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeWakeupEventHandler.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Windows.Forms; + +#if NET11_0_OR_GREATER +/// +/// Represents the method that will handle the +/// event. +/// +/// The source of the event. +/// A that contains the event data. +public delegate void KioskModeWakeupEventHandler(object? sender, KioskModeWakeupEventArgs e); +#endif diff --git a/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeWakeupSource.cs b/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeWakeupSource.cs new file mode 100644 index 00000000000..6c9a05784c4 --- /dev/null +++ b/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeWakeupSource.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Windows.Forms; + +#if NET11_0_OR_GREATER +/// +/// Specifies the source of a +/// notification. +/// +public enum KioskModeWakeupSource +{ + /// + /// The wakeup notification was caused by keyboard activity. + /// + Keyboard = 0, + + /// + /// The wakeup notification was caused by mouse activity. + /// + Mouse = 1, + + /// + /// The wakeup notification was caused by the system resuming from a low + /// power state. + /// + PowerResume = 2, + + /// + /// The wakeup notification was caused by a Windows session activity, such + /// as logon or unlock. + /// + Session = 3, +} +#endif diff --git a/src/test/unit/System.Windows.Forms/System/Windows/Forms/KioskModeManagerTests.cs b/src/test/unit/System.Windows.Forms/System/Windows/Forms/KioskModeManagerTests.cs new file mode 100644 index 00000000000..c5d3adcdff4 --- /dev/null +++ b/src/test/unit/System.Windows.Forms/System/Windows/Forms/KioskModeManagerTests.cs @@ -0,0 +1,423 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable disable + +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Drawing; +using System.Windows.Input; +using Moq; + +namespace System.Windows.Forms.Tests; + +public class KioskModeManagerTests +{ + [WinFormsFact] + public void KioskModeManager_Ctor_Default() + { + using SubKioskModeManager manager = new(); + + Assert.Null(manager.Container); + Assert.Null(manager.ContainerControl); + Assert.False(manager.DesignMode); + Assert.True(manager.EscapeExitsFullScreen); + Assert.False(manager.HideTaskbar); + Assert.False(manager.FullScreen); + Assert.Equal(0, manager.MousePointerAutoHideDelay); + Assert.Null(manager.Site); + Assert.False(manager.SuppressPowerSaving); + Assert.Equal(Keys.F11, manager.ToggleFullScreenKey); + Assert.False(manager.TopMostInFullScreen); + } + + [WinFormsFact] + public void KioskModeManager_Ctor_IContainer() + { + using Container container = new(); + using KioskModeManager manager = new(container); + + Assert.Same(container, manager.Container); + Assert.NotNull(manager.Site); + } + + [WinFormsFact] + public void KioskModeManager_Ctor_NullContainer_ThrowsArgumentNullException() + { + Assert.Throws("container", () => new KioskModeManager(null)); + } + + [WinFormsFact] + public void KioskModeManager_ContainerControl_Set_GetReturnsExpected() + { + using Form form = new(); + using KioskModeManager manager = new() + { + ContainerControl = form + }; + + Assert.Same(form, manager.ContainerControl); + + manager.ContainerControl = form; + Assert.Same(form, manager.ContainerControl); + + manager.ContainerControl = null; + Assert.Null(manager.ContainerControl); + } + + [WinFormsFact] + public void KioskModeManager_ContainerControl_SetWithHandler_CallsContainerControlChanged() + { + using Form form = new(); + using KioskModeManager manager = new(); + int callCount = 0; + EventHandler handler = (sender, e) => + { + Assert.Same(manager, sender); + Assert.Same(EventArgs.Empty, e); + callCount++; + }; + + manager.ContainerControlChanged += handler; + manager.ContainerControl = form; + Assert.Equal(1, callCount); + + manager.ContainerControl = form; + Assert.Equal(1, callCount); + + manager.ContainerControl = null; + Assert.Equal(2, callCount); + + manager.ContainerControlChanged -= handler; + manager.ContainerControl = form; + Assert.Equal(2, callCount); + } + + [WinFormsTheory] + [InlineData(0)] + [InlineData(1)] + [InlineData(3000)] + public void KioskModeManager_MousePointerAutoHideDelay_Set_GetReturnsExpected(int value) + { + using KioskModeManager manager = new() + { + MousePointerAutoHideDelay = value + }; + + Assert.Equal(value, manager.MousePointerAutoHideDelay); + + manager.MousePointerAutoHideDelay = value; + Assert.Equal(value, manager.MousePointerAutoHideDelay); + } + + [WinFormsFact] + public void KioskModeManager_MousePointerAutoHideDelay_SetNegative_ThrowsArgumentOutOfRangeException() + { + using KioskModeManager manager = new(); + + Assert.Throws("value", () => manager.MousePointerAutoHideDelay = -1); + } + + [WinFormsTheory] + [EnumData] + public void KioskModeWakeupEventArgs_Ctor_Source(KioskModeWakeupSource source) + { + KioskModeWakeupEventArgs eventArgs = new(source); + + Assert.Equal(source, eventArgs.Source); + } + + [WinFormsTheory] + [InvalidEnumData] + public void KioskModeWakeupEventArgs_Ctor_InvalidSource_ThrowsInvalidEnumArgumentException(KioskModeWakeupSource source) + { + Assert.Throws("source", () => new KioskModeWakeupEventArgs(source)); + } + + [WinFormsTheory] + [EnumData] + public void KioskModeManager_OnWakeup_Invoke_CallsWakeup(KioskModeWakeupSource source) + { + using SubKioskModeManager manager = new(); + KioskModeWakeupEventArgs eventArgs = new(source); + int callCount = 0; + KioskModeWakeupEventHandler handler = (sender, e) => + { + Assert.Same(manager, sender); + Assert.Same(eventArgs, e); + Assert.Equal(source, e.Source); + callCount++; + }; + + manager.Wakeup += handler; + manager.OnWakeup(eventArgs); + Assert.Equal(1, callCount); + + manager.Wakeup -= handler; + manager.OnWakeup(eventArgs); + Assert.Equal(1, callCount); + } + + [WinFormsFact] + public void KioskModeManager_ToggleFullScreen_Form_RestoresExpected() + { + using Form form = new() + { + Bounds = new Rectangle(10, 20, 300, 200), + FormBorderStyle = FormBorderStyle.FixedDialog, + WindowState = FormWindowState.Normal, + TopMost = false + }; + + using KioskModeManager manager = new() + { + ContainerControl = form, + HideTaskbar = false, + TopMostInFullScreen = true + }; + + manager.ToggleFullScreen(); + + Assert.True(manager.FullScreen); + Assert.Equal(FormBorderStyle.None, form.FormBorderStyle); + Assert.True(form.TopMost); + Assert.Equal(FormWindowState.Maximized, form.WindowState); + + manager.ToggleFullScreen(); + + Assert.False(manager.FullScreen); + Assert.Equal(FormBorderStyle.FixedDialog, form.FormBorderStyle); + Assert.False(form.TopMost); + Assert.Equal(FormWindowState.Normal, form.WindowState); + Assert.Equal(new Rectangle(10, 20, 300, 200), form.Bounds); + } + + [WinFormsFact] + public void KioskModeManager_ToggleFullScreen_UserControlContainer_UsesParentForm() + { + using Form form = new(); + using UserControl userControl = new(); + form.Controls.Add(userControl); + using KioskModeManager manager = new() + { + ContainerControl = userControl + }; + + manager.ToggleFullScreen(); + + Assert.True(manager.FullScreen); + Assert.Equal(FormBorderStyle.None, form.FormBorderStyle); + } + + [WinFormsFact] + public void KioskModeManager_ToggleFullScreen_UserControlContainerParentedAfterSet_UsesParentForm() + { + using Form form = new(); + using UserControl userControl = new(); + using KioskModeManager manager = new() + { + ContainerControl = userControl + }; + + form.Controls.Add(userControl); + manager.ToggleFullScreen(); + + Assert.True(manager.FullScreen); + Assert.Equal(FormBorderStyle.None, form.FormBorderStyle); + } + + [WinFormsFact] + public void KioskModeManager_ContainerControl_SetWhileFullScreen_RestoresPreviousForm() + { + using Form form = new() + { + Bounds = new Rectangle(10, 20, 300, 200), + FormBorderStyle = FormBorderStyle.FixedDialog, + WindowState = FormWindowState.Normal + }; + + using KioskModeManager manager = new() + { + ContainerControl = form + }; + + manager.ToggleFullScreen(); + Assert.True(manager.FullScreen); + + manager.ContainerControl = null; + + Assert.False(manager.FullScreen); + Assert.Equal(FormBorderStyle.FixedDialog, form.FormBorderStyle); + Assert.Equal(FormWindowState.Normal, form.WindowState); + Assert.Equal(new Rectangle(10, 20, 300, 200), form.Bounds); + } + + [WinFormsFact] + public void KioskModeManager_ContainerControl_Set_DoesNotChangeFormKeyPreview() + { + using Form form = new() + { + KeyPreview = false + }; + + using KioskModeManager manager = new() + { + ContainerControl = form + }; + + Assert.False(form.KeyPreview); + } + + [WinFormsFact] + public void KioskModeManager_FullScreen_SetTrueThenFalse_EntersAndExitsFullScreen() + { + using Form form = new() + { + Bounds = new Rectangle(10, 20, 300, 200), + FormBorderStyle = FormBorderStyle.FixedDialog, + WindowState = FormWindowState.Normal + }; + + using KioskModeManager manager = new() + { + ContainerControl = form + }; + + manager.FullScreen = true; + + Assert.True(manager.FullScreen); + Assert.Equal(FormBorderStyle.None, form.FormBorderStyle); + + manager.FullScreen = false; + + Assert.False(manager.FullScreen); + Assert.Equal(FormBorderStyle.FixedDialog, form.FormBorderStyle); + Assert.Equal(FormWindowState.Normal, form.WindowState); + Assert.Equal(new Rectangle(10, 20, 300, 200), form.Bounds); + } + + [WinFormsFact] + public void KioskModeManager_FullScreen_SetWithHandler_CallsFullScreenChanged() + { + using Form form = new(); + using KioskModeManager manager = new() + { + ContainerControl = form + }; + + int callCount = 0; + EventHandler handler = (sender, e) => + { + Assert.Same(manager, sender); + Assert.Same(EventArgs.Empty, e); + callCount++; + }; + + manager.FullScreenChanged += handler; + + manager.FullScreen = true; + Assert.Equal(1, callCount); + + manager.FullScreen = true; + Assert.Equal(1, callCount); + + manager.FullScreen = false; + Assert.Equal(2, callCount); + + manager.FullScreenChanged -= handler; + manager.FullScreen = true; + Assert.Equal(2, callCount); + } + + [WinFormsTheory] + [EnumData] + public void KioskModeManager_WakeUpCommand_ExecutedOnWakeup_WithSourceName(KioskModeWakeupSource source) + { + using SubKioskModeManager manager = new(); + TestCommand command = new(); + manager.WakeUpCommand = command; + + Assert.Same(command, manager.WakeUpCommand); + + manager.OnWakeup(new KioskModeWakeupEventArgs(source)); + + Assert.Equal(1, command.ExecuteCount); + Assert.Equal(source.ToString(), command.LastParameter); + } + + [WinFormsFact] + public void KioskModeManager_WakeUpCommand_Null_DoesNotThrowOnWakeup() + { + using SubKioskModeManager manager = new(); + + Assert.Null(manager.WakeUpCommand); + + manager.OnWakeup(new KioskModeWakeupEventArgs(KioskModeWakeupSource.Keyboard)); + } + + [WinFormsFact] + public void KioskModeManager_Site_Set_AssignsRootComponentAsContainerControl() + { + using Form form = new(); + Mock host = new(); + host.Setup(h => h.RootComponent).Returns(form); + + Mock site = new(); + site.Setup(s => s.GetService(typeof(IDesignerHost))).Returns(host.Object); + + using KioskModeManager manager = new(); + manager.Site = site.Object; + + Assert.Same(form, manager.ContainerControl); + } + + [WinFormsFact] + public void KioskModeManager_Site_Set_DoesNotOverwriteExplicitContainerControl() + { + using Form explicitForm = new(); + using Form rootForm = new(); + Mock host = new(); + host.Setup(h => h.RootComponent).Returns(rootForm); + + Mock site = new(); + site.Setup(s => s.GetService(typeof(IDesignerHost))).Returns(host.Object); + + using KioskModeManager manager = new() + { + ContainerControl = explicitForm + }; + + manager.Site = site.Object; + + Assert.Same(explicitForm, manager.ContainerControl); + } + + private class TestCommand : ICommand + { + public int ExecuteCount { get; private set; } + + public object LastParameter { get; private set; } + + public event EventHandler CanExecuteChanged + { + add { } + remove { } + } + + public bool CanExecute(object parameter) => true; + + public void Execute(object parameter) + { + ExecuteCount++; + LastParameter = parameter; + } + } + + private class SubKioskModeManager : KioskModeManager + { + public new bool DesignMode => base.DesignMode; + + public new void OnWakeup(KioskModeWakeupEventArgs e) + => base.OnWakeup(e); + } +} From 7f3aa6d87d8c4d431770477494bfc7934b42416e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaus=20L=C3=B6ffelmann?= Date: Wed, 1 Jul 2026 17:39:18 -0700 Subject: [PATCH 03/10] Expand KioskModeManager tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Windows/Forms/KioskModeManagerTests.cs | 272 +++++++++++++++++- 1 file changed, 271 insertions(+), 1 deletion(-) diff --git a/src/test/unit/System.Windows.Forms/System/Windows/Forms/KioskModeManagerTests.cs b/src/test/unit/System.Windows.Forms/System/Windows/Forms/KioskModeManagerTests.cs index c5d3adcdff4..6c3f1c2d652 100644 --- a/src/test/unit/System.Windows.Forms/System/Windows/Forms/KioskModeManagerTests.cs +++ b/src/test/unit/System.Windows.Forms/System/Windows/Forms/KioskModeManagerTests.cs @@ -11,6 +11,7 @@ namespace System.Windows.Forms.Tests; +#if NET11_0_OR_GREATER public class KioskModeManagerTests { [WinFormsFact] @@ -329,6 +330,50 @@ public void KioskModeManager_FullScreen_SetWithHandler_CallsFullScreenChanged() Assert.Equal(2, callCount); } + [WinFormsTheory] + [NewAndDefaultData] + public void KioskModeManager_OnContainerControlChanged_Invoke_CallsContainerControlChanged(EventArgs eventArgs) + { + using SubKioskModeManager manager = new(); + int callCount = 0; + EventHandler handler = (sender, e) => + { + Assert.Same(manager, sender); + Assert.Same(eventArgs, e); + callCount++; + }; + + manager.ContainerControlChanged += handler; + manager.OnContainerControlChanged(eventArgs); + Assert.Equal(1, callCount); + + manager.ContainerControlChanged -= handler; + manager.OnContainerControlChanged(eventArgs); + Assert.Equal(1, callCount); + } + + [WinFormsTheory] + [NewAndDefaultData] + public void KioskModeManager_OnFullScreenChanged_Invoke_CallsFullScreenChanged(EventArgs eventArgs) + { + using SubKioskModeManager manager = new(); + int callCount = 0; + EventHandler handler = (sender, e) => + { + Assert.Same(manager, sender); + Assert.Same(eventArgs, e); + callCount++; + }; + + manager.FullScreenChanged += handler; + manager.OnFullScreenChanged(eventArgs); + Assert.Equal(1, callCount); + + manager.FullScreenChanged -= handler; + manager.OnFullScreenChanged(eventArgs); + Assert.Equal(1, callCount); + } + [WinFormsTheory] [EnumData] public void KioskModeManager_WakeUpCommand_ExecutedOnWakeup_WithSourceName(KioskModeWakeupSource source) @@ -341,6 +386,8 @@ public void KioskModeManager_WakeUpCommand_ExecutedOnWakeup_WithSourceName(Kiosk manager.OnWakeup(new KioskModeWakeupEventArgs(source)); + Assert.Equal(1, command.CanExecuteCount); + Assert.Equal(source.ToString(), command.LastCanExecuteParameter); Assert.Equal(1, command.ExecuteCount); Assert.Equal(source.ToString(), command.LastParameter); } @@ -355,6 +402,24 @@ public void KioskModeManager_WakeUpCommand_Null_DoesNotThrowOnWakeup() manager.OnWakeup(new KioskModeWakeupEventArgs(KioskModeWakeupSource.Keyboard)); } + [WinFormsFact] + public void KioskModeManager_WakeUpCommand_CanExecuteFalse_DoesNotExecute() + { + using SubKioskModeManager manager = new(); + TestCommand command = new() + { + CanExecuteResult = false + }; + + manager.WakeUpCommand = command; + manager.OnWakeup(new KioskModeWakeupEventArgs(KioskModeWakeupSource.Mouse)); + + Assert.Equal(1, command.CanExecuteCount); + Assert.Equal(KioskModeWakeupSource.Mouse.ToString(), command.LastCanExecuteParameter); + Assert.Equal(0, command.ExecuteCount); + Assert.Null(command.LastParameter); + } + [WinFormsFact] public void KioskModeManager_Site_Set_AssignsRootComponentAsContainerControl() { @@ -392,8 +457,201 @@ public void KioskModeManager_Site_Set_DoesNotOverwriteExplicitContainerControl() Assert.Same(explicitForm, manager.ContainerControl); } + [WinFormsFact] + public void KioskModeManager_Site_SetWithExplicitNull_DoesNotAssignRootComponent() + { + using Form form = new(); + Mock host = new(); + host.Setup(h => h.RootComponent).Returns(form); + + Mock site = new(); + site.Setup(s => s.GetService(typeof(IDesignerHost))).Returns(host.Object); + + using KioskModeManager manager = new() + { + ContainerControl = null + }; + + manager.Site = site.Object; + + Assert.Null(manager.ContainerControl); + } + + [WinFormsFact] + public void KioskModeManager_Site_SetWithoutExplicitContainerControl_UpdatesResolvedRootComponent() + { + using Form firstForm = new(); + using Form secondForm = new(); + + Mock firstHost = new(); + firstHost.Setup(h => h.RootComponent).Returns(firstForm); + Mock secondHost = new(); + secondHost.Setup(h => h.RootComponent).Returns(secondForm); + + Mock firstSite = new(); + firstSite.Setup(s => s.GetService(typeof(IDesignerHost))).Returns(firstHost.Object); + Mock secondSite = new(); + secondSite.Setup(s => s.GetService(typeof(IDesignerHost))).Returns(secondHost.Object); + + using KioskModeManager manager = new(); + manager.Site = firstSite.Object; + Assert.Same(firstForm, manager.ContainerControl); + + manager.Site = secondSite.Object; + Assert.Same(secondForm, manager.ContainerControl); + } + + [WinFormsFact] + public void KioskModeManager_BeginInit_FullScreenSet_DoesNotEnterUntilEndInit() + { + using Form form = new() + { + Bounds = new Rectangle(10, 20, 300, 200), + FormBorderStyle = FormBorderStyle.FixedDialog, + WindowState = FormWindowState.Normal + }; + + using KioskModeManager manager = new(); + ISupportInitialize supportInitialize = manager; + + supportInitialize.BeginInit(); + manager.ContainerControl = form; + manager.FullScreen = true; + + Assert.Equal(FormBorderStyle.FixedDialog, form.FormBorderStyle); + Assert.Equal(FormWindowState.Normal, form.WindowState); + + supportInitialize.EndInit(); + + Assert.True(manager.FullScreen); + Assert.Equal(FormBorderStyle.None, form.FormBorderStyle); + Assert.Equal(FormWindowState.Maximized, form.WindowState); + } + + [WinFormsFact] + public void KioskModeManager_DisposeWhileFullScreen_RestoresExpected() + { + using Form form = new() + { + Bounds = new Rectangle(10, 20, 300, 200), + FormBorderStyle = FormBorderStyle.FixedDialog, + WindowState = FormWindowState.Normal, + TopMost = false + }; + + KioskModeManager manager = new() + { + ContainerControl = form, + TopMostInFullScreen = true, + FullScreen = true + }; + manager.Dispose(); + + Assert.Equal(FormBorderStyle.FixedDialog, form.FormBorderStyle); + Assert.Equal(FormWindowState.Normal, form.WindowState); + Assert.Equal(new Rectangle(10, 20, 300, 200), form.Bounds); + Assert.False(form.TopMost); + } + + [WinFormsFact] + public void KioskModeManager_ProcessMessage_KeyDownThenKeyUp_TogglesOnce() + { + using Form form = new(); + using KioskModeManager manager = new() + { + ContainerControl = form + }; + + Message keyDownMessage = Message.Create(form.Handle, (int)PInvokeCore.WM_KEYDOWN, (nint)Keys.F11, 0); + Message keyUpMessage = Message.Create(form.Handle, (int)PInvokeCore.WM_KEYUP, (nint)Keys.F11, 0); + + manager.TestAccessor.Dynamic.ProcessMessage(keyDownMessage); + Assert.True(manager.FullScreen); + + manager.TestAccessor.Dynamic.ProcessMessage(keyUpMessage); + Assert.True(manager.FullScreen); + } + + [WinFormsFact] + public void KioskModeManager_ProcessMessage_MouseMove_CallsWakeup() + { + using Form form = new(); + using KioskModeManager manager = new() + { + ContainerControl = form + }; + + int callCount = 0; + KioskModeWakeupEventArgs lastEventArgs = null; + manager.Wakeup += (sender, e) => + { + Assert.Same(manager, sender); + callCount++; + lastEventArgs = e; + }; + + Message message = Message.Create(form.Handle, (int)PInvokeCore.WM_MOUSEMOVE, 0, 0); + manager.TestAccessor.Dynamic.ProcessMessage(message); + + Assert.Equal(1, callCount); + Assert.NotNull(lastEventArgs); + Assert.Equal(KioskModeWakeupSource.Mouse, lastEventArgs.Source); + } + + [WinFormsFact] + public void KioskModeManager_ProcessPowerBroadcast_Resume_CallsWakeup() + { + using SubKioskModeManager manager = new(); + int callCount = 0; + KioskModeWakeupEventArgs lastEventArgs = null; + manager.Wakeup += (sender, e) => + { + Assert.Same(manager, sender); + callCount++; + lastEventArgs = e; + }; + + Message message = Message.Create(IntPtr.Zero, (int)PInvokeCore.WM_POWERBROADCAST, (nint)0x0012, 0); + manager.TestAccessor.Dynamic.ProcessPowerBroadcast(message.WParamInternal); + + Assert.Equal(1, callCount); + Assert.NotNull(lastEventArgs); + Assert.Equal(KioskModeWakeupSource.PowerResume, lastEventArgs.Source); + } + + [WinFormsTheory] + [InlineData(0x0001)] + [InlineData(0x0003)] + [InlineData(0x0005)] + [InlineData(0x0008)] + public void KioskModeManager_ProcessSessionChange_RecognizedReason_CallsWakeup(nint sessionReason) + { + using SubKioskModeManager manager = new(); + int callCount = 0; + KioskModeWakeupEventArgs lastEventArgs = null; + manager.Wakeup += (sender, e) => + { + Assert.Same(manager, sender); + callCount++; + lastEventArgs = e; + }; + + Message message = Message.Create(IntPtr.Zero, 0, sessionReason, 0); + manager.TestAccessor.Dynamic.ProcessSessionChange(message.WParamInternal); + + Assert.Equal(1, callCount); + Assert.NotNull(lastEventArgs); + Assert.Equal(KioskModeWakeupSource.Session, lastEventArgs.Source); + } + private class TestCommand : ICommand { + public bool CanExecuteResult { get; set; } = true; + + public int CanExecuteCount { get; private set; } + + public object LastCanExecuteParameter { get; private set; } + public int ExecuteCount { get; private set; } public object LastParameter { get; private set; } @@ -404,7 +662,12 @@ public event EventHandler CanExecuteChanged remove { } } - public bool CanExecute(object parameter) => true; + public bool CanExecute(object parameter) + { + CanExecuteCount++; + LastCanExecuteParameter = parameter; + return CanExecuteResult; + } public void Execute(object parameter) { @@ -417,7 +680,14 @@ private class SubKioskModeManager : KioskModeManager { public new bool DesignMode => base.DesignMode; + public new void OnContainerControlChanged(EventArgs e) + => base.OnContainerControlChanged(e); + + public new void OnFullScreenChanged(EventArgs e) + => base.OnFullScreenChanged(e); + public new void OnWakeup(KioskModeWakeupEventArgs e) => base.OnWakeup(e); } } +#endif From 54ac8eb36d8f2a76bd9bcb76475cb3f2715800ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaus=20L=C3=B6ffelmann?= Date: Fri, 10 Jul 2026 01:35:51 -0700 Subject: [PATCH 04/10] Harden KioskModeManager lifecycle behavior Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 20883488-cf1e-4ad3-a681-0724e9f16777 --- ...04_KioskManagerComponent.HighRiskReview.md | 38 +++++ .../KioskModeManager/KioskModeManager.cs | 84 +++++++++-- .../Windows/Forms/KioskModeManagerTests.cs | 136 +++++++++++++++++- 3 files changed, 245 insertions(+), 13 deletions(-) create mode 100644 docs/Net11Api_04_KioskManagerComponent.HighRiskReview.md diff --git a/docs/Net11Api_04_KioskManagerComponent.HighRiskReview.md b/docs/Net11Api_04_KioskManagerComponent.HighRiskReview.md new file mode 100644 index 00000000000..b3cb075a1a1 --- /dev/null +++ b/docs/Net11Api_04_KioskManagerComponent.HighRiskReview.md @@ -0,0 +1,38 @@ +# Net11Api_04 KioskModeManager High-Risk Review + +This document records review findings that require an API or lifecycle design decision before they can be patched safely. + +## FullScreen requested state and actual state differ + +`KioskModeManager.FullScreen` currently returns `true` when fullscreen has been requested but no target form is available. `FullScreenChanged`, however, is raised only when the resolved form actually enters or exits fullscreen. This creates two observable state models: + +- The property reports requested state through `_pendingFullScreen || _isFullScreen`. +- The event reports actual window state through changes to `_isFullScreen`. + +Consequently, setting `FullScreen = true` without a resolved form changes the property without raising the event. When a form later becomes available, the event is raised even though the property value remains `true`. + +Before changing this behavior, decide whether `FullScreen` represents requested state or actual window state. The decision must cover: + +- initialization and delayed parenting; +- missing, replaced, reparented, and disposed container controls; +- event ordering and two-way data binding; +- `ToggleFullScreen` behavior while a request is pending; +- disposal and failed fullscreen transitions; +- compatibility for applications already observing the property or event. + +Tests should define the complete transition matrix for requested, pending, entered, exited, reparented, and disposed states. + +## Session-notification registration is not retried + +`KioskModeFormObserver.RegisterSessionNotifications` records a failed `WTSRegisterSessionNotification` call as `false` and retries only after form handle recreation. During Windows startup, registration can fail with `RPC_S_INVALID_BINDING` before `Global\TermSrvReadyEvent` is signaled. A kiosk application started during that interval can therefore miss session notifications for the lifetime of its form handle. + +A safe fix needs a non-blocking retry design that specifies: + +- which Win32 errors are retryable and which are terminal; +- how waiting for `Global\TermSrvReadyEvent` is cancelled; +- how completion is marshalled to the form's UI thread; +- how stale callbacks are rejected after handle recreation; +- how registration and unregistration remain paired; +- how disposal races with an outstanding wait. + +Tests should cover service-not-ready startup, successful retry, handle recreation during the wait, and disposal before the wait completes. diff --git a/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeManager.cs b/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeManager.cs index 30bf82ff0b8..87befa212f9 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeManager.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeManager.cs @@ -72,7 +72,6 @@ public class KioskModeManager : Component, ISupportInitialize private const uint PowerRequestContextVersion = 0; private const POWER_REQUEST_CONTEXT_FLAGS PowerRequestContextSimpleString = (POWER_REQUEST_CONTEXT_FLAGS)0x00000001; private const uint NotifyForThisSession = 0; - private const nint PbtApmResumeSuspend = 0x0007; private const nint PbtApmResumeAutomatic = 0x0012; private const nint WtsConsoleConnect = 0x0001; private const nint WtsRemoteConnect = 0x0003; @@ -91,6 +90,7 @@ public class KioskModeManager : Component, ISupportInitialize private bool _pendingFullScreen; private bool _initializing; private bool _isCursorHidden; + private readonly List _parentChain = []; private KioskModeMessageFilter? _messageFilter; private KioskModeFormObserver? _formObserver; private Timer? _mousePointerAutoHideTimer; @@ -364,8 +364,18 @@ public bool SuppressPowerSaving return; } + bool previousValue = _suppressPowerSaving; _suppressPowerSaving = value; - UpdatePowerRequest(); + + try + { + UpdatePowerRequest(); + } + catch + { + _suppressPowerSaving = previousValue; + throw; + } } } @@ -715,6 +725,7 @@ private void AttachToForm() return; } + UpdateParentChangedSubscriptions(); EnsureMessageMonitoring(); ResolveTargetForm(); if (_pendingFullScreen && _targetForm is not null) @@ -725,6 +736,8 @@ private void AttachToForm() private void DetachFromForm() { + ClearParentChangedSubscriptions(); + if (_messageFilter is not null) { Application.RemoveMessageFilter(_messageFilter); @@ -737,6 +750,52 @@ private void DetachFromForm() _targetForm = null; } + private void OnContainerControlParentChanged(object? sender, EventArgs e) + { + UpdateParentChangedSubscriptions(); + + Form? newTarget = _containerControl as Form ?? _containerControl?.FindForm(); + if (ReferenceEquals(_targetForm, newTarget)) + { + return; + } + + bool restoreFullScreen = _pendingFullScreen || _isFullScreen; + + if (_isFullScreen) + { + ExitFullScreen(); + } + + ResolveTargetForm(); + + if (restoreFullScreen) + { + FullScreen = true; + } + } + + private void UpdateParentChangedSubscriptions() + { + ClearParentChangedSubscriptions(); + + for (Control? current = _containerControl; current is not null; current = current.Parent) + { + current.ParentChanged += OnContainerControlParentChanged; + _parentChain.Add(current); + } + } + + private void ClearParentChangedSubscriptions() + { + foreach (Control control in _parentChain) + { + control.ParentChanged -= OnContainerControlParentChanged; + } + + _parentChain.Clear(); + } + private Form? ResolveTargetForm() { // ContainerControl intentionally supports both Form and UserControl @@ -769,10 +828,7 @@ private void ApplyFullScreen(Form form) form.WindowState = FormWindowState.Normal; form.FormBorderStyle = FormBorderStyle.None; - if (_topMostInFullScreen) - { - form.TopMost = true; - } + form.TopMost = _topMostInFullScreen; if (_hideTaskbar) { @@ -815,8 +871,7 @@ private void UpdatePowerRequest() private void ProcessPowerBroadcast(WPARAM powerEvent) { - nint value = (nint)powerEvent.Value; - if (value is PbtApmResumeAutomatic or PbtApmResumeSuspend) + if ((nint)powerEvent.Value == PbtApmResumeAutomatic) { RaiseWakeup(KioskModeWakeupSource.PowerResume); } @@ -864,12 +919,17 @@ private void OnMousePointerAutoHideTimerTick(object? sender, EventArgs e) } } - private void ProcessKeyboardActivity(Keys keyData, Keys modifiers) + private void ProcessKeyboardActivity(Keys keyData, Keys modifiers, bool isRepeat) { // Any keyboard input wakes the kiosk experience, even when it does not // match one of the configured fullscreen control keys. RaiseWakeup(KioskModeWakeupSource.Keyboard); + if (isRepeat) + { + return; + } + Keys keyCode = keyData & Keys.KeyCode; if (keyCode == _toggleFullScreenKey && modifiers == Keys.None) @@ -1005,7 +1065,11 @@ private bool ProcessMessage(Message message) if (message.MsgInternal == PInvokeCore.WM_KEYDOWN || message.MsgInternal == PInvokeCore.WM_SYSKEYDOWN) { - ProcessKeyboardActivity((Keys)(nint)message.WParamInternal, Control.ModifierKeys & Keys.Modifiers); + bool isRepeat = ((nuint)(nint)message.LParamInternal & (1u << 30)) != 0; + ProcessKeyboardActivity( + (Keys)(nint)message.WParamInternal, + Control.ModifierKeys & Keys.Modifiers, + isRepeat); } else if (message.MsgInternal >= PInvokeCore.WM_MOUSEFIRST && message.MsgInternal <= PInvokeCore.WM_MOUSELAST) diff --git a/src/test/unit/System.Windows.Forms/System/Windows/Forms/KioskModeManagerTests.cs b/src/test/unit/System.Windows.Forms/System/Windows/Forms/KioskModeManagerTests.cs index 6c3f1c2d652..d6a877d8bac 100644 --- a/src/test/unit/System.Windows.Forms/System/Windows/Forms/KioskModeManagerTests.cs +++ b/src/test/unit/System.Windows.Forms/System/Windows/Forms/KioskModeManagerTests.cs @@ -227,6 +227,90 @@ public void KioskModeManager_ToggleFullScreen_UserControlContainerParentedAfterS Assert.Equal(FormBorderStyle.None, form.FormBorderStyle); } + [WinFormsFact] + public void KioskModeManager_FullScreen_SetBeforeUserControlIsParented_EntersWhenParented() + { + using Form form = new(); + using UserControl userControl = new(); + using KioskModeManager manager = new() + { + ContainerControl = userControl, + FullScreen = true + }; + + form.Controls.Add(userControl); + + Assert.True(manager.FullScreen); + Assert.Equal(FormBorderStyle.None, form.FormBorderStyle); + } + + [WinFormsFact] + public void KioskModeManager_FullScreen_UserControlReparented_TransfersToNewForm() + { + using Form firstForm = new() + { + FormBorderStyle = FormBorderStyle.FixedDialog + }; + using Form secondForm = new(); + using UserControl userControl = new(); + firstForm.Controls.Add(userControl); + using KioskModeManager manager = new() + { + ContainerControl = userControl, + FullScreen = true + }; + + secondForm.Controls.Add(userControl); + + Assert.True(manager.FullScreen); + Assert.Equal(FormBorderStyle.FixedDialog, firstForm.FormBorderStyle); + Assert.Equal(FormBorderStyle.None, secondForm.FormBorderStyle); + } + + [WinFormsFact] + public void KioskModeManager_FullScreen_AncestorReparented_TransfersToNewForm() + { + using Form firstForm = new() + { + FormBorderStyle = FormBorderStyle.FixedDialog + }; + using Form secondForm = new(); + using Panel panel = new(); + using UserControl userControl = new(); + panel.Controls.Add(userControl); + firstForm.Controls.Add(panel); + using KioskModeManager manager = new() + { + ContainerControl = userControl, + FullScreen = true + }; + + secondForm.Controls.Add(panel); + + Assert.True(manager.FullScreen); + Assert.Equal(FormBorderStyle.FixedDialog, firstForm.FormBorderStyle); + Assert.Equal(FormBorderStyle.None, secondForm.FormBorderStyle); + } + + [WinFormsFact] + public void KioskModeManager_FullScreen_SetBeforeAncestorIsParented_EntersWhenParented() + { + using Form form = new(); + using Panel panel = new(); + using UserControl userControl = new(); + panel.Controls.Add(userControl); + using KioskModeManager manager = new() + { + ContainerControl = userControl, + FullScreen = true + }; + + form.Controls.Add(panel); + + Assert.True(manager.FullScreen); + Assert.Equal(FormBorderStyle.None, form.FormBorderStyle); + } + [WinFormsFact] public void KioskModeManager_ContainerControl_SetWhileFullScreen_RestoresPreviousForm() { @@ -297,6 +381,28 @@ public void KioskModeManager_FullScreen_SetTrueThenFalse_EntersAndExitsFullScree Assert.Equal(new Rectangle(10, 20, 300, 200), form.Bounds); } + [WinFormsFact] + public void KioskModeManager_FullScreen_TopMostDisabled_OverridesAndRestoresFormTopMost() + { + using Form form = new() + { + TopMost = true + }; + using KioskModeManager manager = new() + { + ContainerControl = form, + TopMostInFullScreen = false + }; + + manager.FullScreen = true; + + Assert.False(form.TopMost); + + manager.FullScreen = false; + + Assert.True(form.TopMost); + } + [WinFormsFact] public void KioskModeManager_FullScreen_SetWithHandler_CallsFullScreenChanged() { @@ -572,6 +678,28 @@ public void KioskModeManager_ProcessMessage_KeyDownThenKeyUp_TogglesOnce() Assert.True(manager.FullScreen); } + [WinFormsFact] + public void KioskModeManager_ProcessMessage_RepeatedKeyDown_DoesNotToggleAgain() + { + using Form form = new(); + using KioskModeManager manager = new() + { + ContainerControl = form + }; + + Message keyDownMessage = Message.Create(form.Handle, (int)PInvokeCore.WM_KEYDOWN, (nint)Keys.F11, 0); + Message repeatedKeyDownMessage = Message.Create( + form.Handle, + (int)PInvokeCore.WM_KEYDOWN, + (nint)Keys.F11, + 1 << 30); + + manager.TestAccessor.Dynamic.ProcessMessage(keyDownMessage); + manager.TestAccessor.Dynamic.ProcessMessage(repeatedKeyDownMessage); + + Assert.True(manager.FullScreen); + } + [WinFormsFact] public void KioskModeManager_ProcessMessage_MouseMove_CallsWakeup() { @@ -599,7 +727,7 @@ public void KioskModeManager_ProcessMessage_MouseMove_CallsWakeup() } [WinFormsFact] - public void KioskModeManager_ProcessPowerBroadcast_Resume_CallsWakeup() + public void KioskModeManager_ProcessPowerBroadcast_InteractiveResume_CallsWakeupOnce() { using SubKioskModeManager manager = new(); int callCount = 0; @@ -611,8 +739,10 @@ public void KioskModeManager_ProcessPowerBroadcast_Resume_CallsWakeup() lastEventArgs = e; }; - Message message = Message.Create(IntPtr.Zero, (int)PInvokeCore.WM_POWERBROADCAST, (nint)0x0012, 0); - manager.TestAccessor.Dynamic.ProcessPowerBroadcast(message.WParamInternal); + Message automaticResume = Message.Create(IntPtr.Zero, (int)PInvokeCore.WM_POWERBROADCAST, (nint)0x0012, 0); + Message interactiveResume = Message.Create(IntPtr.Zero, (int)PInvokeCore.WM_POWERBROADCAST, (nint)0x0007, 0); + manager.TestAccessor.Dynamic.ProcessPowerBroadcast(automaticResume.WParamInternal); + manager.TestAccessor.Dynamic.ProcessPowerBroadcast(interactiveResume.WParamInternal); Assert.Equal(1, callCount); Assert.NotNull(lastEventArgs); From 9cda2469681b878ad1fa799810da518c2f8805b1 Mon Sep 17 00:00:00 2001 From: Klaus Loeffelmann Date: Sun, 19 Jul 2026 09:31:29 -0700 Subject: [PATCH 05/10] Fix kiosk StatusStrip dropdown positioning Size borderless fullscreen forms directly to the selected screen bounds instead of maximizing them. This keeps StatusStrip items and working-area-constrained dropdowns on the same screen edge while preserving restored form state. Fixes dotnet/winforms#14765 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c352332b-4b81-4492-bcfc-9dec524c27c9 --- .../KioskModeManager/KioskModeManager.cs | 17 ++---- .../Windows/Forms/KioskModeManagerTests.cs | 56 ++++++++++++++++++- 2 files changed, 60 insertions(+), 13 deletions(-) diff --git a/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeManager.cs b/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeManager.cs index 87befa212f9..ad82738bf49 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeManager.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeManager.cs @@ -823,22 +823,17 @@ private void EnsureMessageMonitoring() private void ApplyFullScreen(Form form) { + Screen screen = Screen.FromRectangle(form.Bounds); + Rectangle fullScreenBounds = _hideTaskbar + ? screen.Bounds + : screen.WorkingArea; + // Apply fullscreen from a normal state so bounds and border changes do // not operate on Windows' maximized window rectangle. form.WindowState = FormWindowState.Normal; form.FormBorderStyle = FormBorderStyle.None; - form.TopMost = _topMostInFullScreen; - - if (_hideTaskbar) - { - Screen screen = Screen.FromControl(form); - form.Bounds = screen.Bounds; - } - else - { - form.WindowState = FormWindowState.Maximized; - } + form.Bounds = fullScreenBounds; } private void UpdateFormObserver() diff --git a/src/test/unit/System.Windows.Forms/System/Windows/Forms/KioskModeManagerTests.cs b/src/test/unit/System.Windows.Forms/System/Windows/Forms/KioskModeManagerTests.cs index d6a877d8bac..c54c4424ab0 100644 --- a/src/test/unit/System.Windows.Forms/System/Windows/Forms/KioskModeManagerTests.cs +++ b/src/test/unit/System.Windows.Forms/System/Windows/Forms/KioskModeManagerTests.cs @@ -176,13 +176,15 @@ public void KioskModeManager_ToggleFullScreen_Form_RestoresExpected() HideTaskbar = false, TopMostInFullScreen = true }; + Rectangle workingArea = Screen.FromRectangle(form.Bounds).WorkingArea; manager.ToggleFullScreen(); Assert.True(manager.FullScreen); Assert.Equal(FormBorderStyle.None, form.FormBorderStyle); Assert.True(form.TopMost); - Assert.Equal(FormWindowState.Maximized, form.WindowState); + Assert.Equal(FormWindowState.Normal, form.WindowState); + Assert.Equal(workingArea, form.Bounds); manager.ToggleFullScreen(); @@ -193,6 +195,54 @@ public void KioskModeManager_ToggleFullScreen_Form_RestoresExpected() Assert.Equal(new Rectangle(10, 20, 300, 200), form.Bounds); } + [WinFormsFact] + public void KioskModeManager_FullScreen_HideTaskbar_UsesScreenBounds() + { + using Form form = new() + { + Bounds = new Rectangle(10, 20, 300, 200) + }; + using KioskModeManager manager = new() + { + ContainerControl = form, + HideTaskbar = true + }; + Rectangle screenBounds = Screen.FromRectangle(form.Bounds).Bounds; + + manager.FullScreen = true; + + Assert.Equal(FormWindowState.Normal, form.WindowState); + Assert.Equal(screenBounds, form.Bounds); + } + + [WinFormsFact] + public void KioskModeManager_FullScreen_StatusStripDropDown_RemainsAttached() + { + using Form form = new() + { + Bounds = new Rectangle(10, 20, 300, 200) + }; + using StatusStrip statusStrip = new(); + using ToolStripDropDownButton dropDownButton = new("Options"); + dropDownButton.DropDownItems.Add("First"); + statusStrip.Items.Add(dropDownButton); + form.Controls.Add(statusStrip); + using KioskModeManager manager = new() + { + ContainerControl = form + }; + + form.Show(); + manager.FullScreen = true; + dropDownButton.ShowDropDown(); + + Rectangle itemScreenBounds = statusStrip.RectangleToScreen(dropDownButton.Bounds); + Assert.InRange( + Math.Abs(dropDownButton.DropDown.Bounds.Bottom - itemScreenBounds.Top), + 0, + 1); + } + [WinFormsFact] public void KioskModeManager_ToggleFullScreen_UserControlContainer_UsesParentForm() { @@ -619,6 +669,7 @@ public void KioskModeManager_BeginInit_FullScreenSet_DoesNotEnterUntilEndInit() using KioskModeManager manager = new(); ISupportInitialize supportInitialize = manager; + Rectangle workingArea = Screen.FromRectangle(form.Bounds).WorkingArea; supportInitialize.BeginInit(); manager.ContainerControl = form; @@ -631,7 +682,8 @@ public void KioskModeManager_BeginInit_FullScreenSet_DoesNotEnterUntilEndInit() Assert.True(manager.FullScreen); Assert.Equal(FormBorderStyle.None, form.FormBorderStyle); - Assert.Equal(FormWindowState.Maximized, form.WindowState); + Assert.Equal(FormWindowState.Normal, form.WindowState); + Assert.Equal(workingArea, form.Bounds); } [WinFormsFact] From 1180232969f341faa186e9f000d743eae545d6c0 Mon Sep 17 00:00:00 2001 From: Klaus Loeffelmann Date: Sun, 19 Jul 2026 10:42:01 -0700 Subject: [PATCH 06/10] Preserve kiosk monitor selection when minimized Choose fullscreen bounds from RestoreBounds for minimized and maximized forms so a minimized-window sentinel cannot redirect kiosk mode to the wrong monitor. Fixes dotnet/winforms#14765 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c352332b-4b81-4492-bcfc-9dec524c27c9 --- .../KioskModeManager/KioskModeManager.cs | 5 +++- .../Windows/Forms/KioskModeManagerTests.cs | 23 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeManager.cs b/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeManager.cs index ad82738bf49..f7fe931a622 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeManager.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeManager.cs @@ -823,7 +823,10 @@ private void EnsureMessageMonitoring() private void ApplyFullScreen(Form form) { - Screen screen = Screen.FromRectangle(form.Bounds); + Rectangle screenReferenceBounds = form.WindowState == FormWindowState.Normal + ? form.Bounds + : form.RestoreBounds; + Screen screen = Screen.FromRectangle(screenReferenceBounds); Rectangle fullScreenBounds = _hideTaskbar ? screen.Bounds : screen.WorkingArea; diff --git a/src/test/unit/System.Windows.Forms/System/Windows/Forms/KioskModeManagerTests.cs b/src/test/unit/System.Windows.Forms/System/Windows/Forms/KioskModeManagerTests.cs index c54c4424ab0..05d3489818d 100644 --- a/src/test/unit/System.Windows.Forms/System/Windows/Forms/KioskModeManagerTests.cs +++ b/src/test/unit/System.Windows.Forms/System/Windows/Forms/KioskModeManagerTests.cs @@ -215,6 +215,29 @@ public void KioskModeManager_FullScreen_HideTaskbar_UsesScreenBounds() Assert.Equal(screenBounds, form.Bounds); } + [WinFormsFact] + public void KioskModeManager_FullScreen_MinimizedForm_UsesRestoreMonitor() + { + using Form form = new() + { + Bounds = new Rectangle(10, 20, 300, 200), + StartPosition = FormStartPosition.Manual + }; + form.Show(); + form.WindowState = FormWindowState.Minimized; + Rectangle restoreBounds = form.RestoreBounds; + Rectangle workingArea = Screen.FromRectangle( + restoreBounds).WorkingArea; + using KioskModeManager manager = new() + { + ContainerControl = form + }; + + manager.FullScreen = true; + + Assert.Equal(workingArea, form.Bounds); + } + [WinFormsFact] public void KioskModeManager_FullScreen_StatusStripDropDown_RemainsAttached() { From c68ed96d5950e5dde52dc2f1147b629a7c3a6a2e Mon Sep 17 00:00:00 2001 From: Klaus Loeffelmann Date: Sun, 19 Jul 2026 10:52:44 -0700 Subject: [PATCH 07/10] Refresh kiosk bounds after display changes Reapply explicit fullscreen bounds when display geometry or the taskbar working area changes so the normal-state kiosk window tracks the selected screen like the previous maximized window did. Fixes dotnet/winforms#14765 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c352332b-4b81-4492-bcfc-9dec524c27c9 --- .../KioskModeManager/KioskModeManager.cs | 17 +++++++++++++ .../Windows/Forms/KioskModeManagerTests.cs | 25 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeManager.cs b/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeManager.cs index f7fe931a622..0a7a98fede8 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeManager.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeManager.cs @@ -73,6 +73,7 @@ public class KioskModeManager : Component, ISupportInitialize private const POWER_REQUEST_CONTEXT_FLAGS PowerRequestContextSimpleString = (POWER_REQUEST_CONTEXT_FLAGS)0x00000001; private const uint NotifyForThisSession = 0; private const nint PbtApmResumeAutomatic = 0x0012; + private const nint SpiSetWorkArea = 0x002F; private const nint WtsConsoleConnect = 0x0001; private const nint WtsRemoteConnect = 0x0003; private const nint WtsSessionLogon = 0x0005; @@ -839,6 +840,15 @@ private void ApplyFullScreen(Form form) form.Bounds = fullScreenBounds; } + private void RefreshFullScreenBounds() + { + if (_isFullScreen + && _targetForm is { IsDisposed: false } form) + { + ApplyFullScreen(form); + } + } + private void UpdateFormObserver() { if (DesignMode) @@ -1201,6 +1211,13 @@ protected override void WndProc(ref Message m) } base.WndProc(ref m); + + if (m.MsgInternal == PInvokeCore.WM_DISPLAYCHANGE + || (m.MsgInternal == PInvokeCore.WM_SETTINGCHANGE + && (nint)m.WParamInternal == SpiSetWorkArea)) + { + _owner.RefreshFullScreenBounds(); + } } } } diff --git a/src/test/unit/System.Windows.Forms/System/Windows/Forms/KioskModeManagerTests.cs b/src/test/unit/System.Windows.Forms/System/Windows/Forms/KioskModeManagerTests.cs index 05d3489818d..f2d349e6bc3 100644 --- a/src/test/unit/System.Windows.Forms/System/Windows/Forms/KioskModeManagerTests.cs +++ b/src/test/unit/System.Windows.Forms/System/Windows/Forms/KioskModeManagerTests.cs @@ -238,6 +238,31 @@ public void KioskModeManager_FullScreen_MinimizedForm_UsesRestoreMonitor() Assert.Equal(workingArea, form.Bounds); } + [WinFormsFact] + public void KioskModeManager_FullScreen_RefreshBounds_ReappliesWorkingArea() + { + using Form form = new() + { + Bounds = new Rectangle(10, 20, 300, 200) + }; + Rectangle workingArea = Screen.FromRectangle( + form.Bounds).WorkingArea; + using KioskModeManager manager = new() + { + ContainerControl = form, + FullScreen = true + }; + form.Bounds = new Rectangle( + workingArea.X + 10, + workingArea.Y + 10, + 300, + 200); + + manager.TestAccessor.Dynamic.RefreshFullScreenBounds(); + + Assert.Equal(workingArea, form.Bounds); + } + [WinFormsFact] public void KioskModeManager_FullScreen_StatusStripDropDown_RemainsAttached() { From 2e1110d0feefb84292558ff1920be25818b38ad6 Mon Sep 17 00:00:00 2001 From: Klaus Loffelmann Date: Tue, 28 Jul 2026 20:12:28 -0700 Subject: [PATCH 08/10] Remove wakeup surface from KioskModeManager API review feedback: the "Wakeup" elements are more general than kiosk mode and should not be part of KioskModeManager. They can reappear later with a broader scope. Removes the Wakeup event, OnWakeup, WakeUpCommand, KioskModeWakeupEventArgs, KioskModeWakeupEventHandler, and KioskModeWakeupSource, along with the power broadcast and terminal session notification plumbing that only fed them. Display change tracking, which keeps the fullscreen bounds current, is kept. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8999e9ff-9bcc-4755-b337-7fc5496d431c --- ...04_KioskManagerComponent.HighRiskReview.md | 15 -- .../src/NativeMethods.txt | 2 - .../PublicAPI.Unshipped.txt | 14 -- src/System.Windows.Forms/Resources/SR.resx | 6 - .../Resources/xlf/SR.cs.xlf | 10 - .../Resources/xlf/SR.de.xlf | 10 - .../Resources/xlf/SR.es.xlf | 10 - .../Resources/xlf/SR.fr.xlf | 10 - .../Resources/xlf/SR.it.xlf | 10 - .../Resources/xlf/SR.ja.xlf | 10 - .../Resources/xlf/SR.ko.xlf | 10 - .../Resources/xlf/SR.pl.xlf | 10 - .../Resources/xlf/SR.pt-BR.xlf | 10 - .../Resources/xlf/SR.ru.xlf | 10 - .../Resources/xlf/SR.tr.xlf | 10 - .../Resources/xlf/SR.zh-Hans.xlf | 10 - .../Resources/xlf/SR.zh-Hant.xlf | 10 - .../KioskModeManager/KioskModeManager.cs | 150 +------------- .../KioskModeWakeupEventArgs.cs | 36 ---- .../KioskModeWakeupEventHandler.cs | 14 -- .../KioskModeManager/KioskModeWakeupSource.cs | 35 ---- .../Windows/Forms/KioskModeManagerTests.cs | 190 +----------------- 22 files changed, 9 insertions(+), 583 deletions(-) delete mode 100644 src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeWakeupEventArgs.cs delete mode 100644 src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeWakeupEventHandler.cs delete mode 100644 src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeWakeupSource.cs diff --git a/docs/Net11Api_04_KioskManagerComponent.HighRiskReview.md b/docs/Net11Api_04_KioskManagerComponent.HighRiskReview.md index b3cb075a1a1..95b4644865a 100644 --- a/docs/Net11Api_04_KioskManagerComponent.HighRiskReview.md +++ b/docs/Net11Api_04_KioskManagerComponent.HighRiskReview.md @@ -21,18 +21,3 @@ Before changing this behavior, decide whether `FullScreen` represents requested - compatibility for applications already observing the property or event. Tests should define the complete transition matrix for requested, pending, entered, exited, reparented, and disposed states. - -## Session-notification registration is not retried - -`KioskModeFormObserver.RegisterSessionNotifications` records a failed `WTSRegisterSessionNotification` call as `false` and retries only after form handle recreation. During Windows startup, registration can fail with `RPC_S_INVALID_BINDING` before `Global\TermSrvReadyEvent` is signaled. A kiosk application started during that interval can therefore miss session notifications for the lifetime of its form handle. - -A safe fix needs a non-blocking retry design that specifies: - -- which Win32 errors are retryable and which are terminal; -- how waiting for `Global\TermSrvReadyEvent` is cancelled; -- how completion is marshalled to the form's UI thread; -- how stale callbacks are rejected after handle recreation; -- how registration and unregistration remain paired; -- how disposal races with an outstanding wait. - -Tests should cover service-not-ready startup, successful retry, handle recreation during the wait, and disposal before the wait completes. diff --git a/src/System.Windows.Forms.Primitives/src/NativeMethods.txt b/src/System.Windows.Forms.Primitives/src/NativeMethods.txt index 1aefb4bdfd7..9828b18cad8 100644 --- a/src/System.Windows.Forms.Primitives/src/NativeMethods.txt +++ b/src/System.Windows.Forms.Primitives/src/NativeMethods.txt @@ -700,8 +700,6 @@ WC_TABCONTROL WC_TREEVIEW WHEEL_DELTA WindowFromPoint -WTSRegisterSessionNotification -WTSUnRegisterSessionNotification WINDOWPOS WINEVENT_INCONTEXT WSF_VISIBLE diff --git a/src/System.Windows.Forms/PublicAPI.Unshipped.txt b/src/System.Windows.Forms/PublicAPI.Unshipped.txt index aa9021cbaef..9f7e38c0a62 100644 --- a/src/System.Windows.Forms/PublicAPI.Unshipped.txt +++ b/src/System.Windows.Forms/PublicAPI.Unshipped.txt @@ -127,7 +127,6 @@ System.Windows.Forms.KioskModeManager.MousePointerAutoHideDelay.get -> int System.Windows.Forms.KioskModeManager.MousePointerAutoHideDelay.set -> void virtual System.Windows.Forms.KioskModeManager.OnContainerControlChanged(System.EventArgs! e) -> void virtual System.Windows.Forms.KioskModeManager.OnFullScreenChanged(System.EventArgs! e) -> void -virtual System.Windows.Forms.KioskModeManager.OnWakeup(System.Windows.Forms.KioskModeWakeupEventArgs! e) -> void override System.Windows.Forms.KioskModeManager.Site.get -> System.ComponentModel.ISite? override System.Windows.Forms.KioskModeManager.Site.set -> void System.Windows.Forms.KioskModeManager.SuppressPowerSaving.get -> bool @@ -137,16 +136,3 @@ System.Windows.Forms.KioskModeManager.ToggleFullScreenKey.get -> System.Windows. System.Windows.Forms.KioskModeManager.ToggleFullScreenKey.set -> void System.Windows.Forms.KioskModeManager.TopMostInFullScreen.get -> bool System.Windows.Forms.KioskModeManager.TopMostInFullScreen.set -> void -System.Windows.Forms.KioskModeManager.Wakeup -> System.Windows.Forms.KioskModeWakeupEventHandler? -System.Windows.Forms.KioskModeManager.WakeUpCommand.get -> System.Windows.Input.ICommand? -System.Windows.Forms.KioskModeManager.WakeUpCommand.set -> void -System.Windows.Forms.KioskModeWakeupEventArgs -System.Windows.Forms.KioskModeWakeupEventArgs.KioskModeWakeupEventArgs(System.Windows.Forms.KioskModeWakeupSource source) -> void -System.Windows.Forms.KioskModeWakeupEventArgs.Source.get -> System.Windows.Forms.KioskModeWakeupSource -System.Windows.Forms.KioskModeWakeupEventHandler -virtual System.Windows.Forms.KioskModeWakeupEventHandler.Invoke(object? sender, System.Windows.Forms.KioskModeWakeupEventArgs! e) -> void -System.Windows.Forms.KioskModeWakeupSource -System.Windows.Forms.KioskModeWakeupSource.Keyboard = 0 -> System.Windows.Forms.KioskModeWakeupSource -System.Windows.Forms.KioskModeWakeupSource.Mouse = 1 -> System.Windows.Forms.KioskModeWakeupSource -System.Windows.Forms.KioskModeWakeupSource.PowerResume = 2 -> System.Windows.Forms.KioskModeWakeupSource -System.Windows.Forms.KioskModeWakeupSource.Session = 3 -> System.Windows.Forms.KioskModeWakeupSource diff --git a/src/System.Windows.Forms/Resources/SR.resx b/src/System.Windows.Forms/Resources/SR.resx index 651f8585703..521a3bc9a19 100644 --- a/src/System.Windows.Forms/Resources/SR.resx +++ b/src/System.Windows.Forms/Resources/SR.resx @@ -2724,12 +2724,6 @@ To replace this default dialog please handle the DataError event. Indicates whether the form is topmost while fullscreen. - - Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. - - - The command that is executed whenever the kiosk experience is woken. - Provides run-time information or descriptive text for a control. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.cs.xlf b/src/System.Windows.Forms/Resources/xlf/SR.cs.xlf index fb383e46989..fac97f35535 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.cs.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.cs.xlf @@ -6199,16 +6199,6 @@ Trasování zásobníku, kde došlo k neplatné operaci: Indicates whether the form is topmost while fullscreen. - - Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. - Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. - - - - The command that is executed whenever the kiosk experience is woken. - The command that is executed whenever the kiosk experience is woken. - - Enables the automatic handling of text that extends beyond the width of the label control. Umožňuje automatické zpracování textu, který je delší než šířka ovládacího prvku popisku. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.de.xlf b/src/System.Windows.Forms/Resources/xlf/SR.de.xlf index a50f783a344..36dd7cb4147 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.de.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.de.xlf @@ -6199,16 +6199,6 @@ Stapelüberwachung, in der der unzulässige Vorgang auftrat: Indicates whether the form is topmost while fullscreen. - - Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. - Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. - - - - The command that is executed whenever the kiosk experience is woken. - The command that is executed whenever the kiosk experience is woken. - - Enables the automatic handling of text that extends beyond the width of the label control. Aktiviert die automatische Behandlung von Text, der über die Breite des Bezeichnungssteuerelements hinausgeht. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.es.xlf b/src/System.Windows.Forms/Resources/xlf/SR.es.xlf index 9ac01c89344..e5355df8520 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.es.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.es.xlf @@ -6199,16 +6199,6 @@ El seguimiento de la pila donde tuvo lugar la operación no válida fue: Indicates whether the form is topmost while fullscreen. - - Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. - Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. - - - - The command that is executed whenever the kiosk experience is woken. - The command that is executed whenever the kiosk experience is woken. - - Enables the automatic handling of text that extends beyond the width of the label control. Permite el control automático del texto que se extiende más allá del ancho del control de la etiqueta. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.fr.xlf b/src/System.Windows.Forms/Resources/xlf/SR.fr.xlf index 7b8995760d4..0a870517484 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.fr.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.fr.xlf @@ -6199,16 +6199,6 @@ Cette opération non conforme s'est produite sur la trace de la pile : Indicates whether the form is topmost while fullscreen. - - Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. - Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. - - - - The command that is executed whenever the kiosk experience is woken. - The command that is executed whenever the kiosk experience is woken. - - Enables the automatic handling of text that extends beyond the width of the label control. Active la gestion automatique du texte qui dépasse les limites de largeur du contrôle label. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.it.xlf b/src/System.Windows.Forms/Resources/xlf/SR.it.xlf index 65721d5111d..5e38bcd357f 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.it.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.it.xlf @@ -6199,16 +6199,6 @@ Analisi dello stack dove si è verificata l'operazione non valida: Indicates whether the form is topmost while fullscreen. - - Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. - Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. - - - - The command that is executed whenever the kiosk experience is woken. - The command that is executed whenever the kiosk experience is woken. - - Enables the automatic handling of text that extends beyond the width of the label control. Consente la gestione automatica del testo che non è contenuto all'interno della larghezza del controllo etichetta. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.ja.xlf b/src/System.Windows.Forms/Resources/xlf/SR.ja.xlf index d8f67155273..9ca46b67b03 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.ja.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.ja.xlf @@ -6199,16 +6199,6 @@ Stack trace where the illegal operation occurred was: Indicates whether the form is topmost while fullscreen. - - Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. - Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. - - - - The command that is executed whenever the kiosk experience is woken. - The command that is executed whenever the kiosk experience is woken. - - Enables the automatic handling of text that extends beyond the width of the label control. ラベル コントロールの幅を越えるテキストの自動処理を有効にします。 diff --git a/src/System.Windows.Forms/Resources/xlf/SR.ko.xlf b/src/System.Windows.Forms/Resources/xlf/SR.ko.xlf index e4209c74262..8a03410237d 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.ko.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.ko.xlf @@ -6199,16 +6199,6 @@ Stack trace where the illegal operation occurred was: Indicates whether the form is topmost while fullscreen. - - Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. - Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. - - - - The command that is executed whenever the kiosk experience is woken. - The command that is executed whenever the kiosk experience is woken. - - Enables the automatic handling of text that extends beyond the width of the label control. 레이블 컨트롤의 너비보다 큰 텍스트를 자동으로 처리합니다. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.pl.xlf b/src/System.Windows.Forms/Resources/xlf/SR.pl.xlf index aa90fb4b0c6..60584784991 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.pl.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.pl.xlf @@ -6199,16 +6199,6 @@ Stos śledzenia, w którym wystąpiła zabroniona operacja: Indicates whether the form is topmost while fullscreen. - - Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. - Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. - - - - The command that is executed whenever the kiosk experience is woken. - The command that is executed whenever the kiosk experience is woken. - - Enables the automatic handling of text that extends beyond the width of the label control. Włącza automatyczną obsługę tekstu, który mieści się w szerokości formantu etykiety. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.pt-BR.xlf b/src/System.Windows.Forms/Resources/xlf/SR.pt-BR.xlf index 1b9639fc1b6..3c90d5557a1 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.pt-BR.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.pt-BR.xlf @@ -6199,16 +6199,6 @@ O rastreamento de pilha em que a operação ilegal ocorreu foi: Indicates whether the form is topmost while fullscreen. - - Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. - Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. - - - - The command that is executed whenever the kiosk experience is woken. - The command that is executed whenever the kiosk experience is woken. - - Enables the automatic handling of text that extends beyond the width of the label control. Permite a manipulação automática de texto que ultrapassa a largura do controle de rótulo. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.ru.xlf b/src/System.Windows.Forms/Resources/xlf/SR.ru.xlf index 47b1e48e15c..84950d733e1 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.ru.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.ru.xlf @@ -6199,16 +6199,6 @@ Stack trace where the illegal operation occurred was: Indicates whether the form is topmost while fullscreen. - - Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. - Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. - - - - The command that is executed whenever the kiosk experience is woken. - The command that is executed whenever the kiosk experience is woken. - - Enables the automatic handling of text that extends beyond the width of the label control. Включает автоматическую обработку текста, выходящего за пределы ширины элемента управления с подписью. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.tr.xlf b/src/System.Windows.Forms/Resources/xlf/SR.tr.xlf index 44d944c7eac..b86d46a918e 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.tr.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.tr.xlf @@ -6199,16 +6199,6 @@ Geçersiz işlemin gerçekleştiği yığın izi: Indicates whether the form is topmost while fullscreen. - - Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. - Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. - - - - The command that is executed whenever the kiosk experience is woken. - The command that is executed whenever the kiosk experience is woken. - - Enables the automatic handling of text that extends beyond the width of the label control. Etiket denetimi genişliğini aşan metnin otomatik işlenmesini sağlar. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.zh-Hans.xlf b/src/System.Windows.Forms/Resources/xlf/SR.zh-Hans.xlf index 773978a1eb4..8bc4d0ff982 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.zh-Hans.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.zh-Hans.xlf @@ -6199,16 +6199,6 @@ Stack trace where the illegal operation occurred was: Indicates whether the form is topmost while fullscreen. - - Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. - Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. - - - - The command that is executed whenever the kiosk experience is woken. - The command that is executed whenever the kiosk experience is woken. - - Enables the automatic handling of text that extends beyond the width of the label control. 启用对扩展到标签控件宽度以外的文本的自动处理。 diff --git a/src/System.Windows.Forms/Resources/xlf/SR.zh-Hant.xlf b/src/System.Windows.Forms/Resources/xlf/SR.zh-Hant.xlf index 6d1fb1b4638..6dd3bb3be34 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.zh-Hant.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.zh-Hant.xlf @@ -6199,16 +6199,6 @@ Stack trace where the illegal operation occurred was: Indicates whether the form is topmost while fullscreen. - - Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. - Occurs when keyboard, mouse, power resume, or session activity wakes the kiosk experience. - - - - The command that is executed whenever the kiosk experience is woken. - The command that is executed whenever the kiosk experience is woken. - - Enables the automatic handling of text that extends beyond the width of the label control. 啟用自動處理,以處理超出標籤控制項寬度的文件。 diff --git a/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeManager.cs b/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeManager.cs index 0a7a98fede8..83e546c553d 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeManager.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeManager.cs @@ -5,7 +5,6 @@ using System.ComponentModel.Design; using System.Drawing; using System.Runtime.InteropServices; -using System.Windows.Input; using Windows.Win32.System.Power; using Windows.Win32.System.Threading; @@ -25,8 +24,7 @@ namespace System.Windows.Forms; /// /// The component can make the resolved form fullscreen, optionally cover the /// taskbar, keep the form topmost while fullscreen, suppress display and -/// system sleep, hide the mouse pointer after inactivity, and notify the -/// application when user or system activity wakes the kiosk experience. +/// system sleep, and hide the mouse pointer after a period of inactivity. /// /// /// @@ -48,14 +46,6 @@ namespace System.Windows.Forms; /// SuppressPowerSaving = true /// }; /// -/// _kioskModeManager.Wakeup += (sender, e) => -/// { -/// if (e.Source == KioskModeWakeupSource.Mouse) -/// { -/// // Refresh the kiosk surface after mouse activity. -/// } -/// }; -/// /// if (!_kioskModeManager.FullScreen) /// { /// _kioskModeManager.ToggleFullScreen(); @@ -71,18 +61,11 @@ public class KioskModeManager : Component, ISupportInitialize { private const uint PowerRequestContextVersion = 0; private const POWER_REQUEST_CONTEXT_FLAGS PowerRequestContextSimpleString = (POWER_REQUEST_CONTEXT_FLAGS)0x00000001; - private const uint NotifyForThisSession = 0; - private const nint PbtApmResumeAutomatic = 0x0012; private const nint SpiSetWorkArea = 0x002F; - private const nint WtsConsoleConnect = 0x0001; - private const nint WtsRemoteConnect = 0x0003; - private const nint WtsSessionLogon = 0x0005; - private const nint WtsSessionUnlock = 0x0008; private const string PowerRequestReason = "WinForms KioskModeManager: Preventing screen saver and sleep"; private static readonly object s_containerControlChangedEvent = new(); private static readonly object s_fullScreenChangedEvent = new(); - private static readonly object s_wakeupEvent = new(); private ContainerControl? _containerControl; private bool _containerControlExplicitlySet; @@ -107,7 +90,6 @@ public class KioskModeManager : Component, ISupportInitialize private bool _escapeExitsFullScreen = true; private bool _suppressPowerSaving; private int _mousePointerAutoHideDelay; - private ICommand? _wakeUpCommand; private HANDLE _powerRequestHandle; @@ -493,75 +475,6 @@ protected virtual void OnFullScreenChanged(EventArgs e) } } - /// - /// Occurs when keyboard, mouse, power resume, or session activity wakes - /// the kiosk experience. - /// - /// - /// - /// The event reports activity that the component can observe on the UI - /// thread or through Windows power/session notifications. It does not mean - /// that the component caused the computer to wake from sleep, and it does - /// not configure voice wake, network wake, or wake timers. - /// - /// - [SRCategory(nameof(SR.CatAction))] - [SRDescription(nameof(SR.KioskModeManagerWakeupDescr))] - public event KioskModeWakeupEventHandler? Wakeup - { - add => Events.AddHandler(s_wakeupEvent, value); - remove => Events.RemoveHandler(s_wakeupEvent, value); - } - - /// - /// Raises the event. - /// - /// A that contains the event data. - [EditorBrowsable(EditorBrowsableState.Advanced)] - protected virtual void OnWakeup(KioskModeWakeupEventArgs e) - { - if (Events[s_wakeupEvent] is KioskModeWakeupEventHandler handler) - { - handler(this, e); - } - - if (_wakeUpCommand is not null) - { - // The wakeup source is passed as its string name so the bound command stays - // independent of the WinForms-specific enum type. - string parameter = e.Source.ToString(); - if (_wakeUpCommand.CanExecute(parameter)) - { - _wakeUpCommand.Execute(parameter); - } - } - } - - /// - /// Gets or sets the command that is executed whenever the - /// event is raised. - /// - /// - /// An to execute on every wakeup, or - /// to execute no command. The default is . - /// - /// - /// - /// The command is executed with the wakeup source name (the result of - /// .) as the command - /// parameter, keeping the command independent of the WinForms-specific enum type. - /// - /// - [SRCategory(nameof(SR.CatAction))] - [SRDescription(nameof(SR.KioskModeManagerWakeUpCommandDescr))] - [Bindable(true)] - [DefaultValue(null)] - public ICommand? WakeUpCommand - { - get => _wakeUpCommand; - set => _wakeUpCommand = value; - } - /// /// Places the resolved form in fullscreen mode. /// @@ -877,23 +790,6 @@ private void UpdatePowerRequest() } } - private void ProcessPowerBroadcast(WPARAM powerEvent) - { - if ((nint)powerEvent.Value == PbtApmResumeAutomatic) - { - RaiseWakeup(KioskModeWakeupSource.PowerResume); - } - } - - private void ProcessSessionChange(WPARAM sessionEvent) - { - nint value = (nint)sessionEvent.Value; - if (value is WtsConsoleConnect or WtsRemoteConnect or WtsSessionLogon or WtsSessionUnlock) - { - RaiseWakeup(KioskModeWakeupSource.Session); - } - } - private void RestartMousePointerAutoHideTimer() { // Use a WinForms timer so cursor changes happen on the UI thread that @@ -929,10 +825,6 @@ private void OnMousePointerAutoHideTimerTick(object? sender, EventArgs e) private void ProcessKeyboardActivity(Keys keyData, Keys modifiers, bool isRepeat) { - // Any keyboard input wakes the kiosk experience, even when it does not - // match one of the configured fullscreen control keys. - RaiseWakeup(KioskModeWakeupSource.Keyboard); - if (isRepeat) { return; @@ -955,7 +847,6 @@ private void ProcessMouseActivity() { ShowMousePointerIfHidden(); RestartMousePointerAutoHideTimer(); - RaiseWakeup(KioskModeWakeupSource.Mouse); } private void ShowMousePointerIfHidden() @@ -971,9 +862,6 @@ private void ShowMousePointerIfHidden() _isCursorHidden = false; } - private void RaiseWakeup(KioskModeWakeupSource source) - => OnWakeup(new KioskModeWakeupEventArgs(source)); - private unsafe void CreatePowerRequest() { // PowerClearRequest clears the active request, but only CloseHandle @@ -1105,13 +993,12 @@ public bool PreFilterMessage(ref Message m) } /// - /// Observes power and session messages that belong to the target form. + /// Observes display and work area changes that affect the target form. /// private sealed class KioskModeFormObserver : NativeWindow { private readonly KioskModeManager _owner; private Form? _form; - private bool _sessionNotificationsRegistered; public KioskModeFormObserver(KioskModeManager owner) { @@ -1151,7 +1038,6 @@ public void Detach() _form = null; } - UnregisterSessionNotifications(); if (!HWND.IsNull) { ReleaseHandle(); @@ -1166,50 +1052,18 @@ private void OnFormHandleCreated(object? sender, EventArgs e) } AssignHandle(_form.HWND); - RegisterSessionNotifications(); } private void OnFormHandleDestroyed(object? sender, EventArgs e) { - UnregisterSessionNotifications(); if (!HWND.IsNull) { ReleaseHandle(); } } - private void RegisterSessionNotifications() - { - if (_sessionNotificationsRegistered || HWND.IsNull) - { - return; - } - - _sessionNotificationsRegistered = PInvoke.WTSRegisterSessionNotification(HWND, NotifyForThisSession); - } - - private void UnregisterSessionNotifications() - { - if (!_sessionNotificationsRegistered) - { - return; - } - - PInvoke.WTSUnRegisterSessionNotification(HWND); - _sessionNotificationsRegistered = false; - } - protected override void WndProc(ref Message m) { - if (m.MsgInternal == PInvokeCore.WM_POWERBROADCAST) - { - _owner.ProcessPowerBroadcast(m.WParamInternal); - } - else if (m.MsgInternal == PInvokeCore.WM_WTSSESSION_CHANGE) - { - _owner.ProcessSessionChange(m.WParamInternal); - } - base.WndProc(ref m); if (m.MsgInternal == PInvokeCore.WM_DISPLAYCHANGE diff --git a/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeWakeupEventArgs.cs b/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeWakeupEventArgs.cs deleted file mode 100644 index 1ad14d4fcc2..00000000000 --- a/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeWakeupEventArgs.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Windows.Forms; - -#if NET11_0_OR_GREATER -/// -/// Provides data for the event. -/// -/// -/// -/// The property identifies the kind of activity the -/// component observed. Applications can use this value to distinguish direct -/// user activity, such as mouse or keyboard input, from system activity such -/// as power resume or session unlock. -/// -/// -public class KioskModeWakeupEventArgs : EventArgs -{ - /// - /// Initializes a new instance of the - /// class. - /// - /// The source of the wakeup notification. - public KioskModeWakeupEventArgs(KioskModeWakeupSource source) - { - SourceGenerated.EnumValidator.Validate(source, nameof(source)); - Source = source; - } - - /// - /// Gets the source of the wakeup notification. - /// - public KioskModeWakeupSource Source { get; } -} -#endif diff --git a/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeWakeupEventHandler.cs b/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeWakeupEventHandler.cs deleted file mode 100644 index b2784bb3b3c..00000000000 --- a/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeWakeupEventHandler.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Windows.Forms; - -#if NET11_0_OR_GREATER -/// -/// Represents the method that will handle the -/// event. -/// -/// The source of the event. -/// A that contains the event data. -public delegate void KioskModeWakeupEventHandler(object? sender, KioskModeWakeupEventArgs e); -#endif diff --git a/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeWakeupSource.cs b/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeWakeupSource.cs deleted file mode 100644 index 6c9a05784c4..00000000000 --- a/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeWakeupSource.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Windows.Forms; - -#if NET11_0_OR_GREATER -/// -/// Specifies the source of a -/// notification. -/// -public enum KioskModeWakeupSource -{ - /// - /// The wakeup notification was caused by keyboard activity. - /// - Keyboard = 0, - - /// - /// The wakeup notification was caused by mouse activity. - /// - Mouse = 1, - - /// - /// The wakeup notification was caused by the system resuming from a low - /// power state. - /// - PowerResume = 2, - - /// - /// The wakeup notification was caused by a Windows session activity, such - /// as logon or unlock. - /// - Session = 3, -} -#endif diff --git a/src/test/unit/System.Windows.Forms/System/Windows/Forms/KioskModeManagerTests.cs b/src/test/unit/System.Windows.Forms/System/Windows/Forms/KioskModeManagerTests.cs index f2d349e6bc3..7a89924733a 100644 --- a/src/test/unit/System.Windows.Forms/System/Windows/Forms/KioskModeManagerTests.cs +++ b/src/test/unit/System.Windows.Forms/System/Windows/Forms/KioskModeManagerTests.cs @@ -6,7 +6,6 @@ using System.ComponentModel; using System.ComponentModel.Design; using System.Drawing; -using System.Windows.Input; using Moq; namespace System.Windows.Forms.Tests; @@ -119,46 +118,6 @@ public void KioskModeManager_MousePointerAutoHideDelay_SetNegative_ThrowsArgumen Assert.Throws("value", () => manager.MousePointerAutoHideDelay = -1); } - [WinFormsTheory] - [EnumData] - public void KioskModeWakeupEventArgs_Ctor_Source(KioskModeWakeupSource source) - { - KioskModeWakeupEventArgs eventArgs = new(source); - - Assert.Equal(source, eventArgs.Source); - } - - [WinFormsTheory] - [InvalidEnumData] - public void KioskModeWakeupEventArgs_Ctor_InvalidSource_ThrowsInvalidEnumArgumentException(KioskModeWakeupSource source) - { - Assert.Throws("source", () => new KioskModeWakeupEventArgs(source)); - } - - [WinFormsTheory] - [EnumData] - public void KioskModeManager_OnWakeup_Invoke_CallsWakeup(KioskModeWakeupSource source) - { - using SubKioskModeManager manager = new(); - KioskModeWakeupEventArgs eventArgs = new(source); - int callCount = 0; - KioskModeWakeupEventHandler handler = (sender, e) => - { - Assert.Same(manager, sender); - Assert.Same(eventArgs, e); - Assert.Equal(source, e.Source); - callCount++; - }; - - manager.Wakeup += handler; - manager.OnWakeup(eventArgs); - Assert.Equal(1, callCount); - - manager.Wakeup -= handler; - manager.OnWakeup(eventArgs); - Assert.Equal(1, callCount); - } - [WinFormsFact] public void KioskModeManager_ToggleFullScreen_Form_RestoresExpected() { @@ -578,52 +537,6 @@ public void KioskModeManager_OnFullScreenChanged_Invoke_CallsFullScreenChanged(E Assert.Equal(1, callCount); } - [WinFormsTheory] - [EnumData] - public void KioskModeManager_WakeUpCommand_ExecutedOnWakeup_WithSourceName(KioskModeWakeupSource source) - { - using SubKioskModeManager manager = new(); - TestCommand command = new(); - manager.WakeUpCommand = command; - - Assert.Same(command, manager.WakeUpCommand); - - manager.OnWakeup(new KioskModeWakeupEventArgs(source)); - - Assert.Equal(1, command.CanExecuteCount); - Assert.Equal(source.ToString(), command.LastCanExecuteParameter); - Assert.Equal(1, command.ExecuteCount); - Assert.Equal(source.ToString(), command.LastParameter); - } - - [WinFormsFact] - public void KioskModeManager_WakeUpCommand_Null_DoesNotThrowOnWakeup() - { - using SubKioskModeManager manager = new(); - - Assert.Null(manager.WakeUpCommand); - - manager.OnWakeup(new KioskModeWakeupEventArgs(KioskModeWakeupSource.Keyboard)); - } - - [WinFormsFact] - public void KioskModeManager_WakeUpCommand_CanExecuteFalse_DoesNotExecute() - { - using SubKioskModeManager manager = new(); - TestCommand command = new() - { - CanExecuteResult = false - }; - - manager.WakeUpCommand = command; - manager.OnWakeup(new KioskModeWakeupEventArgs(KioskModeWakeupSource.Mouse)); - - Assert.Equal(1, command.CanExecuteCount); - Assert.Equal(KioskModeWakeupSource.Mouse.ToString(), command.LastCanExecuteParameter); - Assert.Equal(0, command.ExecuteCount); - Assert.Null(command.LastParameter); - } - [WinFormsFact] public void KioskModeManager_Site_Set_AssignsRootComponentAsContainerControl() { @@ -801,109 +714,23 @@ public void KioskModeManager_ProcessMessage_RepeatedKeyDown_DoesNotToggleAgain() } [WinFormsFact] - public void KioskModeManager_ProcessMessage_MouseMove_CallsWakeup() + public void KioskModeManager_ProcessMessage_MouseMove_ShowsHiddenMousePointer() { using Form form = new(); using KioskModeManager manager = new() { - ContainerControl = form + ContainerControl = form, + MousePointerAutoHideDelay = 1, + FullScreen = true }; - int callCount = 0; - KioskModeWakeupEventArgs lastEventArgs = null; - manager.Wakeup += (sender, e) => - { - Assert.Same(manager, sender); - callCount++; - lastEventArgs = e; - }; + manager.TestAccessor.Dynamic.OnMousePointerAutoHideTimerTick(null, EventArgs.Empty); + Assert.True(manager.TestAccessor.Dynamic._isCursorHidden); Message message = Message.Create(form.Handle, (int)PInvokeCore.WM_MOUSEMOVE, 0, 0); manager.TestAccessor.Dynamic.ProcessMessage(message); - Assert.Equal(1, callCount); - Assert.NotNull(lastEventArgs); - Assert.Equal(KioskModeWakeupSource.Mouse, lastEventArgs.Source); - } - - [WinFormsFact] - public void KioskModeManager_ProcessPowerBroadcast_InteractiveResume_CallsWakeupOnce() - { - using SubKioskModeManager manager = new(); - int callCount = 0; - KioskModeWakeupEventArgs lastEventArgs = null; - manager.Wakeup += (sender, e) => - { - Assert.Same(manager, sender); - callCount++; - lastEventArgs = e; - }; - - Message automaticResume = Message.Create(IntPtr.Zero, (int)PInvokeCore.WM_POWERBROADCAST, (nint)0x0012, 0); - Message interactiveResume = Message.Create(IntPtr.Zero, (int)PInvokeCore.WM_POWERBROADCAST, (nint)0x0007, 0); - manager.TestAccessor.Dynamic.ProcessPowerBroadcast(automaticResume.WParamInternal); - manager.TestAccessor.Dynamic.ProcessPowerBroadcast(interactiveResume.WParamInternal); - - Assert.Equal(1, callCount); - Assert.NotNull(lastEventArgs); - Assert.Equal(KioskModeWakeupSource.PowerResume, lastEventArgs.Source); - } - - [WinFormsTheory] - [InlineData(0x0001)] - [InlineData(0x0003)] - [InlineData(0x0005)] - [InlineData(0x0008)] - public void KioskModeManager_ProcessSessionChange_RecognizedReason_CallsWakeup(nint sessionReason) - { - using SubKioskModeManager manager = new(); - int callCount = 0; - KioskModeWakeupEventArgs lastEventArgs = null; - manager.Wakeup += (sender, e) => - { - Assert.Same(manager, sender); - callCount++; - lastEventArgs = e; - }; - - Message message = Message.Create(IntPtr.Zero, 0, sessionReason, 0); - manager.TestAccessor.Dynamic.ProcessSessionChange(message.WParamInternal); - - Assert.Equal(1, callCount); - Assert.NotNull(lastEventArgs); - Assert.Equal(KioskModeWakeupSource.Session, lastEventArgs.Source); - } - - private class TestCommand : ICommand - { - public bool CanExecuteResult { get; set; } = true; - - public int CanExecuteCount { get; private set; } - - public object LastCanExecuteParameter { get; private set; } - - public int ExecuteCount { get; private set; } - - public object LastParameter { get; private set; } - - public event EventHandler CanExecuteChanged - { - add { } - remove { } - } - - public bool CanExecute(object parameter) - { - CanExecuteCount++; - LastCanExecuteParameter = parameter; - return CanExecuteResult; - } - - public void Execute(object parameter) - { - ExecuteCount++; - LastParameter = parameter; - } + Assert.False(manager.TestAccessor.Dynamic._isCursorHidden); } private class SubKioskModeManager : KioskModeManager @@ -915,9 +742,6 @@ private class SubKioskModeManager : KioskModeManager public new void OnFullScreenChanged(EventArgs e) => base.OnFullScreenChanged(e); - - public new void OnWakeup(KioskModeWakeupEventArgs e) - => base.OnWakeup(e); } } #endif From 13fffab68621aac6c9146088dd70793830ad1b1e Mon Sep 17 00:00:00 2001 From: Klaus Loffelmann Date: Tue, 28 Jul 2026 20:22:36 -0700 Subject: [PATCH 09/10] Remove HideTaskbar and simplify the kiosk window model API review feedback: HideTaskbar was removed to simplify the window models. Kiosk fullscreen now always sizes the borderless form to the complete bounds of the screen that displays it, so there is a single window model and the kiosk surface always covers the taskbar. Because the fullscreen form now covers the taskbar, drop-downs owned by that form can no longer be constrained to the screen working area - doing so detaches them from the item that opened them (dotnet/winforms#14765). ToolStripDropDown and ToolStripDropDownItem now constrain to the complete screen bounds when the owning top-level window is borderless and covers the screen. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8999e9ff-9bcc-4755-b337-7fc5496d431c --- .../PublicAPI.Unshipped.txt | 2 - src/System.Windows.Forms/Resources/SR.resx | 3 - .../Resources/xlf/SR.cs.xlf | 5 -- .../Resources/xlf/SR.de.xlf | 5 -- .../Resources/xlf/SR.es.xlf | 5 -- .../Resources/xlf/SR.fr.xlf | 5 -- .../Resources/xlf/SR.it.xlf | 5 -- .../Resources/xlf/SR.ja.xlf | 5 -- .../Resources/xlf/SR.ko.xlf | 5 -- .../Resources/xlf/SR.pl.xlf | 5 -- .../Resources/xlf/SR.pt-BR.xlf | 5 -- .../Resources/xlf/SR.ru.xlf | 5 -- .../Resources/xlf/SR.tr.xlf | 5 -- .../Resources/xlf/SR.zh-Hans.xlf | 5 -- .../Resources/xlf/SR.zh-Hant.xlf | 5 -- .../KioskModeManager/KioskModeManager.cs | 66 ++++--------------- .../Controls/ToolStrips/ToolStripDropDown.cs | 4 +- .../ToolStrips/ToolStripDropDownItem.cs | 4 +- .../Windows/Forms/Internal/WinFormsUtils.cs | 31 +++++++++ .../Windows/Forms/KioskModeManagerTests.cs | 33 +++++----- 20 files changed, 65 insertions(+), 143 deletions(-) diff --git a/src/System.Windows.Forms/PublicAPI.Unshipped.txt b/src/System.Windows.Forms/PublicAPI.Unshipped.txt index 9f7e38c0a62..9e066bb6df8 100644 --- a/src/System.Windows.Forms/PublicAPI.Unshipped.txt +++ b/src/System.Windows.Forms/PublicAPI.Unshipped.txt @@ -119,8 +119,6 @@ System.Windows.Forms.KioskModeManager.EscapeExitsFullScreen.set -> void System.Windows.Forms.KioskModeManager.FullScreen.get -> bool System.Windows.Forms.KioskModeManager.FullScreen.set -> void System.Windows.Forms.KioskModeManager.FullScreenChanged -> System.EventHandler? -System.Windows.Forms.KioskModeManager.HideTaskbar.get -> bool -System.Windows.Forms.KioskModeManager.HideTaskbar.set -> void System.Windows.Forms.KioskModeManager.KioskModeManager() -> void System.Windows.Forms.KioskModeManager.KioskModeManager(System.ComponentModel.IContainer! container) -> void System.Windows.Forms.KioskModeManager.MousePointerAutoHideDelay.get -> int diff --git a/src/System.Windows.Forms/Resources/SR.resx b/src/System.Windows.Forms/Resources/SR.resx index 521a3bc9a19..d3886bb35e3 100644 --- a/src/System.Windows.Forms/Resources/SR.resx +++ b/src/System.Windows.Forms/Resources/SR.resx @@ -2709,9 +2709,6 @@ To replace this default dialog please handle the DataError event. Occurs when the fullscreen state changes. - - Indicates whether fullscreen mode covers the Windows taskbar. - The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.cs.xlf b/src/System.Windows.Forms/Resources/xlf/SR.cs.xlf index fac97f35535..1fe847a635f 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.cs.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.cs.xlf @@ -6174,11 +6174,6 @@ Trasování zásobníku, kde došlo k neplatné operaci: Occurs when the fullscreen state changes. - - Indicates whether fullscreen mode covers the Windows taskbar. - Indicates whether fullscreen mode covers the Windows taskbar. - - The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.de.xlf b/src/System.Windows.Forms/Resources/xlf/SR.de.xlf index 36dd7cb4147..7d5b02b4823 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.de.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.de.xlf @@ -6174,11 +6174,6 @@ Stapelüberwachung, in der der unzulässige Vorgang auftrat: Occurs when the fullscreen state changes. - - Indicates whether fullscreen mode covers the Windows taskbar. - Indicates whether fullscreen mode covers the Windows taskbar. - - The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.es.xlf b/src/System.Windows.Forms/Resources/xlf/SR.es.xlf index e5355df8520..01920c9afce 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.es.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.es.xlf @@ -6174,11 +6174,6 @@ El seguimiento de la pila donde tuvo lugar la operación no válida fue: Occurs when the fullscreen state changes. - - Indicates whether fullscreen mode covers the Windows taskbar. - Indicates whether fullscreen mode covers the Windows taskbar. - - The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.fr.xlf b/src/System.Windows.Forms/Resources/xlf/SR.fr.xlf index 0a870517484..9858b7273b5 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.fr.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.fr.xlf @@ -6174,11 +6174,6 @@ Cette opération non conforme s'est produite sur la trace de la pile : Occurs when the fullscreen state changes. - - Indicates whether fullscreen mode covers the Windows taskbar. - Indicates whether fullscreen mode covers the Windows taskbar. - - The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.it.xlf b/src/System.Windows.Forms/Resources/xlf/SR.it.xlf index 5e38bcd357f..78a62927973 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.it.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.it.xlf @@ -6174,11 +6174,6 @@ Analisi dello stack dove si è verificata l'operazione non valida: Occurs when the fullscreen state changes. - - Indicates whether fullscreen mode covers the Windows taskbar. - Indicates whether fullscreen mode covers the Windows taskbar. - - The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.ja.xlf b/src/System.Windows.Forms/Resources/xlf/SR.ja.xlf index 9ca46b67b03..b779ec46905 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.ja.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.ja.xlf @@ -6174,11 +6174,6 @@ Stack trace where the illegal operation occurred was: Occurs when the fullscreen state changes. - - Indicates whether fullscreen mode covers the Windows taskbar. - Indicates whether fullscreen mode covers the Windows taskbar. - - The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.ko.xlf b/src/System.Windows.Forms/Resources/xlf/SR.ko.xlf index 8a03410237d..20578fdf6fe 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.ko.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.ko.xlf @@ -6174,11 +6174,6 @@ Stack trace where the illegal operation occurred was: Occurs when the fullscreen state changes. - - Indicates whether fullscreen mode covers the Windows taskbar. - Indicates whether fullscreen mode covers the Windows taskbar. - - The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.pl.xlf b/src/System.Windows.Forms/Resources/xlf/SR.pl.xlf index 60584784991..90d68c67913 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.pl.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.pl.xlf @@ -6174,11 +6174,6 @@ Stos śledzenia, w którym wystąpiła zabroniona operacja: Occurs when the fullscreen state changes. - - Indicates whether fullscreen mode covers the Windows taskbar. - Indicates whether fullscreen mode covers the Windows taskbar. - - The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.pt-BR.xlf b/src/System.Windows.Forms/Resources/xlf/SR.pt-BR.xlf index 3c90d5557a1..d0bbc52c5b1 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.pt-BR.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.pt-BR.xlf @@ -6174,11 +6174,6 @@ O rastreamento de pilha em que a operação ilegal ocorreu foi: Occurs when the fullscreen state changes. - - Indicates whether fullscreen mode covers the Windows taskbar. - Indicates whether fullscreen mode covers the Windows taskbar. - - The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.ru.xlf b/src/System.Windows.Forms/Resources/xlf/SR.ru.xlf index 84950d733e1..ee2d66f9c63 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.ru.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.ru.xlf @@ -6174,11 +6174,6 @@ Stack trace where the illegal operation occurred was: Occurs when the fullscreen state changes. - - Indicates whether fullscreen mode covers the Windows taskbar. - Indicates whether fullscreen mode covers the Windows taskbar. - - The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.tr.xlf b/src/System.Windows.Forms/Resources/xlf/SR.tr.xlf index b86d46a918e..dd387a2786f 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.tr.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.tr.xlf @@ -6174,11 +6174,6 @@ Geçersiz işlemin gerçekleştiği yığın izi: Occurs when the fullscreen state changes. - - Indicates whether fullscreen mode covers the Windows taskbar. - Indicates whether fullscreen mode covers the Windows taskbar. - - The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.zh-Hans.xlf b/src/System.Windows.Forms/Resources/xlf/SR.zh-Hans.xlf index 8bc4d0ff982..4067e9599aa 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.zh-Hans.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.zh-Hans.xlf @@ -6174,11 +6174,6 @@ Stack trace where the illegal operation occurred was: Occurs when the fullscreen state changes. - - Indicates whether fullscreen mode covers the Windows taskbar. - Indicates whether fullscreen mode covers the Windows taskbar. - - The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.zh-Hant.xlf b/src/System.Windows.Forms/Resources/xlf/SR.zh-Hant.xlf index 6dd3bb3be34..11a299a8763 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.zh-Hant.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.zh-Hant.xlf @@ -6174,11 +6174,6 @@ Stack trace where the illegal operation occurred was: Occurs when the fullscreen state changes. - - Indicates whether fullscreen mode covers the Windows taskbar. - Indicates whether fullscreen mode covers the Windows taskbar. - - The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. diff --git a/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeManager.cs b/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeManager.cs index 83e546c553d..8ac3df51da8 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeManager.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeManager.cs @@ -22,9 +22,9 @@ namespace System.Windows.Forms; /// when fullscreen behavior is needed. /// /// -/// The component can make the resolved form fullscreen, optionally cover the -/// taskbar, keep the form topmost while fullscreen, suppress display and -/// system sleep, and hide the mouse pointer after a period of inactivity. +/// The component can make the resolved form fullscreen, keep the form topmost +/// while fullscreen, suppress display and system sleep, and hide the mouse +/// pointer after a period of inactivity. /// /// /// @@ -40,7 +40,6 @@ namespace System.Windows.Forms; /// _kioskModeManager = new KioskModeManager /// { /// ContainerControl = this, -/// HideTaskbar = true, /// TopMostInFullScreen = true, /// MousePointerAutoHideDelay = 3000, /// SuppressPowerSaving = true @@ -61,7 +60,6 @@ public class KioskModeManager : Component, ISupportInitialize { private const uint PowerRequestContextVersion = 0; private const POWER_REQUEST_CONTEXT_FLAGS PowerRequestContextSimpleString = (POWER_REQUEST_CONTEXT_FLAGS)0x00000001; - private const nint SpiSetWorkArea = 0x002F; private const string PowerRequestReason = "WinForms KioskModeManager: Preventing screen saver and sleep"; private static readonly object s_containerControlChangedEvent = new(); @@ -84,7 +82,6 @@ public class KioskModeManager : Component, ISupportInitialize private Rectangle _savedBounds; private bool _savedTopMost; - private bool _hideTaskbar; private bool _topMostInFullScreen; private Keys _toggleFullScreenKey = Keys.F11; private bool _escapeExitsFullScreen = true; @@ -194,45 +191,6 @@ protected virtual void OnContainerControlChanged(EventArgs e) } } - /// - /// Gets or sets a value indicating whether fullscreen mode covers the - /// Windows taskbar. - /// - /// - /// to size the form to the complete screen bounds; - /// to use normal maximized form behavior. - /// The default is . - /// - /// - /// - /// When this property is , the form is maximized - /// and Windows keeps the taskbar available according to the user's shell - /// settings. When this property is , the form is - /// sized to the screen bounds so the kiosk surface covers the taskbar. - /// - /// - [SRCategory(nameof(SR.CatBehavior))] - [SRDescription(nameof(SR.KioskModeManagerHideTaskbarDescr))] - [DefaultValue(false)] - public bool HideTaskbar - { - get => _hideTaskbar; - set - { - if (_hideTaskbar == value) - { - return; - } - - _hideTaskbar = value; - - if (_isFullScreen && _targetForm is not null) - { - ApplyFullScreen(_targetForm); - } - } - } - /// /// Gets or sets a value indicating whether the form is topmost while /// fullscreen. @@ -424,6 +382,11 @@ public int MousePointerAutoHideDelay /// state. The property is bindable so it can participate in two-way data /// binding. /// + /// + /// In fullscreen mode the form is borderless and sized to the complete bounds + /// of the screen that currently displays it, so the kiosk surface also covers + /// the taskbar. + /// /// [SRCategory(nameof(SR.CatBehavior))] [SRDescription(nameof(SR.KioskModeManagerFullScreenDescr))] @@ -741,9 +704,10 @@ private void ApplyFullScreen(Form form) ? form.Bounds : form.RestoreBounds; Screen screen = Screen.FromRectangle(screenReferenceBounds); - Rectangle fullScreenBounds = _hideTaskbar - ? screen.Bounds - : screen.WorkingArea; + + // Kiosk fullscreen always covers the complete screen, including the + // taskbar, so there is only one window model to reason about. + Rectangle fullScreenBounds = screen.Bounds; // Apply fullscreen from a normal state so bounds and border changes do // not operate on Windows' maximized window rectangle. @@ -993,7 +957,7 @@ public bool PreFilterMessage(ref Message m) } /// - /// Observes display and work area changes that affect the target form. + /// Observes display changes that affect the target form. /// private sealed class KioskModeFormObserver : NativeWindow { @@ -1066,9 +1030,7 @@ protected override void WndProc(ref Message m) { base.WndProc(ref m); - if (m.MsgInternal == PInvokeCore.WM_DISPLAYCHANGE - || (m.MsgInternal == PInvokeCore.WM_SETTINGCHANGE - && (nint)m.WParamInternal == SpiSetWorkArea)) + if (m.MsgInternal == PInvokeCore.WM_DISPLAYCHANGE) { _owner.RefreshFullScreenBounds(); } diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/ToolStrips/ToolStripDropDown.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/ToolStrips/ToolStripDropDown.cs index 44b2c6e0ecd..297970e2933 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/ToolStrips/ToolStripDropDown.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/ToolStrips/ToolStripDropDown.cs @@ -991,7 +991,7 @@ internal Rectangle GetDropDownBounds(Rectangle suggestedBounds) Rectangle suggestedScreenBounds = new(screenPoint, suggestedBounds.Size); dropDownBounds = WorkingAreaConstrained - ? WindowsFormsUtils.ConstrainToScreenWorkingAreaBounds(suggestedScreenBounds) + ? WindowsFormsUtils.ConstrainToScreenWorkingAreaBounds(suggestedScreenBounds, GetToplevelOwnerToolStrip()) : WindowsFormsUtils.ConstrainToScreenBounds(suggestedScreenBounds); } else @@ -1040,7 +1040,7 @@ internal Rectangle CalculateDropDownLocation(Point start, ToolStripDropDownDirec dropDownBounds.Location = new Point(start.X + offset.X, start.Y + offset.Y); if (WorkingAreaConstrained) { - dropDownBounds = WindowsFormsUtils.ConstrainToScreenWorkingAreaBounds(dropDownBounds); + dropDownBounds = WindowsFormsUtils.ConstrainToScreenWorkingAreaBounds(dropDownBounds, GetToplevelOwnerToolStrip()); } return dropDownBounds; diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/ToolStrips/ToolStripDropDownItem.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/ToolStrips/ToolStripDropDownItem.cs index 06c8f21ba53..0ed57f7f47e 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/ToolStrips/ToolStripDropDownItem.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/ToolStrips/ToolStripDropDownItem.cs @@ -334,7 +334,9 @@ private Rectangle DropDownDirectionToDropDownBounds(ToolStripDropDownDirection d Point itemScreenLocation = TranslatePoint(Point.Empty, ToolStripPointType.ToolStripItemCoords, ToolStripPointType.ScreenCoords); dropDownBounds.Location = new Point(itemScreenLocation.X + offset.X, itemScreenLocation.Y + offset.Y); - dropDownBounds = WindowsFormsUtils.ConstrainToScreenWorkingAreaBounds(dropDownBounds); + dropDownBounds = WindowsFormsUtils.ConstrainToScreenWorkingAreaBounds( + dropDownBounds, + Owner?.GetToplevelOwnerToolStrip()); return dropDownBounds; } diff --git a/src/System.Windows.Forms/System/Windows/Forms/Internal/WinFormsUtils.cs b/src/System.Windows.Forms/System/Windows/Forms/Internal/WinFormsUtils.cs index 5496f69767f..20106cb4b6a 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Internal/WinFormsUtils.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Internal/WinFormsUtils.cs @@ -65,6 +65,37 @@ internal static Rectangle ConstrainToScreenWorkingAreaBounds(Rectangle bounds) return ConstrainToBounds(Screen.GetWorkingArea(bounds), bounds); } + /// + /// Constrains to the region that auxiliary windows owned by + /// may occupy. + /// + internal static Rectangle ConstrainToScreenWorkingAreaBounds(Rectangle bounds, Control? owner) + { + return ConstrainToBounds(GetAvailableScreenBounds(bounds, owner), bounds); + } + + /// + /// Gets the region on the screen that displays which auxiliary + /// windows, such as drop-downs, may occupy. + /// + /// + /// + /// This is the working area of that screen, unless belongs to a + /// borderless top-level window that already covers the complete screen. Such a window - a + /// kiosk-style fullscreen form, for example - also covers the taskbar, so constraining to + /// the working area would detach drop-downs from the items that own them. + /// + /// + internal static Rectangle GetAvailableScreenBounds(Rectangle bounds, Control? owner) + { + Screen screen = Screen.FromRectangle(bounds); + + return owner?.TopLevelControl is Form { FormBorderStyle: FormBorderStyle.None } form + && form.Bounds.Contains(screen.Bounds) + ? screen.Bounds + : screen.WorkingArea; + } + /// /// Given a rectangle, constrain it to fit onto the current screen. /// diff --git a/src/test/unit/System.Windows.Forms/System/Windows/Forms/KioskModeManagerTests.cs b/src/test/unit/System.Windows.Forms/System/Windows/Forms/KioskModeManagerTests.cs index 7a89924733a..bef6a210078 100644 --- a/src/test/unit/System.Windows.Forms/System/Windows/Forms/KioskModeManagerTests.cs +++ b/src/test/unit/System.Windows.Forms/System/Windows/Forms/KioskModeManagerTests.cs @@ -22,7 +22,6 @@ public void KioskModeManager_Ctor_Default() Assert.Null(manager.ContainerControl); Assert.False(manager.DesignMode); Assert.True(manager.EscapeExitsFullScreen); - Assert.False(manager.HideTaskbar); Assert.False(manager.FullScreen); Assert.Equal(0, manager.MousePointerAutoHideDelay); Assert.Null(manager.Site); @@ -132,10 +131,9 @@ public void KioskModeManager_ToggleFullScreen_Form_RestoresExpected() using KioskModeManager manager = new() { ContainerControl = form, - HideTaskbar = false, TopMostInFullScreen = true }; - Rectangle workingArea = Screen.FromRectangle(form.Bounds).WorkingArea; + Rectangle screenBounds = Screen.FromRectangle(form.Bounds).Bounds; manager.ToggleFullScreen(); @@ -143,7 +141,7 @@ public void KioskModeManager_ToggleFullScreen_Form_RestoresExpected() Assert.Equal(FormBorderStyle.None, form.FormBorderStyle); Assert.True(form.TopMost); Assert.Equal(FormWindowState.Normal, form.WindowState); - Assert.Equal(workingArea, form.Bounds); + Assert.Equal(screenBounds, form.Bounds); manager.ToggleFullScreen(); @@ -155,7 +153,7 @@ public void KioskModeManager_ToggleFullScreen_Form_RestoresExpected() } [WinFormsFact] - public void KioskModeManager_FullScreen_HideTaskbar_UsesScreenBounds() + public void KioskModeManager_FullScreen_CoversCompleteScreenBounds() { using Form form = new() { @@ -163,8 +161,7 @@ public void KioskModeManager_FullScreen_HideTaskbar_UsesScreenBounds() }; using KioskModeManager manager = new() { - ContainerControl = form, - HideTaskbar = true + ContainerControl = form }; Rectangle screenBounds = Screen.FromRectangle(form.Bounds).Bounds; @@ -185,8 +182,8 @@ public void KioskModeManager_FullScreen_MinimizedForm_UsesRestoreMonitor() form.Show(); form.WindowState = FormWindowState.Minimized; Rectangle restoreBounds = form.RestoreBounds; - Rectangle workingArea = Screen.FromRectangle( - restoreBounds).WorkingArea; + Rectangle screenBounds = Screen.FromRectangle( + restoreBounds).Bounds; using KioskModeManager manager = new() { ContainerControl = form @@ -194,32 +191,32 @@ public void KioskModeManager_FullScreen_MinimizedForm_UsesRestoreMonitor() manager.FullScreen = true; - Assert.Equal(workingArea, form.Bounds); + Assert.Equal(screenBounds, form.Bounds); } [WinFormsFact] - public void KioskModeManager_FullScreen_RefreshBounds_ReappliesWorkingArea() + public void KioskModeManager_FullScreen_RefreshBounds_ReappliesScreenBounds() { using Form form = new() { Bounds = new Rectangle(10, 20, 300, 200) }; - Rectangle workingArea = Screen.FromRectangle( - form.Bounds).WorkingArea; + Rectangle screenBounds = Screen.FromRectangle( + form.Bounds).Bounds; using KioskModeManager manager = new() { ContainerControl = form, FullScreen = true }; form.Bounds = new Rectangle( - workingArea.X + 10, - workingArea.Y + 10, + screenBounds.X + 10, + screenBounds.Y + 10, 300, 200); manager.TestAccessor.Dynamic.RefreshFullScreenBounds(); - Assert.Equal(workingArea, form.Bounds); + Assert.Equal(screenBounds, form.Bounds); } [WinFormsFact] @@ -630,7 +627,7 @@ public void KioskModeManager_BeginInit_FullScreenSet_DoesNotEnterUntilEndInit() using KioskModeManager manager = new(); ISupportInitialize supportInitialize = manager; - Rectangle workingArea = Screen.FromRectangle(form.Bounds).WorkingArea; + Rectangle screenBounds = Screen.FromRectangle(form.Bounds).Bounds; supportInitialize.BeginInit(); manager.ContainerControl = form; @@ -644,7 +641,7 @@ public void KioskModeManager_BeginInit_FullScreenSet_DoesNotEnterUntilEndInit() Assert.True(manager.FullScreen); Assert.Equal(FormBorderStyle.None, form.FormBorderStyle); Assert.Equal(FormWindowState.Normal, form.WindowState); - Assert.Equal(workingArea, form.Bounds); + Assert.Equal(screenBounds, form.Bounds); } [WinFormsFact] From 5113258dc800aa73a75484d7c11e7fa666421491 Mon Sep 17 00:00:00 2001 From: Klaus Loffelmann Date: Tue, 28 Jul 2026 20:29:05 -0700 Subject: [PATCH 10/10] Rename KioskModeManager properties per API review - ToggleFullScreenKey => ToggleFullScreenKeys, for consistency with the plurality of the Keys type. The property now matches the complete key combination, including modifiers, and Keys.None disables key based toggling. - SuppressPowerSaving => AlwaysOn. Also documents that the fullscreen keys are focus driven: they are evaluated only for input dispatched to the managed form or one of its child controls, and no global keyboard hook or system wide hot key is installed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8999e9ff-9bcc-4755-b337-7fc5496d431c --- .../PublicAPI.Unshipped.txt | 8 +- src/System.Windows.Forms/Resources/SR.resx | 10 +-- .../Resources/xlf/SR.cs.xlf | 16 ++-- .../Resources/xlf/SR.de.xlf | 16 ++-- .../Resources/xlf/SR.es.xlf | 16 ++-- .../Resources/xlf/SR.fr.xlf | 16 ++-- .../Resources/xlf/SR.it.xlf | 16 ++-- .../Resources/xlf/SR.ja.xlf | 16 ++-- .../Resources/xlf/SR.ko.xlf | 16 ++-- .../Resources/xlf/SR.pl.xlf | 16 ++-- .../Resources/xlf/SR.pt-BR.xlf | 16 ++-- .../Resources/xlf/SR.ru.xlf | 16 ++-- .../Resources/xlf/SR.tr.xlf | 16 ++-- .../Resources/xlf/SR.zh-Hans.xlf | 16 ++-- .../Resources/xlf/SR.zh-Hant.xlf | 16 ++-- .../KioskModeManager/KioskModeManager.cs | 81 +++++++++++-------- .../Windows/Forms/KioskModeManagerTests.cs | 77 +++++++++++++++++- 17 files changed, 236 insertions(+), 148 deletions(-) diff --git a/src/System.Windows.Forms/PublicAPI.Unshipped.txt b/src/System.Windows.Forms/PublicAPI.Unshipped.txt index 9e066bb6df8..9f5b0f876f2 100644 --- a/src/System.Windows.Forms/PublicAPI.Unshipped.txt +++ b/src/System.Windows.Forms/PublicAPI.Unshipped.txt @@ -110,6 +110,8 @@ virtual System.Windows.Forms.Control.OnVisualStylesModeChanged(System.EventArgs! virtual System.Windows.Forms.Control.VisualStylesMode.get -> System.Windows.Forms.VisualStylesMode virtual System.Windows.Forms.Control.VisualStylesMode.set -> void System.Windows.Forms.KioskModeManager +System.Windows.Forms.KioskModeManager.AlwaysOn.get -> bool +System.Windows.Forms.KioskModeManager.AlwaysOn.set -> void System.Windows.Forms.KioskModeManager.ContainerControl.get -> System.Windows.Forms.ContainerControl? System.Windows.Forms.KioskModeManager.ContainerControl.set -> void System.Windows.Forms.KioskModeManager.ContainerControlChanged -> System.EventHandler? @@ -127,10 +129,8 @@ virtual System.Windows.Forms.KioskModeManager.OnContainerControlChanged(System.E virtual System.Windows.Forms.KioskModeManager.OnFullScreenChanged(System.EventArgs! e) -> void override System.Windows.Forms.KioskModeManager.Site.get -> System.ComponentModel.ISite? override System.Windows.Forms.KioskModeManager.Site.set -> void -System.Windows.Forms.KioskModeManager.SuppressPowerSaving.get -> bool -System.Windows.Forms.KioskModeManager.SuppressPowerSaving.set -> void System.Windows.Forms.KioskModeManager.ToggleFullScreen() -> void -System.Windows.Forms.KioskModeManager.ToggleFullScreenKey.get -> System.Windows.Forms.Keys -System.Windows.Forms.KioskModeManager.ToggleFullScreenKey.set -> void +System.Windows.Forms.KioskModeManager.ToggleFullScreenKeys.get -> System.Windows.Forms.Keys +System.Windows.Forms.KioskModeManager.ToggleFullScreenKeys.set -> void System.Windows.Forms.KioskModeManager.TopMostInFullScreen.get -> bool System.Windows.Forms.KioskModeManager.TopMostInFullScreen.set -> void diff --git a/src/System.Windows.Forms/Resources/SR.resx b/src/System.Windows.Forms/Resources/SR.resx index d3886bb35e3..62f785c0944 100644 --- a/src/System.Windows.Forms/Resources/SR.resx +++ b/src/System.Windows.Forms/Resources/SR.resx @@ -2694,6 +2694,9 @@ To replace this default dialog please handle the DataError event. Manages common fullscreen, input, cursor, and power behavior for kiosk-style WinForms applications. + + Indicates whether Windows should keep the display and system awake while this component is active. + The container whose containing form is controlled by the kiosk mode manager. @@ -2712,11 +2715,8 @@ To replace this default dialog please handle the DataError event. The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. - - Indicates whether Windows should keep the display and system awake while this component is active. - - - The key that toggles between fullscreen and restored mode. + + The key combination that toggles between fullscreen and restored mode. Indicates whether the form is topmost while fullscreen. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.cs.xlf b/src/System.Windows.Forms/Resources/xlf/SR.cs.xlf index 1fe847a635f..d296de7a3a0 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.cs.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.cs.xlf @@ -6149,6 +6149,11 @@ Trasování zásobníku, kde došlo k neplatné operaci: Kombinace klíčů je neplatná. + + Indicates whether Windows should keep the display and system awake while this component is active. + Indicates whether Windows should keep the display and system awake while this component is active. + + Occurs when the ContainerControl property value changes. Occurs when the ContainerControl property value changes. @@ -6179,14 +6184,9 @@ Trasování zásobníku, kde došlo k neplatné operaci: The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. - - Indicates whether Windows should keep the display and system awake while this component is active. - Indicates whether Windows should keep the display and system awake while this component is active. - - - - The key that toggles between fullscreen and restored mode. - The key that toggles between fullscreen and restored mode. + + The key combination that toggles between fullscreen and restored mode. + The key combination that toggles between fullscreen and restored mode. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.de.xlf b/src/System.Windows.Forms/Resources/xlf/SR.de.xlf index 7d5b02b4823..530ac718fe5 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.de.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.de.xlf @@ -6149,6 +6149,11 @@ Stapelüberwachung, in der der unzulässige Vorgang auftrat: Ungültige Schlüsselkombination. + + Indicates whether Windows should keep the display and system awake while this component is active. + Indicates whether Windows should keep the display and system awake while this component is active. + + Occurs when the ContainerControl property value changes. Occurs when the ContainerControl property value changes. @@ -6179,14 +6184,9 @@ Stapelüberwachung, in der der unzulässige Vorgang auftrat: The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. - - Indicates whether Windows should keep the display and system awake while this component is active. - Indicates whether Windows should keep the display and system awake while this component is active. - - - - The key that toggles between fullscreen and restored mode. - The key that toggles between fullscreen and restored mode. + + The key combination that toggles between fullscreen and restored mode. + The key combination that toggles between fullscreen and restored mode. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.es.xlf b/src/System.Windows.Forms/Resources/xlf/SR.es.xlf index 01920c9afce..988fe2976c3 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.es.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.es.xlf @@ -6149,6 +6149,11 @@ El seguimiento de la pila donde tuvo lugar la operación no válida fue: La combinación de teclas no es válida. + + Indicates whether Windows should keep the display and system awake while this component is active. + Indicates whether Windows should keep the display and system awake while this component is active. + + Occurs when the ContainerControl property value changes. Occurs when the ContainerControl property value changes. @@ -6179,14 +6184,9 @@ El seguimiento de la pila donde tuvo lugar la operación no válida fue: The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. - - Indicates whether Windows should keep the display and system awake while this component is active. - Indicates whether Windows should keep the display and system awake while this component is active. - - - - The key that toggles between fullscreen and restored mode. - The key that toggles between fullscreen and restored mode. + + The key combination that toggles between fullscreen and restored mode. + The key combination that toggles between fullscreen and restored mode. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.fr.xlf b/src/System.Windows.Forms/Resources/xlf/SR.fr.xlf index 9858b7273b5..08335c39e44 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.fr.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.fr.xlf @@ -6149,6 +6149,11 @@ Cette opération non conforme s'est produite sur la trace de la pile : La combinaison de touches n'est pas valide. + + Indicates whether Windows should keep the display and system awake while this component is active. + Indicates whether Windows should keep the display and system awake while this component is active. + + Occurs when the ContainerControl property value changes. Occurs when the ContainerControl property value changes. @@ -6179,14 +6184,9 @@ Cette opération non conforme s'est produite sur la trace de la pile : The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. - - Indicates whether Windows should keep the display and system awake while this component is active. - Indicates whether Windows should keep the display and system awake while this component is active. - - - - The key that toggles between fullscreen and restored mode. - The key that toggles between fullscreen and restored mode. + + The key combination that toggles between fullscreen and restored mode. + The key combination that toggles between fullscreen and restored mode. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.it.xlf b/src/System.Windows.Forms/Resources/xlf/SR.it.xlf index 78a62927973..798017b6158 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.it.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.it.xlf @@ -6149,6 +6149,11 @@ Analisi dello stack dove si è verificata l'operazione non valida: Combinazione di tasti non valida. + + Indicates whether Windows should keep the display and system awake while this component is active. + Indicates whether Windows should keep the display and system awake while this component is active. + + Occurs when the ContainerControl property value changes. Occurs when the ContainerControl property value changes. @@ -6179,14 +6184,9 @@ Analisi dello stack dove si è verificata l'operazione non valida: The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. - - Indicates whether Windows should keep the display and system awake while this component is active. - Indicates whether Windows should keep the display and system awake while this component is active. - - - - The key that toggles between fullscreen and restored mode. - The key that toggles between fullscreen and restored mode. + + The key combination that toggles between fullscreen and restored mode. + The key combination that toggles between fullscreen and restored mode. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.ja.xlf b/src/System.Windows.Forms/Resources/xlf/SR.ja.xlf index b779ec46905..75b2c82c1b0 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.ja.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.ja.xlf @@ -6149,6 +6149,11 @@ Stack trace where the illegal operation occurred was: キーの組み合わせが有効ではありません。 + + Indicates whether Windows should keep the display and system awake while this component is active. + Indicates whether Windows should keep the display and system awake while this component is active. + + Occurs when the ContainerControl property value changes. Occurs when the ContainerControl property value changes. @@ -6179,14 +6184,9 @@ Stack trace where the illegal operation occurred was: The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. - - Indicates whether Windows should keep the display and system awake while this component is active. - Indicates whether Windows should keep the display and system awake while this component is active. - - - - The key that toggles between fullscreen and restored mode. - The key that toggles between fullscreen and restored mode. + + The key combination that toggles between fullscreen and restored mode. + The key combination that toggles between fullscreen and restored mode. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.ko.xlf b/src/System.Windows.Forms/Resources/xlf/SR.ko.xlf index 20578fdf6fe..048e1451032 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.ko.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.ko.xlf @@ -6149,6 +6149,11 @@ Stack trace where the illegal operation occurred was: 키 조합이 잘못되었습니다. + + Indicates whether Windows should keep the display and system awake while this component is active. + Indicates whether Windows should keep the display and system awake while this component is active. + + Occurs when the ContainerControl property value changes. Occurs when the ContainerControl property value changes. @@ -6179,14 +6184,9 @@ Stack trace where the illegal operation occurred was: The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. - - Indicates whether Windows should keep the display and system awake while this component is active. - Indicates whether Windows should keep the display and system awake while this component is active. - - - - The key that toggles between fullscreen and restored mode. - The key that toggles between fullscreen and restored mode. + + The key combination that toggles between fullscreen and restored mode. + The key combination that toggles between fullscreen and restored mode. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.pl.xlf b/src/System.Windows.Forms/Resources/xlf/SR.pl.xlf index 90d68c67913..5e707c725ca 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.pl.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.pl.xlf @@ -6149,6 +6149,11 @@ Stos śledzenia, w którym wystąpiła zabroniona operacja: Kombinacja kluczy jest nieprawidłowa. + + Indicates whether Windows should keep the display and system awake while this component is active. + Indicates whether Windows should keep the display and system awake while this component is active. + + Occurs when the ContainerControl property value changes. Occurs when the ContainerControl property value changes. @@ -6179,14 +6184,9 @@ Stos śledzenia, w którym wystąpiła zabroniona operacja: The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. - - Indicates whether Windows should keep the display and system awake while this component is active. - Indicates whether Windows should keep the display and system awake while this component is active. - - - - The key that toggles between fullscreen and restored mode. - The key that toggles between fullscreen and restored mode. + + The key combination that toggles between fullscreen and restored mode. + The key combination that toggles between fullscreen and restored mode. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.pt-BR.xlf b/src/System.Windows.Forms/Resources/xlf/SR.pt-BR.xlf index d0bbc52c5b1..ed1b1f0d8d7 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.pt-BR.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.pt-BR.xlf @@ -6149,6 +6149,11 @@ O rastreamento de pilha em que a operação ilegal ocorreu foi: A combinação de chave não é válida. + + Indicates whether Windows should keep the display and system awake while this component is active. + Indicates whether Windows should keep the display and system awake while this component is active. + + Occurs when the ContainerControl property value changes. Occurs when the ContainerControl property value changes. @@ -6179,14 +6184,9 @@ O rastreamento de pilha em que a operação ilegal ocorreu foi: The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. - - Indicates whether Windows should keep the display and system awake while this component is active. - Indicates whether Windows should keep the display and system awake while this component is active. - - - - The key that toggles between fullscreen and restored mode. - The key that toggles between fullscreen and restored mode. + + The key combination that toggles between fullscreen and restored mode. + The key combination that toggles between fullscreen and restored mode. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.ru.xlf b/src/System.Windows.Forms/Resources/xlf/SR.ru.xlf index ee2d66f9c63..72f0299cebc 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.ru.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.ru.xlf @@ -6149,6 +6149,11 @@ Stack trace where the illegal operation occurred was: Недопустимая комбинация ключа. + + Indicates whether Windows should keep the display and system awake while this component is active. + Indicates whether Windows should keep the display and system awake while this component is active. + + Occurs when the ContainerControl property value changes. Occurs when the ContainerControl property value changes. @@ -6179,14 +6184,9 @@ Stack trace where the illegal operation occurred was: The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. - - Indicates whether Windows should keep the display and system awake while this component is active. - Indicates whether Windows should keep the display and system awake while this component is active. - - - - The key that toggles between fullscreen and restored mode. - The key that toggles between fullscreen and restored mode. + + The key combination that toggles between fullscreen and restored mode. + The key combination that toggles between fullscreen and restored mode. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.tr.xlf b/src/System.Windows.Forms/Resources/xlf/SR.tr.xlf index dd387a2786f..32be056ef1f 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.tr.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.tr.xlf @@ -6149,6 +6149,11 @@ Geçersiz işlemin gerçekleştiği yığın izi: Anahtar birleşimi geçerli değil. + + Indicates whether Windows should keep the display and system awake while this component is active. + Indicates whether Windows should keep the display and system awake while this component is active. + + Occurs when the ContainerControl property value changes. Occurs when the ContainerControl property value changes. @@ -6179,14 +6184,9 @@ Geçersiz işlemin gerçekleştiği yığın izi: The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. - - Indicates whether Windows should keep the display and system awake while this component is active. - Indicates whether Windows should keep the display and system awake while this component is active. - - - - The key that toggles between fullscreen and restored mode. - The key that toggles between fullscreen and restored mode. + + The key combination that toggles between fullscreen and restored mode. + The key combination that toggles between fullscreen and restored mode. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.zh-Hans.xlf b/src/System.Windows.Forms/Resources/xlf/SR.zh-Hans.xlf index 4067e9599aa..c87262791cd 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.zh-Hans.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.zh-Hans.xlf @@ -6149,6 +6149,11 @@ Stack trace where the illegal operation occurred was: 组合键无效。 + + Indicates whether Windows should keep the display and system awake while this component is active. + Indicates whether Windows should keep the display and system awake while this component is active. + + Occurs when the ContainerControl property value changes. Occurs when the ContainerControl property value changes. @@ -6179,14 +6184,9 @@ Stack trace where the illegal operation occurred was: The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. - - Indicates whether Windows should keep the display and system awake while this component is active. - Indicates whether Windows should keep the display and system awake while this component is active. - - - - The key that toggles between fullscreen and restored mode. - The key that toggles between fullscreen and restored mode. + + The key combination that toggles between fullscreen and restored mode. + The key combination that toggles between fullscreen and restored mode. diff --git a/src/System.Windows.Forms/Resources/xlf/SR.zh-Hant.xlf b/src/System.Windows.Forms/Resources/xlf/SR.zh-Hant.xlf index 11a299a8763..bdb8cfaf821 100644 --- a/src/System.Windows.Forms/Resources/xlf/SR.zh-Hant.xlf +++ b/src/System.Windows.Forms/Resources/xlf/SR.zh-Hant.xlf @@ -6149,6 +6149,11 @@ Stack trace where the illegal operation occurred was: 組合鍵無效。 + + Indicates whether Windows should keep the display and system awake while this component is active. + Indicates whether Windows should keep the display and system awake while this component is active. + + Occurs when the ContainerControl property value changes. Occurs when the ContainerControl property value changes. @@ -6179,14 +6184,9 @@ Stack trace where the illegal operation occurred was: The delay, in milliseconds, before the mouse pointer is hidden while fullscreen. A value of 0 disables automatic pointer hiding. - - Indicates whether Windows should keep the display and system awake while this component is active. - Indicates whether Windows should keep the display and system awake while this component is active. - - - - The key that toggles between fullscreen and restored mode. - The key that toggles between fullscreen and restored mode. + + The key combination that toggles between fullscreen and restored mode. + The key combination that toggles between fullscreen and restored mode. diff --git a/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeManager.cs b/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeManager.cs index 8ac3df51da8..d28d652f226 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeManager.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Components/KioskModeManager/KioskModeManager.cs @@ -42,7 +42,7 @@ namespace System.Windows.Forms; /// ContainerControl = this, /// TopMostInFullScreen = true, /// MousePointerAutoHideDelay = 3000, -/// SuppressPowerSaving = true +/// AlwaysOn = true /// }; /// /// if (!_kioskModeManager.FullScreen) @@ -53,7 +53,7 @@ namespace System.Windows.Forms; /// } /// /// -[DefaultProperty(nameof(ToggleFullScreenKey))] +[DefaultProperty(nameof(ToggleFullScreenKeys))] [ToolboxItemFilter("System.Windows.Forms")] [SRDescription(nameof(SR.DescriptionKioskModeManager))] public class KioskModeManager : Component, ISupportInitialize @@ -83,9 +83,9 @@ public class KioskModeManager : Component, ISupportInitialize private bool _savedTopMost; private bool _topMostInFullScreen; - private Keys _toggleFullScreenKey = Keys.F11; + private Keys _toggleFullScreenKeys = Keys.F11; private bool _escapeExitsFullScreen = true; - private bool _suppressPowerSaving; + private bool _alwaysOn; private int _mousePointerAutoHideDelay; private HANDLE _powerRequestHandle; @@ -223,26 +223,35 @@ public bool TopMostInFullScreen } /// - /// Gets or sets the key that toggles fullscreen mode. + /// Gets or sets the key combination that toggles fullscreen mode. /// /// - /// A value that is handled without modifiers. The - /// default is . + /// A value that combines a key code with the modifiers that + /// must be pressed with it, or to disable key-based + /// toggling. The default is . /// + /// + /// + /// The key combination is evaluated only while the managed form or one of its + /// child controls has input focus. The component observes the application's own + /// message queue and never installs a global keyboard hook or registers a + /// system-wide hot key, so other applications keep receiving these keys. + /// + /// [SRCategory(nameof(SR.CatBehavior))] - [SRDescription(nameof(SR.KioskModeManagerToggleFullScreenKeyDescr))] + [SRDescription(nameof(SR.KioskModeManagerToggleFullScreenKeysDescr))] [DefaultValue(Keys.F11)] - public Keys ToggleFullScreenKey + public Keys ToggleFullScreenKeys { - get => _toggleFullScreenKey; + get => _toggleFullScreenKeys; set { - if (_toggleFullScreenKey == value) + if (_toggleFullScreenKeys == value) { return; } - _toggleFullScreenKey = value; + _toggleFullScreenKeys = value; } } @@ -255,6 +264,12 @@ public Keys ToggleFullScreenKey /// without modifiers; otherwise, . The default is /// . /// + /// + /// + /// Like , Escape is evaluated only while the + /// managed form or one of its child controls has input focus. + /// + /// [SRCategory(nameof(SR.CatBehavior))] [SRDescription(nameof(SR.KioskModeManagerEscapeExitsFullScreenDescr))] [DefaultValue(true)] @@ -293,20 +308,20 @@ public bool EscapeExitsFullScreen /// Windows could not create or activate the power request. /// [SRCategory(nameof(SR.CatBehavior))] - [SRDescription(nameof(SR.KioskModeManagerSuppressPowerSavingDescr))] + [SRDescription(nameof(SR.KioskModeManagerAlwaysOnDescr))] [DefaultValue(false)] - public bool SuppressPowerSaving + public bool AlwaysOn { - get => _suppressPowerSaving; + get => _alwaysOn; set { - if (_suppressPowerSaving == value) + if (_alwaysOn == value) { return; } - bool previousValue = _suppressPowerSaving; - _suppressPowerSaving = value; + bool previousValue = _alwaysOn; + _alwaysOn = value; try { @@ -314,7 +329,7 @@ public bool SuppressPowerSaving } catch { - _suppressPowerSaving = previousValue; + _alwaysOn = previousValue; throw; } } @@ -689,8 +704,10 @@ private void ClearParentChangedSubscriptions() private void EnsureMessageMonitoring() { - // IMessageFilter observes input for child controls without changing - // Form.KeyPreview or installing global keyboard/mouse hooks. + // IMessageFilter observes the input this application already receives, so keys + // stay focus driven: messages are only acted on when they target the managed + // form or one of its child controls. This changes neither Form.KeyPreview nor + // installs a global keyboard or mouse hook. if (_messageFilter is null) { _messageFilter = new KioskModeMessageFilter(this); @@ -744,7 +761,7 @@ private void UpdatePowerRequest() return; } - if (_suppressPowerSaving) + if (_alwaysOn) { CreatePowerRequest(); } @@ -787,21 +804,19 @@ private void OnMousePointerAutoHideTimerTick(object? sender, EventArgs e) } } - private void ProcessKeyboardActivity(Keys keyData, Keys modifiers, bool isRepeat) + private void ProcessKeyboardActivity(Keys keyData, bool isRepeat) { + // Key repeats must not toggle fullscreen mode again while the key is held down. if (isRepeat) { return; } - Keys keyCode = keyData & Keys.KeyCode; - - if (keyCode == _toggleFullScreenKey && modifiers == Keys.None) + if (_toggleFullScreenKeys != Keys.None && keyData == _toggleFullScreenKeys) { ToggleFullScreen(); } - else if (keyCode == Keys.Escape && modifiers == Keys.None - && _escapeExitsFullScreen && _isFullScreen) + else if (keyData == Keys.Escape && _escapeExitsFullScreen && _isFullScreen) { ExitFullScreen(); } @@ -926,10 +941,9 @@ private bool ProcessMessage(Message message) || message.MsgInternal == PInvokeCore.WM_SYSKEYDOWN) { bool isRepeat = ((nuint)(nint)message.LParamInternal & (1u << 30)) != 0; - ProcessKeyboardActivity( - (Keys)(nint)message.WParamInternal, - Control.ModifierKeys & Keys.Modifiers, - isRepeat); + Keys keyData = ((Keys)(nint)message.WParamInternal & Keys.KeyCode) + | (Control.ModifierKeys & Keys.Modifiers); + ProcessKeyboardActivity(keyData, isRepeat); } else if (message.MsgInternal >= PInvokeCore.WM_MOUSEFIRST && message.MsgInternal <= PInvokeCore.WM_MOUSELAST) @@ -941,7 +955,8 @@ private bool ProcessMessage(Message message) } /// - /// Observes keyboard and mouse messages that belong to the target form. + /// Observes keyboard and mouse messages that are dispatched to the target form + /// or to one of its child controls. /// private sealed class KioskModeMessageFilter : IMessageFilter { diff --git a/src/test/unit/System.Windows.Forms/System/Windows/Forms/KioskModeManagerTests.cs b/src/test/unit/System.Windows.Forms/System/Windows/Forms/KioskModeManagerTests.cs index bef6a210078..2172b886920 100644 --- a/src/test/unit/System.Windows.Forms/System/Windows/Forms/KioskModeManagerTests.cs +++ b/src/test/unit/System.Windows.Forms/System/Windows/Forms/KioskModeManagerTests.cs @@ -18,6 +18,7 @@ public void KioskModeManager_Ctor_Default() { using SubKioskModeManager manager = new(); + Assert.False(manager.AlwaysOn); Assert.Null(manager.Container); Assert.Null(manager.ContainerControl); Assert.False(manager.DesignMode); @@ -25,8 +26,7 @@ public void KioskModeManager_Ctor_Default() Assert.False(manager.FullScreen); Assert.Equal(0, manager.MousePointerAutoHideDelay); Assert.Null(manager.Site); - Assert.False(manager.SuppressPowerSaving); - Assert.Equal(Keys.F11, manager.ToggleFullScreenKey); + Assert.Equal(Keys.F11, manager.ToggleFullScreenKeys); Assert.False(manager.TopMostInFullScreen); } @@ -117,6 +117,79 @@ public void KioskModeManager_MousePointerAutoHideDelay_SetNegative_ThrowsArgumen Assert.Throws("value", () => manager.MousePointerAutoHideDelay = -1); } + [WinFormsTheory] + [InlineData(Keys.F11)] + [InlineData(Keys.None)] + [InlineData(Keys.Alt | Keys.Enter)] + [InlineData(Keys.Control | Keys.Shift | Keys.F)] + public void KioskModeManager_ToggleFullScreenKeys_Set_GetReturnsExpected(Keys value) + { + using KioskModeManager manager = new() + { + ToggleFullScreenKeys = value + }; + + Assert.Equal(value, manager.ToggleFullScreenKeys); + + manager.ToggleFullScreenKeys = value; + Assert.Equal(value, manager.ToggleFullScreenKeys); + } + + [WinFormsTheory] + [BoolData] + public void KioskModeManager_AlwaysOn_Set_GetReturnsExpected(bool value) + { + using KioskModeManager manager = new() + { + AlwaysOn = value + }; + + Assert.Equal(value, manager.AlwaysOn); + + manager.AlwaysOn = value; + Assert.Equal(value, manager.AlwaysOn); + + manager.AlwaysOn = !value; + Assert.Equal(!value, manager.AlwaysOn); + } + + [WinFormsTheory] + [InlineData(Keys.Alt | Keys.Enter)] + [InlineData(Keys.Control | Keys.Shift | Keys.F)] + public void KioskModeManager_ProcessKeyboardActivity_MatchingKeyCombination_TogglesFullScreen(Keys keys) + { + using Form form = new(); + using KioskModeManager manager = new() + { + ContainerControl = form, + ToggleFullScreenKeys = keys + }; + + manager.TestAccessor.Dynamic.ProcessKeyboardActivity(keys, false); + Assert.True(manager.FullScreen); + + manager.TestAccessor.Dynamic.ProcessKeyboardActivity(keys & Keys.KeyCode, false); + Assert.True(manager.FullScreen); + + manager.TestAccessor.Dynamic.ProcessKeyboardActivity(keys, false); + Assert.False(manager.FullScreen); + } + + [WinFormsFact] + public void KioskModeManager_ProcessKeyboardActivity_KeysNone_DoesNotToggleFullScreen() + { + using Form form = new(); + using KioskModeManager manager = new() + { + ContainerControl = form, + ToggleFullScreenKeys = Keys.None + }; + + manager.TestAccessor.Dynamic.ProcessKeyboardActivity(Keys.None, false); + + Assert.False(manager.FullScreen); + } + [WinFormsFact] public void KioskModeManager_ToggleFullScreen_Form_RestoresExpected() {