File: Microsoft\Windows\Controls\Ribbon\Primitives\RibbonGroupItemsPanel.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\System.Windows.Controls.Ribbon\System.Windows.Controls.Ribbon_dxtfdo3u_wpftmp.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.
// See the LICENSE file in the project root for more information.
 
using System.Collections.Generic;
using MS.Internal;
#if RIBBON_IN_FRAMEWORK
using Microsoft.Windows.Controls;
 
#if RIBBON_IN_FRAMEWORK
namespace System.Windows.Controls.Ribbon.Primitives
#else
namespace Microsoft.Windows.Controls.Ribbon.Primitives
#endif
{
#endif
 
    /// <summary>
    ///   The default panel for the RibbonGroup class.
    /// </summary>
    public class RibbonGroupItemsPanel : Panel, IProvideStarLayoutInfo, IContainsStarLayoutManager
    {
        #region Constructor
 
        public RibbonGroupItemsPanel()
        {
            Unloaded += new RoutedEventHandler(OnRibbonGroupItemsPanelUnloaded);
        }
 
        #endregion
 
        #region Protected Methods
 
        protected override Size MeasureOverride(Size availableSize)
        {
            Size desiredSize = new Size();
            IContainsStarLayoutManager iContainsStarLayoutManager = (IContainsStarLayoutManager)this;
 
            RibbonHelper.InitializeStarLayoutManager(this);
            bool isStarLayoutPass = (iContainsStarLayoutManager.StarLayoutManager == null ? false : iContainsStarLayoutManager.StarLayoutManager.IsStarLayoutPass);
 
            if (!isStarLayoutPass)
            {
                desiredSize = NonStarPassMeasure(availableSize);
            }
            else
            {
                desiredSize = StarMeasurePass(availableSize);
            }
 
            return desiredSize;
        }
 
        protected override Size ArrangeOverride(Size finalSize)
        {
            UIElementCollection children = InternalChildren;
            int childCount = children.Count;
            double remainingHeightInColumn = finalSize.Height;
            double columnWidth = 0;
            double currentX = 0;
            double starLayoutCombinationCount = _starLayoutCombinations.Count;
            for (int i = 0; i < childCount; i++)
            {
                UIElement child = children[i];
                Size childDesiredSize = child.DesiredSize;
                if (DoubleUtil.GreaterThan(childDesiredSize.Height, remainingHeightInColumn))
                {
                    currentX += columnWidth;
                    columnWidth = childDesiredSize.Width;
                    if (IsStarChild(child))
                    {
                        if (_childIndexToStarLayoutIndexMap.ContainsKey(i))
                        {
                            int starLayoutIndex = _childIndexToStarLayoutIndexMap[i];
                            if (starLayoutIndex < starLayoutCombinationCount)
                            {
                                StarLayoutInfo starLayoutInfo = _starLayoutCombinations[starLayoutIndex];
                                columnWidth = starLayoutInfo.AllocatedStarWidth;
                            }
                        }
                    }
                    child.Arrange(new Rect(currentX, 0, columnWidth, childDesiredSize.Height));
                    remainingHeightInColumn = Math.Max(0, finalSize.Height - childDesiredSize.Height);
                }
                else
                {
                    double arrangeWidth = child.DesiredSize.Width;
                    if (IsStarChild(child))
                    {
                        if (_childIndexToStarLayoutIndexMap.ContainsKey(i))
                        {
                            int starLayoutIndex = _childIndexToStarLayoutIndexMap[i];
                            if (starLayoutIndex < starLayoutCombinationCount)
                            {
                                if (CanChildStretch(child))
                                {
                                    // If the child's HorizontalAlignment is Left/Right, then
                                    // use its desired width as arrange width.
                                    StarLayoutInfo starLayoutInfo = _starLayoutCombinations[starLayoutIndex];
                                    arrangeWidth = starLayoutInfo.AllocatedStarWidth;
                                }
                            }
                        }
                    }
                    columnWidth = Math.Max(columnWidth, arrangeWidth);
                    child.Arrange(new Rect(currentX, (finalSize.Height - remainingHeightInColumn), arrangeWidth, childDesiredSize.Height));
                    remainingHeightInColumn -= childDesiredSize.Height;
                }
            }
            return finalSize;
        }
 
        #endregion
 
        #region Private Methods
 
        private void OnRibbonGroupItemsPanelUnloaded(object sender, RoutedEventArgs e)
        {
            IContainsStarLayoutManager iContainsStarLayoutManager = (IContainsStarLayoutManager)this;
            if (iContainsStarLayoutManager.StarLayoutManager != null)
            {
                iContainsStarLayoutManager.StarLayoutManager.UnregisterStarLayoutProvider(this);
                iContainsStarLayoutManager.StarLayoutManager = null;
            }
        }
 
        private static double GetStarChildMinWidth(FrameworkElement child, ref double maxStarColumnWidth)
        {
            if (child == null)
            {
                return 0;
            }
            if (CanChildStretch(child))
            {
                // If the child can stretch then use child.MaxWidth
                maxStarColumnWidth = Math.Max(maxStarColumnWidth, child.MaxWidth);
            }
            else
            {
                // If the child cannot stretch then use child.DesiredWidth for MaxWidth.
                maxStarColumnWidth = Math.Max(maxStarColumnWidth, Math.Min(child.MaxWidth, child.DesiredSize.Width));
            }
            return child.MinWidth;
        }
 
        private static bool IsStarChild(UIElement child)
        {
            double weight;
            return IsStarChild(child, out weight);
        }
 
        private static bool IsStarChild(UIElement child, out double weight)
        {
            weight = 0;
            RibbonControlSizeDefinition controlDef = RibbonControlService.GetControlSizeDefinition(child);
            if (controlDef != null)
            {
                weight = controlDef.Width.Value;
                return controlDef.Width.IsStar;
            }
            return false;
        }
 
        private void CreateStarLayoutCombination(Size constraint,
            double starWeight,
            double starMinWidth,
            double starMaxWidth,
            List<int> starChildIndices)
        {
            if (starChildIndices.Count > 0)
            {
                StarLayoutInfo starLayoutInfo = new StarLayoutInfo()
                {
                    RequestedStarMinWidth = starMinWidth,
                    RequestedStarMaxWidth = starMaxWidth,
                    RequestedStarWeight = starWeight
                };
                _starLayoutCombinations.Add(starLayoutInfo);
 
                UIElementCollection children = InternalChildren;
                int starLayoutIndex = _starLayoutCombinations.Count - 1;
                foreach (int starChildIndex in starChildIndices)
                {
                    _childIndexToStarLayoutIndexMap[starChildIndex] = starLayoutIndex;
                    children[starChildIndex].Measure(new Size(starLayoutInfo.RequestedStarMinWidth, constraint.Height));
                }
                starChildIndices.Clear();
            }
        }
 
        private Size NonStarPassMeasure(Size availableSize)
        {
            UIElementCollection children = InternalChildren;
            int childCount = children.Count;
            double desiredWidth = 0;
            double desiredHeight = 0;
            double columnWidth = 0;
            double columnHeight = 0;
            double maxStarColumnWidth = 0;
            double starWeight = 0;
            List<int> starChildIndices = new List<int>();
 
            _starLayoutCombinations.Clear();
            _childIndexToStarLayoutIndexMap.Clear();
 
            for (int i = 0; i < childCount; i++)
            {
                UIElement child = children[i];
                child.Measure(availableSize);
                Size childDesiredSize = child.DesiredSize;
 
                if (DoubleUtil.GreaterThan(columnHeight + childDesiredSize.Height, availableSize.Height))
                {
                    // When switching to next column, create a star layout combination
                    // for previous column if needed.
                    CreateStarLayoutCombination(availableSize,
                        starWeight,
                        columnWidth,
                        maxStarColumnWidth,
                        starChildIndices);
 
                    starWeight = 0;
                    desiredHeight = Math.Min(Math.Max(desiredHeight, columnHeight), availableSize.Height);
                    columnHeight = childDesiredSize.Height;
                    desiredWidth += columnWidth;
                    double currentWeight = 0;
                    if (IsStarChild(child, out currentWeight))
                    {
                        starChildIndices.Add(i);
                        starWeight += currentWeight;
                        maxStarColumnWidth = 0;
                        columnWidth = GetStarChildMinWidth(child as FrameworkElement, ref maxStarColumnWidth);
                    }
                    else
                    {
                        columnWidth = childDesiredSize.Width;
                        maxStarColumnWidth = columnWidth;
                    }
                }
                else
                {
                    columnHeight += childDesiredSize.Height;
                    double currentWeight = 0;
                    if (IsStarChild(child, out currentWeight))
                    {
                        starWeight += currentWeight;
                        starChildIndices.Add(i);
                        columnWidth = Math.Max(columnWidth, GetStarChildMinWidth(child as FrameworkElement, ref maxStarColumnWidth));
                    }
                    else
                    {
                        columnWidth = Math.Max(columnWidth, childDesiredSize.Width);
                        maxStarColumnWidth = Math.Max(maxStarColumnWidth, columnWidth);
                    }
                }
            }
            desiredWidth += columnWidth;
            desiredHeight = Math.Min(Math.Max(desiredHeight, columnHeight), availableSize.Height);
            CreateStarLayoutCombination(availableSize,
                        starWeight,
                        columnWidth,
                        maxStarColumnWidth,
                        starChildIndices);
            return new Size(desiredWidth, desiredHeight);
        }
 
        private Size StarMeasurePass(Size availableSize)
        {
            Size originalDesiredSize = DesiredSize;
            UIElementCollection children = InternalChildren;
            int childCount = children.Count;
            int starLayoutCombinationCount = _starLayoutCombinations.Count;
            for (int i = 0; i < childCount; i++)
            {
                UIElement child = children[i];
                if (IsStarChild(child))
                {
                    if (_childIndexToStarLayoutIndexMap.ContainsKey(i))
                    {
                        int starLayoutIndex = _childIndexToStarLayoutIndexMap[i];
                        if (starLayoutIndex < starLayoutCombinationCount)
                        {
                            StarLayoutInfo starLayoutInfo = _starLayoutCombinations[starLayoutIndex];
                            child.Measure(new Size(starLayoutInfo.AllocatedStarWidth, availableSize.Height));
                        }
                    }
                }
            }
 
            double desiredWidth = originalDesiredSize.Width;
            AdjustDesiredWidthForStars(children, ref desiredWidth);
 
            return new Size(desiredWidth, originalDesiredSize.Height);
        }
 
        /// <summary>
        ///     Adjust the desired width for all the star
        ///     based children based upon their
        ///     allocated/desired width.
        /// </summary>
        private void AdjustDesiredWidthForStars(UIElementCollection children, ref double desiredWidth)
        {
            int oldStarLayoutIndex = -1;
            List<int> columnStarChildren = null;
            int childCount = children.Count;
            for (int i = 0; i < childCount; i++)
            {
                UIElement child = children[i];
                if (IsStarChild(child) &&
                    _childIndexToStarLayoutIndexMap.ContainsKey(i))
                {
                    int starLayoutIndex = _childIndexToStarLayoutIndexMap[i];
                    if (starLayoutIndex != oldStarLayoutIndex)
                    {
                        // star layout index for all star children in a column
                        // should be same. Hence using that to determine the
                        // star based controls of a column.
                        AdjustDesiredWidthForStarColumn(children, columnStarChildren, oldStarLayoutIndex, ref desiredWidth);
                        if (oldStarLayoutIndex == -1)
                        {
                            AddItemToList(i, ref columnStarChildren);
                        }
                        oldStarLayoutIndex = starLayoutIndex;
                    }
                    else
                    {
                        AddItemToList(i, ref columnStarChildren);
                    }
                }
            }
            AdjustDesiredWidthForStarColumn(children,
                columnStarChildren,
                oldStarLayoutIndex,
                ref desiredWidth);
        }
 
        /// <summary>
        ///     Adjust the desired width for all the star
        ///     based children in a column based upon their
        ///     allocated/desired width.
        /// </summary>
        private void AdjustDesiredWidthForStarColumn(UIElementCollection children, List<int> columnStarChildren, int starLayoutIndex, ref double desiredWidth)
        {
            if (columnStarChildren != null && columnStarChildren.Count > 0 && starLayoutIndex >= 0)
            {
                bool foundStretchableStar = false;
                double columnDesiredWidth = 0;
                foreach (int columnChildIndex in columnStarChildren)
                {
                    UIElement child = children[columnChildIndex];
                    if (CanChildStretch(child))
                    {
                        foundStretchableStar = true;
                        break;
                    }
                    else
                    {
                        columnDesiredWidth = Math.Max(columnDesiredWidth, child.DesiredSize.Width);
                    }
                }
                if (foundStretchableStar)
                {
                    // if there are any non-item based star children
                    // then desired width should be equal to that of
                    // allocated width.
                    StarLayoutInfo starLayoutInfo = _starLayoutCombinations[starLayoutIndex];
                    desiredWidth += (starLayoutInfo.AllocatedStarWidth - starLayoutInfo.RequestedStarMinWidth);
                }
                else
                {
                    // if there are no non-item based star children
                    // then desired width should be equal to that of
                    // computed desired width.
                    StarLayoutInfo starLayoutInfo = _starLayoutCombinations[starLayoutIndex];
                    desiredWidth += Math.Max((columnDesiredWidth - starLayoutInfo.RequestedStarMinWidth), 0);
                }
                columnStarChildren.Clear();
            }
        }
 
        private static bool CanChildStretch(UIElement child)
        {
            // Gets the content child of RibbonControl and
            // determines if its horizontal alignment is
            // center or stretch. It is considered to be so
            // by default.
            RibbonControl ribbonControl = child as RibbonControl;
            if (ribbonControl != null)
            {
                UIElement contentChild = ribbonControl.ContentChild;
                if (contentChild != null)
                {
                    HorizontalAlignment childAlignment = (HorizontalAlignment)contentChild.GetValue(HorizontalAlignmentProperty);
                    if (childAlignment == HorizontalAlignment.Left ||
                        childAlignment == HorizontalAlignment.Right)
                    {
                        return false;
                    }
                }
            }
            return true;
        }
 
        /// <summary>
        ///     Helper method to instantiate the list
        ///     if needed and then add the item to it.
        /// </summary>
        private static void AddItemToList<T>(T item, ref List<T> list)
        {
            if (list == null)
            {
                list = new List<T>();
            }
            list.Add(item);
        }
 
        #endregion
 
        #region IProvideStarLayoutInfo Members
 
        public IEnumerable<StarLayoutInfo> StarLayoutCombinations
        {
            get { return _starLayoutCombinations; }
        }
 
        public void OnStarSizeAllocationCompleted()
        {
            TreeHelper.InvalidateMeasureForVisualAncestorPath(this, RibbonHelper.IsISupportStarLayout);
        }
 
        public UIElement TargetElement
        {
            get
            {
                FrameworkElement itemsPresenter = TemplatedParent as FrameworkElement;
                if (itemsPresenter != null)
                {
                    return itemsPresenter.TemplatedParent as UIElement;
                }
 
                return null;
            }
        }
 
        public void OnInitializeLayout()
        {
            IContainsStarLayoutManager iContainsStarLayoutManager = (IContainsStarLayoutManager)this;
            if (iContainsStarLayoutManager.StarLayoutManager != null && !iContainsStarLayoutManager.StarLayoutManager.IsStarLayoutPass)
            {
                TreeHelper.InvalidateMeasureForVisualAncestorPath(this, RibbonHelper.IsISupportStarLayout);
            }
        }
 
        #endregion
 
        #region IContainsStarLayoutManager Members
 
        ISupportStarLayout IContainsStarLayoutManager.StarLayoutManager
        {
            get;
            set;
        }
 
        #endregion
 
        #region Private Data
 
        List<StarLayoutInfo> _starLayoutCombinations = new List<StarLayoutInfo>();
        Dictionary<int, int> _childIndexToStarLayoutIndexMap = new Dictionary<int, int>();
 
        #endregion
    }
}