| File: Microsoft\Windows\Controls\Ribbon\Primitives\RibbonTitlePanel.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. #region Using directives using MS.Internal; using System.Windows.Controls.Primitives; using System.Windows.Media; #if RIBBON_IN_FRAMEWORK namespace System.Windows.Controls.Ribbon.Primitives #else namespace Microsoft.Windows.Controls.Ribbon.Primitives #endif { #endregion /// <summary> /// Implements the title panel of the Ribbon, this layouts out the Ribbon title /// and RibbonContextualTabGroup separators around the QAT and system buttons. /// </summary> public class RibbonTitlePanel : Panel { #region Private Properties /// <summary> /// DependencyProperty for Ribbon property. /// </summary> public static readonly DependencyProperty RibbonProperty = RibbonControlService.RibbonProperty.AddOwner(typeof(RibbonTitlePanel)); /// <summary> /// This property is used to access visual style brushes defined on the Ribbon class. /// </summary> public Ribbon Ribbon { get { return RibbonControlService.GetRibbon(this); } } #endregion #region Protected Methods /// <summary> /// Measures the children of the RibbonTitlePanel. /// </summary> /// <param name="constraint">The space available for layout.</param> /// <returns>Returns the RibbonTitlePanel's desired size as determined by measurement of its children.</returns> protected override Size MeasureOverride(Size availableSize) { Size desiredSize = new Size(); int count = Children.Count; if (count == 0) { return desiredSize; } double startContextualTabX = availableSize.Width; double endContextualTabX = 0.0; RibbonContextualTabGroupItemsControl groupHeaderItemsControl = Ribbon.ContextualTabGroupItemsControl; if (groupHeaderItemsControl != null && groupHeaderItemsControl.Visibility == Visibility.Visible) { // A dummy measure to ensure containers are generated. // We need to know FirstContextualTabHeader and LastContextualTabHeader if (groupHeaderItemsControl.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated) { groupHeaderItemsControl.Measure(availableSize); } // Calculate start and end positions RibbonContextualTabGroup firstContextualTab = groupHeaderItemsControl.FirstContextualTabHeader; if (firstContextualTab != null) { startContextualTabX = Math.Min(CalculateContextualTabGroupStartX(firstContextualTab), availableSize.Width); } RibbonContextualTabGroup lastContextualTab = groupHeaderItemsControl.LastContextualTabHeader; if (lastContextualTab != null) { endContextualTabX = Math.Min(CalculateContextualTabGroupEndX(lastContextualTab), availableSize.Width); } groupHeaderItemsControl.Measure(new Size(Math.Max(endContextualTabX - startContextualTabX, 0), availableSize.Height)); desiredSize.Width += groupHeaderItemsControl.DesiredSize.Width; desiredSize.Height = Math.Max(desiredSize.Height, groupHeaderItemsControl.DesiredSize.Height); } FrameworkElement qat = Ribbon.QatTopHost as FrameworkElement; FrameworkElement titleHost = Ribbon.TitleHost as FrameworkElement; if (qat != null && titleHost != null) { double availableToQat = 0.0, availableToTitle; double leftSpace, rightSpace; // If ribbon is not collapsed and QAT is on top if (qat.Visibility == Visibility.Visible) { rightSpace = availableSize.Width - endContextualTabX; // 1) if there is no room on the right for Title // 2) or when there are no contextual tabs (i.e. endContextualTabX==0), then rightSpace=entire width // want to ensure MinWidth for title if possible if ((DoubleUtil.LessThan(rightSpace, titleHost.MinWidth) || DoubleUtil.GreaterThan(startContextualTabX, endContextualTabX)) && DoubleUtil.GreaterThan(startContextualTabX, titleHost.MinWidth)) { // Leave titleHost.MinWidth at the left of ContextualTabGroupItemsControl for TitleHost availableToQat = startContextualTabX - titleHost.MinWidth; } else { // Give QAT space from 0 to start of ContextualTabGroupItemsControl availableToQat = startContextualTabX; } // The size computed could be negative because of invalid // transforms due to invalid arrange. Assume that the space // available is 0 in such cases and assume that validation of // transforms at a later point of time will fix the situation. availableToQat = Math.Max(availableToQat, 0); // Measure QAT qat.Measure(new Size(availableToQat, availableSize.Height)); availableToQat = qat.DesiredSize.Width; desiredSize.Width += availableToQat; desiredSize.Height = Math.Max(desiredSize.Height, qat.DesiredSize.Height); } // Measure TitleHost. // We try to give it titleHost.MinWidth, but it may get less than that when QAT has already reached qat.MinWidth endContextualTabX = Math.Max(endContextualTabX, availableToQat); rightSpace = availableSize.Width - endContextualTabX; leftSpace = startContextualTabX - availableToQat; // first measure to title's full size and see if it can fit entirely to the left titleHost.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); if (DoubleUtil.LessThanOrClose(titleHost.DesiredSize.Width, leftSpace)) { availableToTitle = leftSpace; } else { // title takes best of right or left availableToTitle = Math.Max(leftSpace, rightSpace); } // The size computed could be negative because of invalid // transforms due to invalid arrange. Assume that the space // available is 0 in such cases and assume that validation of // transforms at a later point of time will fix the situation. availableToTitle = Math.Max(availableToTitle, 0); titleHost.Measure(new Size(availableToTitle, availableSize.Height)); desiredSize.Width += titleHost.DesiredSize.Width; desiredSize.Height = Math.Max(desiredSize.Height, titleHost.DesiredSize.Height); } desiredSize.Width = Math.Min(desiredSize.Width, availableSize.Width); // Prevent clipping return desiredSize; } /// <summary> /// Arranges the children of the RibbonTitlePanel. /// </summary> /// <param name="arrangeSize">The arranged size of the TitlePanel.</param> /// <returns>The size at which the TitlePanel was arranged.</returns> protected override Size ArrangeOverride(Size finalSize) { double x = 0.0; double width; double height; double startContextualTabX = 0; double endContextualTabX = 0; // Arrange ContextualTabHeaders RibbonContextualTabGroupItemsControl tabGroups = Ribbon.ContextualTabGroupItemsControl; if (tabGroups != null && tabGroups.Visibility == Visibility.Visible) { RibbonContextualTabGroup firstContextualTab = tabGroups.FirstContextualTabHeader; if (firstContextualTab != null) { startContextualTabX = Math.Min(CalculateContextualTabGroupStartX(firstContextualTab), finalSize.Width); } endContextualTabX = Math.Min(startContextualTabX + tabGroups.DesiredSize.Width, finalSize.Width); tabGroups.Arrange(new Rect(startContextualTabX, finalSize.Height - tabGroups.DesiredSize.Height, tabGroups.DesiredSize.Width, tabGroups.DesiredSize.Height)); } // Arrange QuickAccessToolbar double qatDesiredWidth = 0.0; UIElement qat = Ribbon.QatTopHost; if (qat != null) { qatDesiredWidth = qat.DesiredSize.Width; qat.Arrange(new Rect(0, 0.0, qatDesiredWidth, qat.DesiredSize.Height)); } endContextualTabX = Math.Max(endContextualTabX, qatDesiredWidth); // Arrange the title UIElement titleHost = Ribbon.TitleHost; if (titleHost != null) { x = 0.0; width = titleHost.DesiredSize.Width; height = titleHost.DesiredSize.Height; double leftSpace = startContextualTabX - qatDesiredWidth; // If title can fit entirely to the left, then arrange on the left // i.e. between QAT and CTGHeaders if ( DoubleUtil.LessThanOrClose(width, leftSpace) ) { x = qatDesiredWidth; } else { // Arrange to the right of CTGHeaders. x = endContextualTabX; ; } titleHost.Arrange(new Rect(x, 0.0, width, height)); } return finalSize; } #endregion #region Private Methods /// <summary> /// Calculates the x-position at which the given ContextualTabGroup should be placed on the title panel. /// </summary> /// <param name="group">The RibbonContextualTabGroup to position.</param> /// <returns>The x-offset at which the ContextualTabGroup should be positioned.</returns> private double CalculateContextualTabGroupStartX(RibbonContextualTabGroup groupHeader) { double result = 0.0; // A visible CTG can have some of its tabs Collapsed. // We should start from the first visible Tab in this CTG. RibbonTab firstTab = groupHeader.FirstVisibleTab; if (firstTab != null && firstTab.RibbonTabHeader != null && Ribbon != null) { RibbonTabHeader tabHeader = firstTab.RibbonTabHeader; GeneralTransform transformRibbonTabToRibbon = tabHeader.TransformToAncestor(Ribbon); GeneralTransform transformRibbonToRibbonTitlePanel = Ribbon.TransformToDescendant(this); Point point = new Point(); point = transformRibbonTabToRibbon.Transform(point); point = transformRibbonToRibbonTitlePanel.Transform(point); result = point.X - tabHeader.Margin.Left; // Reduce RibbonTab Margin } return result; } private double CalculateContextualTabGroupEndX(RibbonContextualTabGroup groupHeader) { double endX = CalculateContextualTabGroupStartX(groupHeader); foreach (RibbonTab tab in groupHeader.Tabs) { if (tab.Visibility == Visibility.Visible && tab.RibbonTabHeader != null) { endX += tab.RibbonTabHeader.DesiredSize.Width; } } return endX; } #endregion } }