From 90593353bf666e1493bb30bce8a87b486d9f7928 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=AB=E5=9F=8Evia?= Date: Wed, 12 Jul 2023 16:03:36 +0800 Subject: [PATCH] 1.1.15 --- .../Controls/TabControlPanel.cs | 197 +++++++++++ ...bControlScrollButtonVisibilityConverter.cs | 26 ++ .../Converters/TabPanelMaxHeightConverter.cs | 12 +- .../Converters/TabPanelMaxWidthConverter.cs | 12 +- .../Panuon.WPF.UI.Internal.projitems | 2 + .../Resources/ConverterKeys.cs | 1 + .../Resources/Converters.xaml | 1 + .../Styles/TabControlStyle.xaml | 41 +++ .../Templates/SwitchTemplate.xaml | 1 + .../Templates/TabControlTemplate.xaml | 313 ++++++++---------- ...t.cs => TabControlHeaderPanelAlignment.cs} | 2 +- .../Panuon.WPF.UI/Helpers/TabControlHelper.cs | 85 ++++- .../Panuon.WPF.UI/Panuon.WPF.UI.projitems | 2 +- .../Panuon.WPF.UI/Properties/AssemblyInfo.cs | 4 +- 14 files changed, 498 insertions(+), 201 deletions(-) create mode 100644 SourceCode/SharedResources/Panuon.WPF.UI.Internal/Controls/TabControlPanel.cs create mode 100644 SourceCode/SharedResources/Panuon.WPF.UI.Internal/Converters/TabControlScrollButtonVisibilityConverter.cs rename SourceCode/SharedResources/Panuon.WPF.UI/Enums/{TabPanelAlignment.cs => TabControlHeaderPanelAlignment.cs} (68%) diff --git a/SourceCode/SharedResources/Panuon.WPF.UI.Internal/Controls/TabControlPanel.cs b/SourceCode/SharedResources/Panuon.WPF.UI.Internal/Controls/TabControlPanel.cs new file mode 100644 index 00000000..cbd76f9c --- /dev/null +++ b/SourceCode/SharedResources/Panuon.WPF.UI.Internal/Controls/TabControlPanel.cs @@ -0,0 +1,197 @@ +using System; +using System.Data; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Media.Media3D; + +namespace Panuon.WPF.UI.Internal +{ + class TabControlPanel + : Panel + { + #region Properties + + #region HeaderPanelAlignment + public TabControlHeaderPanelAlignment HeaderPanelAlignment + { + get { return (TabControlHeaderPanelAlignment)GetValue(HeaderPanelAlignmentProperty); } + set { SetValue(HeaderPanelAlignmentProperty, value); } + } + + public static readonly DependencyProperty HeaderPanelAlignmentProperty = + DependencyProperty.Register("HeaderPanelAlignment", typeof(TabControlHeaderPanelAlignment), typeof(TabControlPanel), new FrameworkPropertyMetadata(TabControlHeaderPanelAlignment.Stretch, FrameworkPropertyMetadataOptions.AffectsArrange)); + #endregion + + #region Orientation + public Orientation Orientation + { + get { return (Orientation)GetValue(OrientationProperty); } + set { SetValue(OrientationProperty, value); } + } + + public static readonly DependencyProperty OrientationProperty = + DependencyProperty.Register("Orientation", typeof(Orientation), typeof(TabControlPanel), new FrameworkPropertyMetadata(Orientation.Vertical, FrameworkPropertyMetadataOptions.AffectsArrange)); + #endregion + + #endregion + + protected override Size MeasureOverride(Size constraint) + { + var width = 0d; + var height = 0d; + foreach (FrameworkElement child in Children) + { + child.Measure(constraint); + if (child is ScrollViewer scrollViewer) + { + var tabPanel = scrollViewer.Content as TabPanel; + tabPanel.Measure(constraint); + switch (Orientation) + { + case Orientation.Vertical: + height += tabPanel.DesiredSize.Height; + break; + case Orientation.Horizontal: + width += tabPanel.DesiredSize.Width; + break; + } + } + else + { + switch (Orientation) + { + case Orientation.Vertical: + height += child.DesiredSize.Height; + break; + case Orientation.Horizontal: + width += child.DesiredSize.Width; + break; + } + } + switch (Orientation) + { + case Orientation.Vertical: + width = Math.Max(width, child.DesiredSize.Width); + break; + case Orientation.Horizontal: + height = Math.Max(height, child.DesiredSize.Height); + break; + } + } + + switch (Orientation) + { + case Orientation.Vertical: + return new Size(width, double.IsInfinity(constraint.Height) + ? height + : constraint.Height); + default: + return new Size(double.IsInfinity(constraint.Width) + ? width + : constraint.Width, height); + } + } + + protected override Size ArrangeOverride(Size arrangeSize) + { + var ccFront = Children[0]; + var bdrFront = Children[1]; + var scrollViewer = (ScrollViewer)Children[2]; + var bdrEnd = Children[3]; + var ccEnd = Children[4]; + + switch (Orientation) + { + case Orientation.Vertical: + var contentHeight = (scrollViewer.Content as TabPanel).DesiredSize.Height; + SetCurrentValue(ContentWidthOrHeightProperty, contentHeight); + + var top = 0d; + var scrollViewerHeight = Math.Max(0, arrangeSize.Height - ccFront.DesiredSize.Height - ccEnd.DesiredSize.Height); + if(contentHeight + ccFront.DesiredSize.Height + ccFront.DesiredSize.Height < arrangeSize.Height) + { + switch (HeaderPanelAlignment) + { + case TabControlHeaderPanelAlignment.Stretch: + break; + case TabControlHeaderPanelAlignment.Front: + scrollViewerHeight = Math.Min(scrollViewerHeight, contentHeight); + break; + case TabControlHeaderPanelAlignment.Center: + scrollViewerHeight = Math.Min(scrollViewerHeight, contentHeight); + top = (arrangeSize.Height - ccFront.DesiredSize.Height - scrollViewerHeight - ccEnd.DesiredSize.Height) / 2; + break; + case TabControlHeaderPanelAlignment.End: + scrollViewerHeight = Math.Min(scrollViewerHeight, contentHeight); + top = arrangeSize.Height - ccFront.DesiredSize.Height - scrollViewerHeight - ccEnd.DesiredSize.Height; + break; + } + } + + ccFront.Arrange(new Rect(0, top, arrangeSize.Width, ccFront.DesiredSize.Height)); + bdrFront.Arrange(new Rect(0, top, arrangeSize.Width, ccFront.DesiredSize.Height)); + top += ccFront.DesiredSize.Height; + + scrollViewer.Height = scrollViewerHeight; + scrollViewer.Arrange(new Rect(0, top, arrangeSize.Width, scrollViewerHeight)); + + top += scrollViewerHeight; + bdrEnd.Arrange(new Rect(0, top, arrangeSize.Width, ccEnd.DesiredSize.Height)); + ccEnd.Arrange(new Rect(0, top, arrangeSize.Width, ccEnd.DesiredSize.Height)); + break; + default: + var contentWidth = (scrollViewer.Content as TabPanel).DesiredSize.Width; + SetCurrentValue(ContentWidthOrHeightProperty, contentWidth); + + var left = 0d; + var scrollViewerWidth = Math.Max(0, arrangeSize.Width - ccFront.DesiredSize.Width - ccEnd.DesiredSize.Width); + + if (contentWidth + ccFront.DesiredSize.Width + ccFront.DesiredSize.Width < arrangeSize.Width) + { + switch (HeaderPanelAlignment) + { + case TabControlHeaderPanelAlignment.Stretch: + break; + case TabControlHeaderPanelAlignment.Front: + scrollViewerWidth = Math.Min(scrollViewerWidth, contentWidth); + break; + case TabControlHeaderPanelAlignment.Center: + scrollViewerWidth = Math.Min(scrollViewerWidth, contentWidth); + left = (arrangeSize.Width - ccFront.DesiredSize.Width - scrollViewerWidth - ccEnd.DesiredSize.Width) / 2; + break; + case TabControlHeaderPanelAlignment.End: + scrollViewerWidth = Math.Min(scrollViewerWidth, contentWidth); + left = arrangeSize.Width - ccFront.DesiredSize.Width - scrollViewerWidth - ccEnd.DesiredSize.Width; + break; + } + } + + ccFront.Arrange(new Rect(left, 0, ccFront.DesiredSize.Width, arrangeSize.Height)); + bdrFront.Arrange(new Rect(left, 0, ccFront.DesiredSize.Width, arrangeSize.Height)); + left += ccFront.DesiredSize.Width; + + scrollViewer.Width = scrollViewerWidth; + scrollViewer.Arrange(new Rect(left, 0, scrollViewerWidth, arrangeSize.Height)); + + left += scrollViewerWidth; + bdrEnd.Arrange(new Rect(left, 0, ccEnd.DesiredSize.Width, arrangeSize.Height)); + ccEnd.Arrange(new Rect(left, 0, ccEnd.DesiredSize.Width, arrangeSize.Height)); + break; + } + + return arrangeSize; + } + + public double ContentWidthOrHeight + { + get { return (double)GetValue(ContentWidthOrHeightProperty); } + set { SetValue(ContentWidthOrHeightProperty, value); } + } + + public static readonly DependencyProperty ContentWidthOrHeightProperty = + DependencyProperty.Register("ContentWidthOrHeight", typeof(double), typeof(TabControlPanel)); + + + } +} diff --git a/SourceCode/SharedResources/Panuon.WPF.UI.Internal/Converters/TabControlScrollButtonVisibilityConverter.cs b/SourceCode/SharedResources/Panuon.WPF.UI.Internal/Converters/TabControlScrollButtonVisibilityConverter.cs new file mode 100644 index 00000000..8df5c65c --- /dev/null +++ b/SourceCode/SharedResources/Panuon.WPF.UI.Internal/Converters/TabControlScrollButtonVisibilityConverter.cs @@ -0,0 +1,26 @@ +using System; +using System.Globalization; +using System.Windows; + +namespace Panuon.WPF.UI.Internal.Converters +{ + class TabControlScrollButtonVisibilityConverter + : OneWayMultiValueConverterBase + { + public override object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + { + var actualWidth = (double)values[0]; + var containerWidth = (double)values[1]; + var frontWidth = (double)values[2]; + var endWidth = (double)values[3]; + var contentWidth = containerWidth + frontWidth + endWidth; + + if(contentWidth > actualWidth + && Math.Abs(actualWidth - contentWidth) > 1) + { + return Visibility.Visible; + } + return Visibility.Collapsed; + } + } +} diff --git a/SourceCode/SharedResources/Panuon.WPF.UI.Internal/Converters/TabPanelMaxHeightConverter.cs b/SourceCode/SharedResources/Panuon.WPF.UI.Internal/Converters/TabPanelMaxHeightConverter.cs index 478593bd..a1d65d1c 100644 --- a/SourceCode/SharedResources/Panuon.WPF.UI.Internal/Converters/TabPanelMaxHeightConverter.cs +++ b/SourceCode/SharedResources/Panuon.WPF.UI.Internal/Converters/TabPanelMaxHeightConverter.cs @@ -9,20 +9,10 @@ class TabPanelMaxHeightConverter { public override object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { - var height = (double)values[0]; - var alignment = (VerticalAlignment)values[1]; - if(double.IsNaN(height) || alignment != VerticalAlignment.Stretch) - { - return double.PositiveInfinity; - } var tabActualHeight = (double)values[2]; - if (double.IsNaN(tabActualHeight)) - { - return double.PositiveInfinity; - } var frontHeight = (double)values[3]; var endHeight = (double)values[4]; - return tabActualHeight - frontHeight - endHeight; + return Math.Max(0, tabActualHeight - frontHeight - endHeight); } } } diff --git a/SourceCode/SharedResources/Panuon.WPF.UI.Internal/Converters/TabPanelMaxWidthConverter.cs b/SourceCode/SharedResources/Panuon.WPF.UI.Internal/Converters/TabPanelMaxWidthConverter.cs index b5914d91..67250f2e 100644 --- a/SourceCode/SharedResources/Panuon.WPF.UI.Internal/Converters/TabPanelMaxWidthConverter.cs +++ b/SourceCode/SharedResources/Panuon.WPF.UI.Internal/Converters/TabPanelMaxWidthConverter.cs @@ -4,14 +4,14 @@ namespace Panuon.WPF.UI.Internal.Converters { - class TabPanelMaxWidthConverter + class TabPanelMaxWidthConverter : OneWayMultiValueConverterBase { public override object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { - var width = (double)values[0]; + var height = (double)values[0]; var alignment = (HorizontalAlignment)values[1]; - if(double.IsNaN(width) || alignment != HorizontalAlignment.Stretch) + if (double.IsNaN(height) || alignment != HorizontalAlignment.Stretch) { return double.PositiveInfinity; } @@ -22,7 +22,9 @@ public override object Convert(object[] values, Type targetType, object paramete } var frontWidth = (double)values[3]; var endWidth = (double)values[4]; - return tabActualWidth - frontWidth - endWidth; + var backWidth = (double)values[5]; + var foreWidth = (double)values[6]; + return Math.Max(0, tabActualWidth - frontWidth - endWidth - backWidth - foreWidth); } } -} +} \ No newline at end of file diff --git a/SourceCode/SharedResources/Panuon.WPF.UI.Internal/Panuon.WPF.UI.Internal.projitems b/SourceCode/SharedResources/Panuon.WPF.UI.Internal/Panuon.WPF.UI.Internal.projitems index cf4d752e..f8020156 100644 --- a/SourceCode/SharedResources/Panuon.WPF.UI.Internal/Panuon.WPF.UI.Internal.projitems +++ b/SourceCode/SharedResources/Panuon.WPF.UI.Internal/Panuon.WPF.UI.Internal.projitems @@ -19,6 +19,7 @@ + @@ -60,6 +61,7 @@ + diff --git a/SourceCode/SharedResources/Panuon.WPF.UI.Internal/Resources/ConverterKeys.cs b/SourceCode/SharedResources/Panuon.WPF.UI.Internal/Resources/ConverterKeys.cs index 7cb95147..e00e78c6 100644 --- a/SourceCode/SharedResources/Panuon.WPF.UI.Internal/Resources/ConverterKeys.cs +++ b/SourceCode/SharedResources/Panuon.WPF.UI.Internal/Resources/ConverterKeys.cs @@ -43,6 +43,7 @@ static class ConverterKeys public const string SliderTextLeftConverter = nameof(SliderTextLeftConverter); public const string SliderTextTopConverter = nameof(SliderTextTopConverter); public const string SwitchToggleMarginConverter = nameof(SwitchToggleMarginConverter); + public const string TabControlScrollButtonVisibilityConverter = nameof(TabControlScrollButtonVisibilityConverter); public const string TabPanelMaxHeightConverter = nameof(TabPanelMaxHeightConverter); public const string TabPanelMaxWidthConverter = nameof(TabPanelMaxWidthConverter); public const string ThicknessLeftRightOnlyConverter = nameof(ThicknessLeftRightOnlyConverter); diff --git a/SourceCode/SharedResources/Panuon.WPF.UI.Internal/Resources/Converters.xaml b/SourceCode/SharedResources/Panuon.WPF.UI.Internal/Resources/Converters.xaml index cfe595cb..f4b1a5ad 100644 --- a/SourceCode/SharedResources/Panuon.WPF.UI.Internal/Resources/Converters.xaml +++ b/SourceCode/SharedResources/Panuon.WPF.UI.Internal/Resources/Converters.xaml @@ -20,6 +20,7 @@ + diff --git a/SourceCode/SharedResources/Panuon.WPF.UI.Internal/Styles/TabControlStyle.xaml b/SourceCode/SharedResources/Panuon.WPF.UI.Internal/Styles/TabControlStyle.xaml index 0ac3a50f..5e9ad847 100644 --- a/SourceCode/SharedResources/Panuon.WPF.UI.Internal/Styles/TabControlStyle.xaml +++ b/SourceCode/SharedResources/Panuon.WPF.UI.Internal/Styles/TabControlStyle.xaml @@ -7,6 +7,7 @@ xmlns:irs="clr-namespace:Panuon.WPF.UI.Internal.Resources"> + @@ -34,6 +35,42 @@ Value="{Binding Path=(i:VisualStateHelper.Foreground), RelativeSource={RelativeSource AncestorType=TabItem}, Mode=OneWay}" /> + + +