| File: Microsoft\Windows\Controls\Ribbon\Primitives\RibbonMenuItemsPanel.cs | Web Access |
| Project: src\wpf\src\Microsoft.DotNet.Wpf\src\System.Windows.Controls.Ribbon\System.Windows.Controls.Ribbon.csproj (System.Windows.Controls.Ribbon) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Windows.Controls.Primitives; using System.Collections.Generic; using MS.Internal; using Microsoft.Windows.Controls; #if RIBBON_IN_FRAMEWORK namespace System.Windows.Controls.Ribbon.Primitives #else namespace Microsoft.Windows.Controls.Ribbon.Primitives #endif { public class RibbonMenuItemsPanel : VirtualizingStackPanel, ISupportStarLayout { #region Constructor static RibbonMenuItemsPanel() { OrientationProperty.OverrideMetadata(typeof(RibbonMenuItemsPanel), new FrameworkPropertyMetadata(Orientation.Vertical)); } #endregion #region ISupportStarLayout Members public void RegisterStarLayoutProvider(IProvideStarLayoutInfoBase starLayoutInfoProvider) { ArgumentNullException.ThrowIfNull(starLayoutInfoProvider); if (!_registeredStarLayoutProviders.Contains(starLayoutInfoProvider)) { _registeredStarLayoutProviders.Add(starLayoutInfoProvider); InvalidateMeasure(); } } public void UnregisterStarLayoutProvider(IProvideStarLayoutInfoBase starLayoutInfoProvider) { ArgumentNullException.ThrowIfNull(starLayoutInfoProvider); if (_registeredStarLayoutProviders.Contains(starLayoutInfoProvider)) { _registeredStarLayoutProviders.Remove(starLayoutInfoProvider); InvalidateMeasure(); } } public bool IsStarLayoutPass { get { return _isStarLayout; } } #endregion #region Protected Methods protected override void OnIsItemsHostChanged(bool oldIsItemsHost, bool newIsItemsHost) { base.OnIsItemsHostChanged(oldIsItemsHost, newIsItemsHost); ItemsControl itemsControl = ParentItemsControl; RibbonMenuButton menuButtonParent = itemsControl as RibbonMenuButton; RibbonMenuItem menuItemParent = itemsControl as RibbonMenuItem; // ParentItemsControl should be either RibbonMenuButton or RibbonMenuItem if (menuButtonParent != null || menuItemParent != null) { if (newIsItemsHost) { IItemContainerGenerator generator = itemsControl.ItemContainerGenerator as IItemContainerGenerator; if (generator != null && generator.GetItemContainerGeneratorForPanel(this) == generator) { if (menuButtonParent != null) { menuButtonParent.InternalItemsHost = this; } else { menuItemParent?.InternalItemsHost = this; } } } else { if (menuButtonParent != null && menuButtonParent.InternalItemsHost == this) { menuButtonParent.InternalItemsHost = null; } else if (menuItemParent != null && menuItemParent.InternalItemsHost == this) { menuItemParent.InternalItemsHost = null; } } } } protected override Size MeasureOverride(Size availableSize) { InitializeLayoutOnStars(); Size baseDesiredSize = base.MeasureOverride(availableSize); // If there are no starLayoutProviders then return desiredSize calculated by VSP. if (_registeredStarLayoutProviders.Count == 0) { return baseDesiredSize; } Size desiredSize = new Size(); double maxChildWidth = 0.0, totalChildHeight = 0.0; // First pass: All children are Auto sized by the previous Measure // Gallery understands AutoLayout pass // and its DesiredSize is just the width required to display its MinColumnCount // and MinHeight required to display atleast 1 row foreach (UIElement child in InternalChildren) { if (child.DesiredSize.Width > maxChildWidth) { maxChildWidth = child.DesiredSize.Width; } totalChildHeight += child.DesiredSize.Height; } desiredSize.Width = maxChildWidth; desiredSize.Height = totalChildHeight; // Cache the MinSize computed in the Auto-pass. // this value is consumed by resizing logic to prevent resizing less than the MinSize. if (double.IsInfinity(availableSize.Width)) { _cachedAutoSize = new Size(maxChildWidth, totalChildHeight); } else { // If not infinity then stretch to all the available width maxChildWidth = Math.Max(availableSize.Width, maxChildWidth); } // 2nd pass: StarLayout pass // Iterate through registered StarLayoutProviders (aka galleries) // and remeasure them with maxWidth,remainingHeight calculated in 1st pass. _isStarLayout = true; double remainingHeight = availableSize.Height - totalChildHeight; InitializeLayoutOnStars(); if (_registeredStarLayoutProviders.Count > 0) { // Divide remaining height equally among all galleries. Even if there // is no remaining space it is important to rerun the star layout pass // so that the ScrollBars for the galleries can be enabled. Otherwise // that wont happen because the galleries were measured to infinity // in the auto pass. double surplusHeight = Math.Max(remainingHeight, 0.0) / _registeredStarLayoutProviders.Count; List<IProvideStarLayoutInfoBase> InvalidateRegisteredLayoutProvidersList = new List<IProvideStarLayoutInfoBase>(); ; foreach (IProvideStarLayoutInfoBase starLayoutInfoProvider in _registeredStarLayoutProviders) { UIElement starLayoutTarget = starLayoutInfoProvider.TargetElement; if (!InternalChildren.Contains(starLayoutTarget)) { InvalidateRegisteredLayoutProvidersList.Add(starLayoutInfoProvider); starLayoutTarget = null; } if (starLayoutTarget != null) { // Remeasure with surplusHeight added desiredSize.Height -= starLayoutTarget.DesiredSize.Height; double availableHeight = starLayoutTarget.DesiredSize.Height + surplusHeight; starLayoutTarget.Measure(new Size(maxChildWidth, availableHeight)); desiredSize.Width = Math.Max(starLayoutTarget.DesiredSize.Width, desiredSize.Width); desiredSize.Height += starLayoutTarget.DesiredSize.Height; } } // Invalidating _registeredStarLayoutProviders as some star provider Targets children has been removed from the Panel if (InvalidateRegisteredLayoutProvidersList.Count > 0) { for (int i = 0; i < InvalidateRegisteredLayoutProvidersList.Count; i++) { if (_registeredStarLayoutProviders.Contains(InvalidateRegisteredLayoutProvidersList[i])) { UnregisterStarLayoutProvider(InvalidateRegisteredLayoutProvidersList[i]); } } } } _isStarLayout = false; return desiredSize; } protected override Size ArrangeOverride(Size finalSize) { if (_registeredStarLayoutProviders.Count == 0) { return base.ArrangeOverride(finalSize); } double totalDesiredHeight = 0.0; double remainingHeight = 0.0, surplusHeight = 0.0; UIElementCollection children = InternalChildren; // First pass: Sum up each child's DesiredSize and calculate the surplus Height for (int i = 0; i < children.Count; i++) { totalDesiredHeight += children[i].DesiredSize.Height; } // Divide remainingHeight equally among starLayoutProviders remainingHeight = finalSize.Height - totalDesiredHeight; HashSet<UIElement> starLayoutTargets = GetStarLayoutProviderTargets(); if (DoubleUtil.GreaterThan(remainingHeight, 0.0) && starLayoutTargets.Count > 0) { surplusHeight = remainingHeight / starLayoutTargets.Count; } // Second pass. Arrange each child double startY = 0.0; for (int i = 0; i < children.Count; i++) { UIElement child = children[i]; if (starLayoutTargets.Contains(child)) { // if the child is a StarLayoutProvider, give it the surplusHeight. double availableHeight = child.DesiredSize.Height + surplusHeight; child.Arrange(new Rect(0, startY, finalSize.Width, availableHeight)); startY += availableHeight; } else { child.Arrange(new Rect(0.0, startY, finalSize.Width, child.DesiredSize.Height)); startY += child.DesiredSize.Height; } } return finalSize; } /// <summary> /// Calls OnInitializeLayout on each of the registered StarLayoutProviders /// </summary> private void InitializeLayoutOnStars() { foreach (IProvideStarLayoutInfoBase starProvider in _registeredStarLayoutProviders) { starProvider.OnInitializeLayout(); } } #endregion #region Internal Methods internal void BringIndexIntoViewInternal(int index) { base.BringIndexIntoView(index); } private HashSet<UIElement> GetStarLayoutProviderTargets() { HashSet<UIElement> targets = new HashSet<UIElement>(); foreach (IProvideStarLayoutInfoBase starProvider in _registeredStarLayoutProviders) { UIElement starLayoutTarget = starProvider.TargetElement; if (starLayoutTarget != null) { targets.Add(starLayoutTarget); } } return targets; } #endregion #if IN_RIBBON_GALLERY #region InRibbonGallery internal void RemoveFirstGallery(InRibbonGallery irg) { for (int i = 0; i < InternalChildren.Count; i++) { if (InternalChildren[i].Equals(irg.FirstGallery)) { Debug.Assert(_firstRibbonGalleryReInsertIndex == -1); RemoveInternalChildRange(i, 1); _firstRibbonGalleryReInsertIndex = i; break; } } } internal void ReInsertFirstGallery(InRibbonGallery irg) { RibbonGallery firstGallery = irg.FirstGallery; if (firstGallery != null && _firstRibbonGalleryReInsertIndex >= 0) { Debug.Assert(_firstRibbonGalleryReInsertIndex <= InternalChildren.Count); InsertInternalChild(_firstRibbonGalleryReInsertIndex, firstGallery); _firstRibbonGalleryReInsertIndex = -1; } } // Used in the InRibbonGallery scenario to remember the index of a plucked RibbonGallery. private int _firstRibbonGalleryReInsertIndex = -1; #endregion #endif /// <summary> /// The parent ItemsControl /// </summary> private ItemsControl ParentItemsControl { get { return TreeHelper.FindTemplatedAncestor<ItemsControl>(this); } } internal Size CachedAutoSize { get { return _cachedAutoSize; } } private bool _isStarLayout; private WeakHashSet<IProvideStarLayoutInfoBase> _registeredStarLayoutProviders = new WeakHashSet<IProvideStarLayoutInfoBase>(); private Size _cachedAutoSize = new Size(); } }