// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Specialized;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms.Layout;
using System.Windows.Forms.VisualStyles;
using Windows.Win32.Graphics.Dwm;
using Windows.Win32.System.Threading;
using Windows.Win32.UI.Accessibility;
namespace System.Windows.Forms;
///
/// Represents a window or dialog box that makes up an application's user interface.
///
[ToolboxItemFilter("System.Windows.Forms.Control.TopLevel")]
[ToolboxItem(false)]
[DesignTimeVisible(false)]
[Designer($"System.Windows.Forms.Design.FormDocumentDesigner, {Assemblies.SystemDesign}", typeof(IRootDesigner))]
[DefaultEvent(nameof(Load))]
[InitializationEvent(nameof(Load))]
[DesignerCategory("Form")]
public partial class Form : ContainerControl
{
private static readonly object s_activatedEvent = new();
private static readonly object s_closingEvent = new();
private static readonly object s_closedEvent = new();
private static readonly object s_formClosingEvent = new();
private static readonly object s_formClosedEvent = new();
private static readonly object s_deactivateEvent = new();
private static readonly object s_loadEvent = new();
private static readonly object s_mdiChildActivateEvent = new();
private static readonly object s_inputLanguageChangeEvent = new();
private static readonly object s_inputLanguageChangeRequestEvent = new();
private static readonly object s_menuStartEvent = new();
private static readonly object s_menuCompleteEvent = new();
private static readonly object s_maximumSizeChangedEvent = new();
private static readonly object s_minimumSizeChangedEvent = new();
private static readonly object s_helpButtonChangedEvent = new();
private static readonly object s_shownEvent = new();
private static readonly object s_resizeBeginEvent = new();
private static readonly object s_resizeEndEvent = new();
private static readonly object s_rightToLeftLayoutChangedEvent = new();
private static readonly object s_dpiChangedEvent = new();
private static readonly object s_formBorderColorChanged = new();
private static readonly object s_formCaptionBackColorChanged = new();
private static readonly object s_formCaptionTextColorChanged = new();
private static readonly object s_formCornerPreferenceChanged = new();
//
// The following flags should be used with formState[..] not formStateEx[..]
// Don't add any more sections to this vector, it is already full.
//
private static readonly BitVector32.Section s_formStateAllowTransparency = BitVector32.CreateSection(1);
private static readonly BitVector32.Section s_formStateBorderStyle = BitVector32.CreateSection(6, s_formStateAllowTransparency);
private static readonly BitVector32.Section s_formStateTaskBar = BitVector32.CreateSection(1, s_formStateBorderStyle);
private static readonly BitVector32.Section s_formStateControlBox = BitVector32.CreateSection(1, s_formStateTaskBar);
private static readonly BitVector32.Section s_formStateKeyPreview = BitVector32.CreateSection(1, s_formStateControlBox);
private static readonly BitVector32.Section s_formStateLayered = BitVector32.CreateSection(1, s_formStateKeyPreview);
private static readonly BitVector32.Section s_formStateMaximizeBox = BitVector32.CreateSection(1, s_formStateLayered);
private static readonly BitVector32.Section s_formStateMinimizeBox = BitVector32.CreateSection(1, s_formStateMaximizeBox);
private static readonly BitVector32.Section s_formStateHelpButton = BitVector32.CreateSection(1, s_formStateMinimizeBox);
private static readonly BitVector32.Section s_formStateStartPos = BitVector32.CreateSection(4, s_formStateHelpButton);
private static readonly BitVector32.Section s_formStateWindowState = BitVector32.CreateSection(2, s_formStateStartPos);
private static readonly BitVector32.Section s_formStateShowWindowOnCreate = BitVector32.CreateSection(1, s_formStateWindowState);
private static readonly BitVector32.Section s_formStateAutoScaling = BitVector32.CreateSection(1, s_formStateShowWindowOnCreate);
private static readonly BitVector32.Section s_formStateSetClientSize = BitVector32.CreateSection(1, s_formStateAutoScaling);
private static readonly BitVector32.Section s_formStateTopMost = BitVector32.CreateSection(1, s_formStateSetClientSize);
private static readonly BitVector32.Section s_formStateSWCalled = BitVector32.CreateSection(1, s_formStateTopMost);
private static readonly BitVector32.Section s_formStateMdiChildMax = BitVector32.CreateSection(1, s_formStateSWCalled);
private static readonly BitVector32.Section s_formStateRenderSizeGrip = BitVector32.CreateSection(1, s_formStateMdiChildMax);
private static readonly BitVector32.Section s_formStateSizeGripStyle = BitVector32.CreateSection(2, s_formStateRenderSizeGrip);
private static readonly BitVector32.Section s_formStateIsWindowActivated = BitVector32.CreateSection(1, s_formStateSizeGripStyle);
private static readonly BitVector32.Section s_formStateIsTextEmpty = BitVector32.CreateSection(1, s_formStateIsWindowActivated);
private static readonly BitVector32.Section s_formStateIsActive = BitVector32.CreateSection(1, s_formStateIsTextEmpty);
private static readonly BitVector32.Section s_formStateIconSet = BitVector32.CreateSection(1, s_formStateIsActive);
// The following flags should be used with formStateEx[...] not formState[..]
private static readonly BitVector32.Section s_formStateExCalledClosing = BitVector32.CreateSection(1);
private static readonly BitVector32.Section s_formStateExUpdateMenuHandlesSuspendCount = BitVector32.CreateSection(8, s_formStateExCalledClosing);
private static readonly BitVector32.Section s_formStateExUpdateMenuHandlesDeferred = BitVector32.CreateSection(1, s_formStateExUpdateMenuHandlesSuspendCount);
private static readonly BitVector32.Section s_formStateExUseMdiChildProc = BitVector32.CreateSection(1, s_formStateExUpdateMenuHandlesDeferred);
private static readonly BitVector32.Section s_formStateExCalledOnLoad = BitVector32.CreateSection(1, s_formStateExUseMdiChildProc);
private static readonly BitVector32.Section s_formStateExCalledMakeVisible = BitVector32.CreateSection(1, s_formStateExCalledOnLoad);
private static readonly BitVector32.Section s_formStateExCalledCreateControl = BitVector32.CreateSection(1, s_formStateExCalledMakeVisible);
private static readonly BitVector32.Section s_formStateExAutoSize = BitVector32.CreateSection(1, s_formStateExCalledCreateControl);
private static readonly BitVector32.Section s_formStateExInUpdateMdiControlStrip = BitVector32.CreateSection(1, s_formStateExAutoSize);
private static readonly BitVector32.Section s_formStateExShowIcon = BitVector32.CreateSection(1, s_formStateExInUpdateMdiControlStrip);
private static readonly BitVector32.Section s_formStateExMnemonicProcessed = BitVector32.CreateSection(1, s_formStateExShowIcon);
private static readonly BitVector32.Section s_formStateExInScale = BitVector32.CreateSection(1, s_formStateExMnemonicProcessed);
private static readonly BitVector32.Section s_formStateExInModalSizingLoop = BitVector32.CreateSection(1, s_formStateExInScale);
private static readonly BitVector32.Section s_formStateExSettingAutoScale = BitVector32.CreateSection(1, s_formStateExInModalSizingLoop);
private static readonly BitVector32.Section s_formStateExWindowBoundsWidthIsClientSize = BitVector32.CreateSection(1, s_formStateExSettingAutoScale);
private static readonly BitVector32.Section s_formStateExWindowBoundsHeightIsClientSize = BitVector32.CreateSection(1, s_formStateExWindowBoundsWidthIsClientSize);
private static readonly BitVector32.Section s_formStateExWindowClosing = BitVector32.CreateSection(1, s_formStateExWindowBoundsHeightIsClientSize);
private const int SizeGripSize = 16;
private static Icon? s_defaultIcon;
private static readonly Lock s_internalSyncObject = new();
// Property store keys for properties. The property store allocates most efficiently
// in groups of four, so we try to lump properties in groups of four based on how
// likely they are going to be used in a group.
private static readonly int s_propAcceptButton = PropertyStore.CreateKey();
private static readonly int s_propCancelButton = PropertyStore.CreateKey();
private static readonly int s_propDefaultButton = PropertyStore.CreateKey();
private static readonly int s_propDialogOwner = PropertyStore.CreateKey();
private static readonly int s_propOwner = PropertyStore.CreateKey();
private static readonly int s_propOwnedForms = PropertyStore.CreateKey();
private static readonly int s_propMaximizedBounds = PropertyStore.CreateKey();
private static readonly int s_propMinTrackSize = PropertyStore.CreateKey();
private static readonly int s_propMaxTrackSize = PropertyStore.CreateKey();
private static readonly int s_propFormMdiParent = PropertyStore.CreateKey();
private static readonly int s_propActiveMdiChild = PropertyStore.CreateKey();
private static readonly int s_propFormerlyActiveMdiChild = PropertyStore.CreateKey();
private static readonly int s_propMdiChildFocusable = PropertyStore.CreateKey();
private static readonly int s_propDummyMdiMenu = PropertyStore.CreateKey();
private static readonly int s_propMainMenuStrip = PropertyStore.CreateKey();
private static readonly int s_propMdiWindowListStrip = PropertyStore.CreateKey();
private static readonly int s_propMdiControlStrip = PropertyStore.CreateKey();
private static readonly int s_propOpacity = PropertyStore.CreateKey();
private static readonly int s_propTransparencyKey = PropertyStore.CreateKey();
private static readonly int s_propFormCornerPreference = PropertyStore.CreateKey();
private static readonly int s_propFormBorderColor = PropertyStore.CreateKey();
private static readonly int s_propFormCaptionTextColor = PropertyStore.CreateKey();
private static readonly int s_propFormCaptionBackColor = PropertyStore.CreateKey();
private static readonly int s_propFormScreenCaptureMode = PropertyStore.CreateKey();
// Form per instance members
// Note: Do not add anything to this list unless absolutely necessary.
private BitVector32 _formState = new(0x21338); // magic value... all the defaults... see the ctor for details...
private BitVector32 _formStateEx;
private Icon? _icon;
private Icon? _smallIcon;
private Size _autoScaleBaseSize = Size.Empty;
private Size _minAutoSize = Size.Empty;
private Rectangle _restoredWindowBounds = new(-1, -1, -1, -1);
private BoundsSpecified _restoredWindowBoundsSpecified;
private DialogResult _dialogResult;
private MdiClient? _ctlClient;
private NativeWindow? _ownerWindow;
private bool _rightToLeftLayout;
private Rectangle _restoreBounds = new(-1, -1, -1, -1);
private CloseReason _closeReason = CloseReason.None;
private VisualStyleRenderer? _sizeGripRenderer;
// Cache Form's size for the DPI. When Form is moved between the monitors with different DPI settings, we use
// cached values to set the size matching the DPI on the Form instead of recalculating the size again. This help
// preventing rounding error in size calculations with float DPI factor and rounding it to nearest integer.
private Dictionary? _dpiFormSizes;
private bool _processingDpiChanged;
private bool _inRecreateHandle;
private TaskCompletionSource? _nonModalFormCompletion;
private TaskCompletionSource? _modalFormCompletion;
private readonly Lock _lock = new();
///
/// Initializes a new instance of the class.
///
public Form() : base()
{
// Assert section.
Debug.Assert(_formState[s_formStateAllowTransparency] == 0, "Failed to set formState[FormStateAllowTransparency]");
Debug.Assert(_formState[s_formStateBorderStyle] == (int)FormBorderStyle.Sizable, "Failed to set formState[FormStateBorderStyle]");
Debug.Assert(_formState[s_formStateTaskBar] == 1, "Failed to set formState[FormStateTaskBar]");
Debug.Assert(_formState[s_formStateControlBox] == 1, "Failed to set formState[FormStateControlBox]");
Debug.Assert(_formState[s_formStateKeyPreview] == 0, "Failed to set formState[FormStateKeyPreview]");
Debug.Assert(_formState[s_formStateLayered] == 0, "Failed to set formState[FormStateLayered]");
Debug.Assert(_formState[s_formStateMaximizeBox] == 1, "Failed to set formState[FormStateMaximizeBox]");
Debug.Assert(_formState[s_formStateMinimizeBox] == 1, "Failed to set formState[FormStateMinimizeBox]");
Debug.Assert(_formState[s_formStateHelpButton] == 0, "Failed to set formState[FormStateHelpButton]");
Debug.Assert(_formState[s_formStateStartPos] == (int)FormStartPosition.WindowsDefaultLocation, "Failed to set formState[FormStateStartPos]");
Debug.Assert(_formState[s_formStateWindowState] == (int)FormWindowState.Normal, "Failed to set formState[FormStateWindowState]");
Debug.Assert(_formState[s_formStateShowWindowOnCreate] == 0, "Failed to set formState[FormStateShowWindowOnCreate]");
Debug.Assert(_formState[s_formStateAutoScaling] == 1, "Failed to set formState[FormStateAutoScaling]");
Debug.Assert(_formState[s_formStateSetClientSize] == 0, "Failed to set formState[FormStateSetClientSize]");
Debug.Assert(_formState[s_formStateTopMost] == 0, "Failed to set formState[FormStateTopMost]");
Debug.Assert(_formState[s_formStateSWCalled] == 0, "Failed to set formState[FormStateSWCalled]");
Debug.Assert(_formState[s_formStateMdiChildMax] == 0, "Failed to set formState[FormStateMdiChildMax]");
Debug.Assert(_formState[s_formStateRenderSizeGrip] == 0, "Failed to set formState[FormStateRenderSizeGrip]");
Debug.Assert(_formState[s_formStateSizeGripStyle] == 0, "Failed to set formState[FormStateSizeGripStyle]");
Debug.Assert(_formState[s_formStateIsWindowActivated] == 0, "Failed to set formState[FormStateIsWindowActivated]");
Debug.Assert(_formState[s_formStateIsTextEmpty] == 0, "Failed to set formState[FormStateIsTextEmpty]");
Debug.Assert(_formState[s_formStateIsActive] == 0, "Failed to set formState[FormStateIsActive]");
Debug.Assert(_formState[s_formStateIconSet] == 0, "Failed to set formState[FormStateIconSet]");
_formStateEx[s_formStateExShowIcon] = 1;
SetState(States.Visible, false);
SetState(States.TopLevel, true);
}
///
/// Indicates the control on the form that is clicked when
/// the user presses the ENTER key.
///
[DefaultValue(null)]
[SRDescription(nameof(SR.FormAcceptButtonDescr))]
public IButtonControl? AcceptButton
{
get => Properties.GetValueOrDefault(s_propAcceptButton);
set
{
if (Properties.AddOrRemoveValue(s_propAcceptButton, value) != value)
{
UpdateDefaultButton();
}
}
}
///
/// Retrieves true if this form is currently active.
///
internal bool Active
{
get
{
Form? parentForm = ParentForm;
if (parentForm is null)
{
return _formState[s_formStateIsActive] != 0;
}
return parentForm.ActiveControl == this && parentForm.Active;
}
set
{
if (_formState[s_formStateIsActive] != 0 == value)
{
return;
}
if (value && !CanRecreateHandle())
{
return;
}
_formState[s_formStateIsActive] = value ? 1 : 0;
if (value)
{
_formState[s_formStateIsWindowActivated] = 1;
// Check if validation has been canceled to avoid raising Validation event multiple times.
if (!ValidationCancelled)
{
if (ActiveControl is null)
{
// If no control is selected focus will go to form
SelectNextControl(null, true, true, true, false);
}
InnerMostActiveContainerControl.FocusActiveControlInternal();
}
OnActivated(EventArgs.Empty);
}
else
{
_formState[s_formStateIsWindowActivated] = 0;
OnDeactivate(EventArgs.Empty);
}
}
}
///
/// Gets the currently active form for this application.
///
public static Form? ActiveForm => FromHandle(PInvokeCore.GetForegroundWindow()) as Form;
///
///
/// Gets the currently active multiple document interface (MDI) child window.
/// Note: Don't use this property internally, use ActiveMdiChildInternal instead (see comments below).
///
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[SRDescription(nameof(SR.FormActiveMDIChildDescr))]
public Form? ActiveMdiChild
{
get
{
Form? mdiChild = ActiveMdiChildInternal;
// We keep the active mdi child in the cached in the property store; when changing its value
// (due to a change to one of the following properties/methods: Visible, Enabled, Active, Show/Hide,
// Focus() or as the result of WM_SETFOCUS/WM_ACTIVATE/WM_MDIACTIVATE) we temporarily set it to null
// (to properly handle menu merging among other things) rendering the cache out-of-date; the problem
// arises when the user has an event handler that is raised during this process; in that case we ask
// Windows for it (see ActiveMdiChildFromWindows).
if (mdiChild is null)
{
// If this.MdiClient is not null it means this.IsMdiContainer == true.
if (_ctlClient is not null && _ctlClient.IsHandleCreated)
{
IntPtr hwnd = PInvokeCore.SendMessage(_ctlClient, PInvokeCore.WM_MDIGETACTIVE);
mdiChild = FromHandle(hwnd) as Form;
}
}
if (mdiChild is not null && mdiChild.Visible && mdiChild.Enabled)
{
return mdiChild;
}
return null;
}
}
///
/// Property to be used internally. See comments a on ActiveMdiChild property.
///
internal Form? ActiveMdiChildInternal
{
get => Properties.GetValueOrDefault