| File: System\Windows\Forms\Design\ToolStripContainerDesigner.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.Collections; using System.ComponentModel; using System.ComponentModel.Design; using System.Drawing; using System.Drawing.Design; using System.Windows.Forms.Design.Behavior; namespace System.Windows.Forms.Design; internal class ToolStripContainerDesigner : ParentControlDesigner { private ToolStripPanel? _topToolStripPanel; private ToolStripPanel? _bottomToolStripPanel; private ToolStripPanel? _leftToolStripPanel; private ToolStripPanel? _rightToolStripPanel; private ToolStripContentPanel? _contentToolStripPanel; private const string TopToolStripPanelName = "TopToolStripPanel"; private const string BottomToolStripPanelName = "BottomToolStripPanel"; private const string LeftToolStripPanelName = "LeftToolStripPanel"; private const string RightToolStripPanelName = "RightToolStripPanel"; private const string ContentToolStripPanelName = "ContentPanel"; private Control[]? _panels; private bool _disableDrawGrid; private ISelectionService? _selectionService; private ToolStripContainer? _toolStripContainer; /// <summary> /// The <see cref="IDesignerHost"/> that owns this designer. /// </summary> private IDesignerHost _designerHost => GetRequiredService<IDesignerHost>(); /// <summary> /// Shadow the <see cref="ToolStripContainer.TopToolStripPanelVisible"/> property at design-time /// so that we only set the visibility at design time if the user sets it directly. /// </summary> private bool TopToolStripPanelVisible { get => (bool)ShadowProperties[nameof(TopToolStripPanelVisible)]!; // This is first set in the Initialize method set { ShadowProperties[nameof(TopToolStripPanelVisible)] = value; ((ToolStripContainer)Component).TopToolStripPanelVisible = value; } } /// <summary> /// Shadow the <see cref="ToolStripContainer.LeftToolStripPanelVisible"/> property at design-time /// so that we only set the visibility at design time if the user sets it directly. /// </summary> private bool LeftToolStripPanelVisible { get => (bool)ShadowProperties[nameof(LeftToolStripPanelVisible)]!; // This is first set in the Initialize method set { ShadowProperties[nameof(LeftToolStripPanelVisible)] = value; ((ToolStripContainer)Component).LeftToolStripPanelVisible = value; } } /// <summary> /// Shadow the <see cref="ToolStripContainer.RightToolStripPanelVisible"/> property at design-time /// so that we only set the visibility at design time if the user sets it directly. /// </summary> private bool RightToolStripPanelVisible { get => (bool)ShadowProperties[nameof(RightToolStripPanelVisible)]!; // This is first set in the Initialize method set { ShadowProperties[nameof(RightToolStripPanelVisible)] = value; ((ToolStripContainer)Component).RightToolStripPanelVisible = value; } } /// <summary> /// Shadow the <see cref="ToolStripContainer.BottomToolStripPanelVisible"/> property at design-time /// so that we only set the visibility at design time if the user sets it directly. /// </summary> private bool BottomToolStripPanelVisible { get => (bool)ShadowProperties[nameof(BottomToolStripPanelVisible)]!; // This is first set in the Initialize method set { ShadowProperties[nameof(BottomToolStripPanelVisible)] = value; ((ToolStripContainer)Component).BottomToolStripPanelVisible = value; } } public override DesignerActionListCollection ActionLists { get { DesignerActionListCollection actions = []; // Here is our action list we'll use ToolStripContainerActionList actionList = new(_toolStripContainer!) { AutoShow = true }; actions.Add(actionList); return actions; } } /// <summary> /// The ToolStripContainerDesigner will re-parent any controls that are within it's lasso at /// creation time. /// </summary> protected override bool AllowControlLasso => false; protected override bool DrawGrid => !_disableDrawGrid && base.DrawGrid; public override IList SnapLines // We don't want padding SnapLines, so call directly to the internal method. => EdgeAndMarginSnapLines().Unwrap(); /// <summary> /// Returns the internal control designer with the specified index in the ControlDesigner. /// internalControlIndex is zero-based. /// </summary> public override ControlDesigner? InternalControlDesigner(int internalControlIndex) { if (_panels is null) { return null; } if (internalControlIndex >= _panels.Length || internalControlIndex < 0) { return null; } Control panel = _panels[internalControlIndex]; return _designerHost.GetDesigner(panel) as ControlDesigner; } /// <summary> /// We want those to come with in any cut, copy operations. /// </summary> public override ICollection AssociatedComponents { get { ArrayList components = []; foreach (Control parent in _toolStripContainer!.Controls) { foreach (Control control in parent.Controls) { components.Add(control); } } return components; } } protected override IComponent[]? CreateToolCore(ToolboxItem tool, int x, int y, int width, int height, bool hasLocation, bool hasSize) { if (tool is null) { return null; } Type? toolType = tool.GetType(_designerHost); if (typeof(StatusStrip).IsAssignableFrom(toolType)) { InvokeCreateTool(GetDesigner(_bottomToolStripPanel!), tool); } else if (typeof(ToolStrip).IsAssignableFrom(toolType)) { InvokeCreateTool(GetDesigner(_topToolStripPanel!), tool); } else { InvokeCreateTool(GetDesigner(_contentToolStripPanel!), tool); } return null; } public override bool CanParent(Control control) => false; protected override void Dispose(bool disposing) { base.Dispose(disposing); if (_selectionService is not null) { _selectionService = null; } } private ToolStripPanelDesigner? GetDesigner(ToolStripPanel panel) => _designerHost.GetDesigner(panel) as ToolStripPanelDesigner; private PanelDesigner? GetDesigner(ToolStripContentPanel panel) => _designerHost.GetDesigner(panel) as PanelDesigner; private static ToolStripContainer? ContainerParent(Control control) { if (control is null or ToolStripContainer) { return null; } while (control.Parent is not null) { if (control.Parent is ToolStripContainer parent) { return parent; } control = control.Parent; } return null; } protected override ControlBodyGlyph GetControlGlyph(GlyphSelectionType selectionType) { if (!TryGetService(out SelectionManager? selectionManager)) { return base.GetControlGlyph(selectionType); } // Create BodyGlyphs for all _panels for (int i = 0; i <= 4; i++) { Control currentPanel = _panels![i]; Rectangle translatedBounds = BehaviorService?.ControlRectInAdornerWindow(currentPanel) ?? Rectangle.Empty; ControlDesigner? panelDesigner = InternalControlDesigner(i); OnSetCursor(); if (panelDesigner is not null) { // create our glyph, and set its cursor appropriately ControlBodyGlyph bodyGlyph = new(translatedBounds, Cursor.Current, currentPanel, panelDesigner); selectionManager.BodyGlyphAdorner.Glyphs.Add(bodyGlyph); bool addGlyphs = true; ICollection selComponents = _selectionService!.GetSelectedComponents(); if (!_selectionService.GetComponentSelected(_toolStripContainer!)) { foreach (object comp in selComponents) { if (comp is Control control) { ToolStripContainer? container = ContainerParent(control); addGlyphs = container == _toolStripContainer; } } } if (addGlyphs) { if (panelDesigner is ToolStripPanelDesigner designer) { AddPanelSelectionGlyph(designer, selectionManager); } } } } return base.GetControlGlyph(selectionType); } private static Control? GetAssociatedControl(Component component) { Control? associatedControl = null; if (component is Control control) { return control; } if (component is ToolStripItem item) { Control? parent = item.GetCurrentParent(); parent ??= item.Owner; return parent; } return associatedControl; } private bool CheckDropDownBounds(ToolStripDropDownItem dropDownItem, Glyph childGlyph, GlyphCollection glyphs) { if (dropDownItem is null) { return false; } Rectangle glyphBounds = childGlyph.Bounds; Rectangle controlBounds = BehaviorService?.ControlRectInAdornerWindow(dropDownItem.DropDown) ?? Rectangle.Empty; if (!glyphBounds.IntersectsWith(controlBounds)) { glyphs.Insert(0, childGlyph); } return true; } /// <summary> /// Checks if the associated control bounds overlap the PanelSelectionGlyph bounds. /// </summary> private bool CheckAssociatedControl(Component component, Glyph childGlyph, GlyphCollection glyphs) { bool result = false; if (component is ToolStripDropDownItem item) { result = CheckDropDownBounds(item, childGlyph, glyphs); } if (!result) { Control? associatedControl = GetAssociatedControl(component); if (associatedControl is not null && _toolStripContainer is not null && associatedControl != _toolStripContainer && !PInvoke.IsChild(_toolStripContainer, associatedControl)) { Rectangle glyphBounds = childGlyph.Bounds; Rectangle controlBounds = BehaviorService?.ControlRectInAdornerWindow(associatedControl) ?? Rectangle.Empty; if ((component == _designerHost.RootComponent) || !glyphBounds.IntersectsWith(controlBounds)) { glyphs.Insert(0, childGlyph); } result = true; } } return result; } protected override Control? GetParentForComponent(IComponent component) { Type toolType = component.GetType(); if (typeof(StatusStrip).IsAssignableFrom(toolType)) { return _bottomToolStripPanel; } else if (typeof(ToolStrip).IsAssignableFrom(toolType)) { return _topToolStripPanel; } else { return _contentToolStripPanel; } } public override void Initialize(IComponent component) { _toolStripContainer = (ToolStripContainer)component; base.Initialize(component); AutoResizeHandles = true; _topToolStripPanel = _toolStripContainer.TopToolStripPanel; _bottomToolStripPanel = _toolStripContainer.BottomToolStripPanel; _leftToolStripPanel = _toolStripContainer.LeftToolStripPanel; _rightToolStripPanel = _toolStripContainer.RightToolStripPanel; _contentToolStripPanel = _toolStripContainer.ContentPanel; _panels = [_contentToolStripPanel, _leftToolStripPanel, _rightToolStripPanel, _topToolStripPanel, _bottomToolStripPanel]; // Add custom bitmaps for the child toolStripPanels. ToolboxBitmapAttribute bottomToolboxBitmapAttribute = new(typeof(ToolStripPanel), "ToolStripContainer_BottomToolStripPanel"); ToolboxBitmapAttribute rightToolboxBitmapAttribute = new(typeof(ToolStripPanel), "ToolStripContainer_RightToolStripPanel"); ToolboxBitmapAttribute topToolboxBitmapAttribute = new(typeof(ToolStripPanel), "ToolStripContainer_TopToolStripPanel"); ToolboxBitmapAttribute leftToolboxBitmapAttribute = new(typeof(ToolStripPanel), "ToolStripContainer_LeftToolStripPanel"); TypeDescriptor.AddAttributes(_bottomToolStripPanel, bottomToolboxBitmapAttribute, new DescriptionAttribute("bottom")); TypeDescriptor.AddAttributes(_rightToolStripPanel, rightToolboxBitmapAttribute, new DescriptionAttribute("right")); TypeDescriptor.AddAttributes(_leftToolStripPanel, leftToolboxBitmapAttribute, new DescriptionAttribute("left")); TypeDescriptor.AddAttributes(_topToolStripPanel, topToolboxBitmapAttribute, new DescriptionAttribute("top")); EnableDesignMode(_topToolStripPanel, TopToolStripPanelName); EnableDesignMode(_bottomToolStripPanel, BottomToolStripPanelName); EnableDesignMode(_leftToolStripPanel, LeftToolStripPanelName); EnableDesignMode(_rightToolStripPanel, RightToolStripPanelName); EnableDesignMode(_contentToolStripPanel, ContentToolStripPanelName); _selectionService ??= GetService<ISelectionService>(); if (_topToolStripPanel is not null) { ToolStripPanelDesigner? panelDesigner = _designerHost?.GetDesigner(_topToolStripPanel) as ToolStripPanelDesigner; panelDesigner?.ExpandTopPanel(); } // Set ShadowProperties TopToolStripPanelVisible = _toolStripContainer.TopToolStripPanelVisible; LeftToolStripPanelVisible = _toolStripContainer.LeftToolStripPanelVisible; RightToolStripPanelVisible = _toolStripContainer.RightToolStripPanelVisible; BottomToolStripPanelVisible = _toolStripContainer.BottomToolStripPanelVisible; } protected override void OnPaintAdornments(PaintEventArgs pe) { try { _disableDrawGrid = true; // we don't want to do this for the tab control designer // because you can't drag anything onto it anyway. // so we will always return false for draw grid. base.OnPaintAdornments(pe); } finally { _disableDrawGrid = false; } } /// <summary> /// Allows a designer to filter the set of properties /// the component it is designing will expose through the /// TypeDescriptor object. This method is called /// immediately before its corresponding "Post" method. /// If you are overriding this method you should call /// the base implementation before you perform your own /// filtering. /// </summary> protected override void PreFilterProperties(IDictionary properties) { base.PreFilterProperties(properties); // Handle shadowed properties string[] shadowProps = [ "TopToolStripPanelVisible", "LeftToolStripPanelVisible", "RightToolStripPanelVisible", "BottomToolStripPanelVisible" ]; Attribute[] empty = []; for (int i = 0; i < shadowProps.Length; i++) { PropertyDescriptor? propertyDescriptor = (PropertyDescriptor?)properties[shadowProps[i]]; if (propertyDescriptor is not null) { properties[shadowProps[i]] = TypeDescriptor.CreateProperty(typeof(ToolStripContainerDesigner), propertyDescriptor, empty); } } } private void AddPanelSelectionGlyph(ToolStripPanelDesigner designer, SelectionManager selectionManager) { if (designer is null || designer.GetGlyph() is not { } childGlyph || _selectionService is null) { return; } // Now create SelectionGlyph for the panel and add it ICollection selectedComponents = _selectionService.GetSelectedComponents(); foreach (object selectedComponent in selectedComponents) { Component component = (Component)selectedComponent; if (component is not null) { if (!CheckAssociatedControl(component, childGlyph, selectionManager.BodyGlyphAdorner.Glyphs)) { selectionManager.BodyGlyphAdorner.Glyphs.Insert(0, childGlyph); } } } } }