| File: System\Windows\Forms\Controls\ToolStrips\ToolStripPanelCell.cs | Web Access |
| Project: src\winforms\src\System.Windows.Forms\System.Windows.Forms.csproj (System.Windows.Forms) |
// 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.Drawing; using System.Globalization; using System.Windows.Forms.Layout; namespace System.Windows.Forms; /// this class is a container for toolstrips on a rafting row. /// you can set layout styles on this container all day long and not /// affect the underlying toolstrip's properties.... so if its /// removed from a rafting container its still got its defaults /// set up for it. internal sealed class ToolStripPanelCell : ArrangedElement { private ToolStrip _wrappedToolStrip; private ToolStripPanelRow? _parent; private Size _maxSize = LayoutUtils.s_maxSize; private bool _currentlySizing; private bool _currentlyDragging; private bool _restoreOnVisibleChanged; private Rectangle _cachedBounds = Rectangle.Empty; #if DEBUG #pragma warning disable IDE0052 // Remove unread private members private readonly string _cellID; #pragma warning restore IDE0052 [ThreadStatic] private static int t_cellCount; #endif public ToolStripPanelCell(Control control) : this(parent: null, control) { } public ToolStripPanelCell(ToolStripPanelRow? parent, Control control) { #if DEBUG // Ensure 1:1 Cell/ToolStripPanel mapping _cellID = $"{control.Name}.{++t_cellCount}"; #endif ToolStripPanelRow = parent; ArgumentNullException.ThrowIfNull(control); if (control is not ToolStrip toolStrip) { throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, string.Format(SR.TypedControlCollectionShouldBeOfType, nameof(ToolStrip))), control.GetType().Name); } _wrappedToolStrip = toolStrip; CommonProperties.SetAutoSize(this, true); _wrappedToolStrip.LocationChanging += OnToolStripLocationChanging; _wrappedToolStrip.VisibleChanged += OnToolStripVisibleChanged; } public Rectangle CachedBounds { get { return _cachedBounds; } set { _cachedBounds = value; Debug.Assert(_cachedBounds.X >= 0 && _cachedBounds.Y >= 0, "cached bounds are outside of the client area, investigate"); } } public Control Control { get { return _wrappedToolStrip; } } // ToolStripPanelCell is considered to be Visible if the wrapped control is InDesignMode. // This property is accessed from the ToolStripPanelRow in cases where we are moving the toolStrip around // during a drag operation. public bool ControlInDesignMode { get { return (_wrappedToolStrip is not null && _wrappedToolStrip.IsInDesignMode); } } public IArrangedElement InnerElement { get { return _wrappedToolStrip; } } public ISupportToolStripPanel DraggedControl { get { return _wrappedToolStrip; } } public ToolStripPanelRow? ToolStripPanelRow { get { return _parent; } set { if (_parent != value) { if (_parent is not null) { ((IList)_parent.Cells).Remove(this); } _parent = value; Margin = Padding.Empty; } } } public override bool Visible { get { if (Control is not null && ToolStripPanelRow is not null && Control.ParentInternal == ToolStripPanelRow.ToolStripPanel) { return InnerElement.ParticipatesInLayout; } return false; } set { Control.Visible = value; } } public Size MaximumSize { get { return _maxSize; } } public override LayoutEngine LayoutEngine { get { return DefaultLayout.Instance; } } protected override IArrangedElement? GetContainer() { return _parent; } public int Grow(int growBy) { if (ToolStripPanelRow is not null && ToolStripPanelRow.Orientation == Orientation.Vertical) { return GrowVertical(growBy); } else { return GrowHorizontal(growBy); } } private int GrowVertical(int growBy) { // Grow ---] // Pref [ ] // Max [ ] if (MaximumSize.Height >= Control.PreferredSize.Height) { // nothing to grow. return 0; } // Grow ---] // Pref [ ] // Max [ ] if (MaximumSize.Height + growBy >= Control.PreferredSize.Height) { int freed = Control.PreferredSize.Height - MaximumSize.Height; _maxSize = LayoutUtils.s_maxSize; return freed; } // Grow ---] // Pref [ ] // Max [ ] if (MaximumSize.Height + growBy < Control.PreferredSize.Height) { _maxSize.Height += growBy; return growBy; } return 0; } private int GrowHorizontal(int growBy) { // Grow ---] // Pref [ ] // Max [ ] if (MaximumSize.Width >= Control.PreferredSize.Width) { // nothing to grow. return 0; } // Grow ---] // Pref [ ] // Max [ ] if (MaximumSize.Width + growBy >= Control.PreferredSize.Width) { int freed = Control.PreferredSize.Width - MaximumSize.Width; _maxSize = LayoutUtils.s_maxSize; return freed; } // Grow ---] // Pref [ ] // Max [ ] if (MaximumSize.Width + growBy < Control.PreferredSize.Width) { _maxSize.Width += growBy; return growBy; } return 0; } protected override void Dispose(bool disposing) { try { if (disposing) { if (_wrappedToolStrip is not null) { #if DEBUG t_cellCount--; #endif _wrappedToolStrip.LocationChanging -= OnToolStripLocationChanging; _wrappedToolStrip.VisibleChanged -= OnToolStripVisibleChanged; } _wrappedToolStrip = null!; if (_parent is not null) { ((IList)_parent.Cells).Remove(this); } _parent = null; } #if DEBUG else { t_cellCount--; } #endif } finally { base.Dispose(disposing); } } protected override ArrangedElementCollection GetChildren() { return ArrangedElementCollection.Empty; } public override Size GetPreferredSize(Size constrainingSize) { ISupportToolStripPanel draggedControl = DraggedControl; Size preferredSize; if (draggedControl.Stretch && ToolStripPanelRow is not null) { if (ToolStripPanelRow.Orientation == Orientation.Horizontal) { constrainingSize.Width = ToolStripPanelRow.Bounds.Width; preferredSize = _wrappedToolStrip.GetPreferredSize(constrainingSize); preferredSize.Width = constrainingSize.Width; } else { constrainingSize.Height = ToolStripPanelRow.Bounds.Height; preferredSize = _wrappedToolStrip.GetPreferredSize(constrainingSize); preferredSize.Height = constrainingSize.Height; } } else { preferredSize = (!_wrappedToolStrip.AutoSize) ? _wrappedToolStrip.Size : _wrappedToolStrip.GetPreferredSize(constrainingSize); } // return LayoutUtils.IntersectSizes(constrainingSize, preferredSize); return preferredSize; } protected override void SetBoundsCore(Rectangle bounds, BoundsSpecified specified) { _currentlySizing = true; CachedBounds = bounds; try { if (ToolStripPanelRow is null) { _currentlySizing = false; return; } if (DraggedControl.IsCurrentlyDragging) { if (ToolStripPanelRow.Cells[ToolStripPanelRow.Cells.Count - 1] == this) { Rectangle displayRectangle = ToolStripPanelRow.DisplayRectangle; if (ToolStripPanelRow.Orientation == Orientation.Horizontal) { int spaceToFree = bounds.Right - displayRectangle.Right; if (spaceToFree > 0 && bounds.Width > spaceToFree) { bounds.Width -= spaceToFree; } } else { int spaceToFree = bounds.Bottom - displayRectangle.Bottom; if (spaceToFree > 0 && bounds.Height > spaceToFree) { bounds.Height -= spaceToFree; } } } base.SetBoundsCore(bounds, specified); InnerElement.SetBounds(bounds, specified); } else { if (!ToolStripPanelRow.CachedBoundsMode) { base.SetBoundsCore(bounds, specified); InnerElement.SetBounds(bounds, specified); } } } finally { _currentlySizing = false; } } /// <summary> /// New EventHandler for The LocationChanging so that ToolStripPanelCell Listens to the Location Property on the /// ToolStrips being changed. The ToolStrip needs to Raft (Join) to the appropriate Location Depending on the /// new Location w.r.t to the oldLocation ... Hence the need for this event listener. /// </summary> private void OnToolStripLocationChanging(object? sender, ToolStripLocationCancelEventArgs e) { if (ToolStripPanelRow is null) { return; } if (!_currentlySizing && !_currentlyDragging) { try { _currentlyDragging = true; Point newloc = e.NewLocation; // detect if we haven't yet performed a layout - force one so we can // properly join to the row. if (ToolStripPanelRow.Bounds == Rectangle.Empty) { ToolStripPanelRow.ToolStripPanel.PerformUpdate(true); } if (_wrappedToolStrip is not null) { ToolStripPanelRow.ToolStripPanel.Join(_wrappedToolStrip, newloc); } } finally { _currentlyDragging = false; e.Cancel = true; } } } private void OnToolStripVisibleChanged(object? sender, EventArgs e) { if (_wrappedToolStrip is not null && !_wrappedToolStrip.IsInDesignMode && !_wrappedToolStrip.IsCurrentlyDragging && !_wrappedToolStrip.IsDisposed // ensure we have a live-runtime only toolstrip. && !_wrappedToolStrip.Disposing) { // Rejoin the row when visibility is toggled. // we don't want to do this logic at DT, as the DropSourceBehavior // will set the toolstrip visible = false. if (!Control.Visible) { // if we are becoming visible = false, remember if we were in a toolstrippanelrow at the time. _restoreOnVisibleChanged = (ToolStripPanelRow is not null && ((IList)ToolStripPanelRow.Cells).Contains(this)); } else if (_restoreOnVisibleChanged) { try { // if we are becoming visible = true, and we ARE in a toolstrippanelrow, rejoin. if (ToolStripPanelRow is not null && ((IList)ToolStripPanelRow.Cells).Contains(this)) { ToolStripPanelRow.ToolStripPanel.Join(_wrappedToolStrip, _wrappedToolStrip.Location); } } finally { _restoreOnVisibleChanged = false; } } } } }