| File: System\ComponentModel\Design\DesignerActionUI.DesignerActionToolStripDropDown.cs | Web Access |
| Project: src\winforms\src\System.Windows.Forms.Design\src\System.Windows.Forms.Design.csproj (System.Windows.Forms.Design) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Drawing; using System.Windows.Forms; using System.Windows.Forms.Design.Behavior; namespace System.ComponentModel.Design; internal partial class DesignerActionUI { internal class DesignerActionToolStripDropDown : ToolStripDropDown { private readonly IWin32Window? _mainParentWindow; private ToolStripControlHost? _panel; private readonly DesignerActionUI _designerActionUI; private bool _cancelClose; private Glyph? _relatedGlyph; public DesignerActionToolStripDropDown(DesignerActionUI designerActionUI, IWin32Window? mainParentWindow) { _mainParentWindow = mainParentWindow; _designerActionUI = designerActionUI; } public DesignerActionPanel? CurrentPanel => _panel?.Control as DesignerActionPanel; // we're not topmost because we can show modal editors above us. protected override bool TopMost => false; public void UpdateContainerSize() { if (CurrentPanel is not null) { Size panelSize = CurrentPanel.GetPreferredSize(new Size(150, int.MaxValue)); if (CurrentPanel.Size == panelSize) { // If the panel size didn't actually change, we still have to force a call to PerformLayout to make // sure that controls get repositioned properly within the panel. The issue arises because we did a // measure-only Layout that determined some sizes, and then we end up painting with those values even // though there wasn't an actual Layout performed. CurrentPanel.PerformLayout(); } else { CurrentPanel.Size = panelSize; } ClientSize = panelSize; } } public void CheckFocusIsRight() { // fix to get the focus to NOT stay on ContainerControl HWND focusedControl = PInvoke.GetFocus(); if (focusedControl == Handle) { _panel?.Focus(); } focusedControl = PInvoke.GetFocus(); if (CurrentPanel is not null && CurrentPanel.Handle == focusedControl) { CurrentPanel.SelectNextControl(null, true, true, true, true); } } protected override void OnLayout(LayoutEventArgs levent) { base.OnLayout(levent); UpdateContainerSize(); } protected override void OnClosing(ToolStripDropDownClosingEventArgs e) { if (e.CloseReason == ToolStripDropDownCloseReason.AppFocusChange && _cancelClose) { _cancelClose = false; e.Cancel = true; } // When we get closing event as a result of an activation change, pre-populate e.Cancel based on why we're exiting. // - if it's a modal window that's owned by VS don't exit // - if it's a window that's owned by the toolstrip dropdown don't exit else if (e.CloseReason is ToolStripDropDownCloseReason.AppFocusChange or ToolStripDropDownCloseReason.AppClicked) { HWND hwndActivating = PInvoke.GetActiveWindow(); if (Handle == hwndActivating && e.CloseReason == ToolStripDropDownCloseReason.AppClicked) { e.Cancel = false; } else if (WindowOwnsWindow((HWND)Handle, hwndActivating)) { // We're being de-activated for someone owned by the panel. e.Cancel = true; } else if (_mainParentWindow is not null && !WindowOwnsWindow((HWND)_mainParentWindow.Handle, hwndActivating)) { if (IsWindowEnabled(_mainParentWindow.Handle)) { // The activated windows is not a child/owned windows of the main top level windows let toolstripdropdown handle this e.Cancel = false; } else { e.Cancel = true; } base.OnClosing(e); return; } // What's the owner of the windows being activated? HWND parent = (HWND)PInvokeCore.GetWindowLong( new HandleRef<HWND>(this, hwndActivating), WINDOW_LONG_PTR_INDEX.GWL_HWNDPARENT); // is it currently disabled (ie, the activating windows is in modal mode) if (!IsWindowEnabled(parent)) { // We are in a modal case e.Cancel = true; } } base.OnClosing(e); } public void SetDesignerActionPanel(DesignerActionPanel panel, Glyph relatedGlyph) { if (_panel is not null && panel == (DesignerActionPanel)_panel.Control) { return; } Debug.Assert(relatedGlyph is not null, "related glyph cannot be null"); _relatedGlyph = relatedGlyph; panel.SizeChanged += PanelResized; if (_panel is not null) { Items.Remove(_panel); _panel.Dispose(); _panel = null; } _panel = new ToolStripControlHost(panel) { // We don't want a margin Margin = Padding.Empty, Size = panel.Size }; SuspendLayout(); Size = panel.Size; Items.Add(_panel); ResumeLayout(); if (Visible) { CheckFocusIsRight(); } } private void PanelResized(object? sender, EventArgs e) { Control ctrl = (Control)sender!; if (Size.Width != ctrl.Size.Width || Size.Height != ctrl.Size.Height) { SuspendLayout(); Size = ctrl.Size; _panel?.Size = ctrl.Size; _designerActionUI.UpdateDAPLocation(component: null, _relatedGlyph as DesignerActionGlyph); ResumeLayout(); } } protected override void SetVisibleCore(bool visible) { base.SetVisibleCore(visible); if (visible) { CheckFocusIsRight(); } } /// <summary> /// Determines whether a given window (specified using native window handle) is a descendant of this control. /// This catches both contained descendants and 'owned' windows such as modal dialogs. Using window handles /// rather than Control objects allows it to catch un-managed windows as well. /// </summary> private static bool WindowOwnsWindow(HWND hWndOwner, HWND hWndDescendant) { if (hWndDescendant == hWndOwner) { return true; } while (!hWndDescendant.IsNull) { hWndDescendant = (HWND)PInvokeCore.GetWindowLong(hWndDescendant, WINDOW_LONG_PTR_INDEX.GWL_HWNDPARENT); if (hWndDescendant.IsNull) { return false; } if (hWndDescendant == hWndOwner) { return true; } } return false; } private bool IsWindowEnabled(IntPtr handle) { int style = (int)PInvokeCore.GetWindowLong(this, WINDOW_LONG_PTR_INDEX.GWL_STYLE); return (style & (int)WINDOW_STYLE.WS_DISABLED) == 0; } private void WmActivate(ref Message m) { if ((nint)m.WParamInternal == PInvoke.WA_INACTIVE) { HWND hwndActivating = (HWND)m.LParamInternal; _cancelClose = WindowOwnsWindow((HWND)Handle, hwndActivating); } else { _cancelClose = false; } base.WndProc(ref m); } protected override void WndProc(ref Message m) { switch (m.MsgInternal) { case PInvokeCore.WM_ACTIVATE: WmActivate(ref m); return; } base.WndProc(ref m); } protected override bool ProcessDialogKey(Keys keyData) { // since we're not hosted in a form we need to do the same logic as Form.cs. // If we get an enter key we need to find the current focused control. // if it's a button, we click it and return that we handled the message if (keyData == Keys.Enter) { HWND focusedControlPtr = PInvoke.GetFocus(); Control? focusedControl = FromChildHandle(focusedControlPtr); if (focusedControl is IButtonControl button) { button.PerformClick(); return true; } } return base.ProcessDialogKey(keyData); } } }