From 76e52645ce1c92e416747197b993f4707b4038d6 Mon Sep 17 00:00:00 2001 From: HeBianGu <908293466@qq.com> Date: Thu, 14 Dec 2023 18:11:41 +0800 Subject: [PATCH] Panel+ --- Solution/WPF-Control.sln | 11 +- .../Controls/H.Controls.Panel/AssemblyInfo.cs | 19 + .../H.Controls.Panel/DefinitionGrid.cs | 7 + Source/Controls/H.Controls.Panel/Extention.cs | 27 + .../H.Controls.Panel/H.Controls.Panel.csproj | 5 + Source/Controls/H.Controls.Panel/HexGrid.xaml | 97 +++ .../Controls/H.Controls.Panel/HexGrid.xaml.cs | 404 ++++++++++++ .../H.Controls.Panel/Panel/ArcPanel.cs | 131 ++++ .../H.Controls.Panel/Panel/CirclePanel.cs | 88 +++ .../H.Controls.Panel/Panel/CoverPanel.cs | 149 +++++ .../H.Controls.Panel/Panel/CrossPanel.cs | 178 ++++++ .../H.Controls.Panel/Panel/DelayStackPanel.cs | 167 +++++ .../H.Controls.Panel/Panel/DockPanel.cs | 463 ++++++++++++++ .../H.Controls.Panel/Panel/DragPanel.cs | 477 +++++++++++++++ .../H.Controls.Panel/Panel/GridAreaAttach.cs | 574 ++++++++++++++++++ .../H.Controls.Panel/Panel/PanelBase.cs | 138 +++++ .../H.Controls.Panel/Panel/RandomPanel.cs | 114 ++++ .../H.Controls.Panel/Themes/Generic.xaml | 11 + 18 files changed, 3058 insertions(+), 2 deletions(-) create mode 100644 Source/Controls/H.Controls.Panel/AssemblyInfo.cs create mode 100644 Source/Controls/H.Controls.Panel/DefinitionGrid.cs create mode 100644 Source/Controls/H.Controls.Panel/Extention.cs create mode 100644 Source/Controls/H.Controls.Panel/H.Controls.Panel.csproj create mode 100644 Source/Controls/H.Controls.Panel/HexGrid.xaml create mode 100644 Source/Controls/H.Controls.Panel/HexGrid.xaml.cs create mode 100644 Source/Controls/H.Controls.Panel/Panel/ArcPanel.cs create mode 100644 Source/Controls/H.Controls.Panel/Panel/CirclePanel.cs create mode 100644 Source/Controls/H.Controls.Panel/Panel/CoverPanel.cs create mode 100644 Source/Controls/H.Controls.Panel/Panel/CrossPanel.cs create mode 100644 Source/Controls/H.Controls.Panel/Panel/DelayStackPanel.cs create mode 100644 Source/Controls/H.Controls.Panel/Panel/DockPanel.cs create mode 100644 Source/Controls/H.Controls.Panel/Panel/DragPanel.cs create mode 100644 Source/Controls/H.Controls.Panel/Panel/GridAreaAttach.cs create mode 100644 Source/Controls/H.Controls.Panel/Panel/PanelBase.cs create mode 100644 Source/Controls/H.Controls.Panel/Panel/RandomPanel.cs create mode 100644 Source/Controls/H.Controls.Panel/Themes/Generic.xaml diff --git a/Solution/WPF-Control.sln b/Solution/WPF-Control.sln index b1cecc64..211bbfb9 100644 --- a/Solution/WPF-Control.sln +++ b/Solution/WPF-Control.sln @@ -233,9 +233,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "H.Test.Project", "..\Source EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "H.Controls.PrintBox", "..\Source\Controls\H.Controls.PrintBox\H.Controls.PrintBox.csproj", "{D917DF70-86C1-41C4-A8F1-B0BFF091DC26}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "H.Presenters.Design", "..\Source\Presenters\H.Presenters.Design\H.Presenters.Design.csproj", "{2A209B5F-0ED4-4286-B4A9-24265B125C82}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "H.Presenters.Design", "..\Source\Presenters\H.Presenters.Design\H.Presenters.Design.csproj", "{2A209B5F-0ED4-4286-B4A9-24265B125C82}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "H.Test.Design", "..\Source\Tests\H.Test.Design\H.Test.Design.csproj", "{0EC77F0B-A7D3-48E9-811A-EC76298452A7}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "H.Test.Design", "..\Source\Tests\H.Test.Design\H.Test.Design.csproj", "{0EC77F0B-A7D3-48E9-811A-EC76298452A7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "H.Controls.Panel", "..\Source\Controls\H.Controls.Panel\H.Controls.Panel.csproj", "{8634791D-536C-424F-B6D5-8986A4C6EF3B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -659,6 +661,10 @@ Global {0EC77F0B-A7D3-48E9-811A-EC76298452A7}.Debug|Any CPU.Build.0 = Debug|Any CPU {0EC77F0B-A7D3-48E9-811A-EC76298452A7}.Release|Any CPU.ActiveCfg = Release|Any CPU {0EC77F0B-A7D3-48E9-811A-EC76298452A7}.Release|Any CPU.Build.0 = Release|Any CPU + {8634791D-536C-424F-B6D5-8986A4C6EF3B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8634791D-536C-424F-B6D5-8986A4C6EF3B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8634791D-536C-424F-B6D5-8986A4C6EF3B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8634791D-536C-424F-B6D5-8986A4C6EF3B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -770,6 +776,7 @@ Global {D917DF70-86C1-41C4-A8F1-B0BFF091DC26} = {9E63ED70-6F96-4CB2-8B9D-EAEC77D0548E} {2A209B5F-0ED4-4286-B4A9-24265B125C82} = {B5FE7036-B316-4FC3-81E8-00BD6C76784D} {0EC77F0B-A7D3-48E9-811A-EC76298452A7} = {E2C0B11D-5ED5-4F22-8E56-F68982B6E59B} + {8634791D-536C-424F-B6D5-8986A4C6EF3B} = {9E63ED70-6F96-4CB2-8B9D-EAEC77D0548E} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DF825C3D-F4F4-4E62-BBFF-B42A5E81112F} diff --git a/Source/Controls/H.Controls.Panel/AssemblyInfo.cs b/Source/Controls/H.Controls.Panel/AssemblyInfo.cs new file mode 100644 index 00000000..5c12ca16 --- /dev/null +++ b/Source/Controls/H.Controls.Panel/AssemblyInfo.cs @@ -0,0 +1,19 @@ +// Copyright © 2022 By HeBianGu(QQ:908293466) https://github.com/HeBianGu/WPF-ControlBase + +using System.Windows; +using System.Windows.Markup; + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] + +[assembly: XmlnsDefinition("QQ:908293466", "H.Controls.Panel")] +[assembly: XmlnsPrefix("QQ:908293466", "h")] + +[assembly: XmlnsDefinition("https://github.com/HeBianGu", "H.Controls.Panel")] +[assembly: XmlnsPrefix("https://github.com/HeBianGu", "h")] diff --git a/Source/Controls/H.Controls.Panel/DefinitionGrid.cs b/Source/Controls/H.Controls.Panel/DefinitionGrid.cs new file mode 100644 index 00000000..b59668bf --- /dev/null +++ b/Source/Controls/H.Controls.Panel/DefinitionGrid.cs @@ -0,0 +1,7 @@ +// Copyright © 2022 By HeBianGu(QQ:908293466) https://github.com/HeBianGu/WPF-ControlBase + +namespace H.Controls.Panel +{ + + +} diff --git a/Source/Controls/H.Controls.Panel/Extention.cs b/Source/Controls/H.Controls.Panel/Extention.cs new file mode 100644 index 00000000..1d5cf348 --- /dev/null +++ b/Source/Controls/H.Controls.Panel/Extention.cs @@ -0,0 +1,27 @@ +// Copyright © 2022 By HeBianGu(QQ:908293466) https://github.com/HeBianGu/WPF-ControlBase + +namespace System +{ + public static class Extention + { + ///// + ///// 注册 + ///// + ///// + //public static void AddPropertyGrid(this IServiceCollection service) + //{ + // service.AddSingleton(); + //} + + ///// + ///// 配置 + ///// + ///// + //public static void UseContainPanel(this IApplicationBuilder service, Action action) + //{ + // action?.Invoke(ContainPanelSetting.Instance); + //} + } + + +} diff --git a/Source/Controls/H.Controls.Panel/H.Controls.Panel.csproj b/Source/Controls/H.Controls.Panel/H.Controls.Panel.csproj new file mode 100644 index 00000000..e9328183 --- /dev/null +++ b/Source/Controls/H.Controls.Panel/H.Controls.Panel.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/Source/Controls/H.Controls.Panel/HexGrid.xaml b/Source/Controls/H.Controls.Panel/HexGrid.xaml new file mode 100644 index 00000000..70908ff2 --- /dev/null +++ b/Source/Controls/H.Controls.Panel/HexGrid.xaml @@ -0,0 +1,97 @@ + + + + + + + + diff --git a/Source/Controls/H.Controls.Panel/HexGrid.xaml.cs b/Source/Controls/H.Controls.Panel/HexGrid.xaml.cs new file mode 100644 index 00000000..ac715948 --- /dev/null +++ b/Source/Controls/H.Controls.Panel/HexGrid.xaml.cs @@ -0,0 +1,404 @@ +// Copyright © 2022 By HeBianGu(QQ:908293466) https://github.com/HeBianGu/WPF-ControlBase + +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Media; + +namespace H.Controls.Panel +{ + public class HexGrid : System.Windows.Controls.Panel + { + static HexGrid() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(HexGrid), new FrameworkPropertyMetadata(typeof(HexGrid))); + } + + #region Orientation + public static readonly DependencyProperty OrientationProperty = + DependencyProperty.RegisterAttached("Orientation", typeof(Orientation), typeof(HexGrid), + new FrameworkPropertyMetadata(Orientation.Horizontal, + FrameworkPropertyMetadataOptions.AffectsMeasure | + FrameworkPropertyMetadataOptions.AffectsArrange | + FrameworkPropertyMetadataOptions.Inherits)); + + public static void SetOrientation(DependencyObject element, Orientation value) + { + element.SetValue(OrientationProperty, value); + } + + public static Orientation GetOrientation(DependencyObject element) + { + return (Orientation)element.GetValue(OrientationProperty); + } + + public Orientation Orientation + { + get { return (Orientation)GetValue(OrientationProperty); } + set { SetValue(OrientationProperty, value); } + } + #endregion + + public static readonly DependencyProperty RowCountProperty = + DependencyProperty.Register("RowCount", typeof(int), typeof(HexGrid), + new FrameworkPropertyMetadata(1, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsArrange), + ValidateCountCallback); + + + public int RowCount + { + get { return (int)GetValue(RowCountProperty); } + set { SetValue(RowCountProperty, value); } + } + + public static readonly DependencyProperty ColumnCountProperty = + DependencyProperty.Register("ColumnCount", typeof(int), typeof(HexGrid), + new FrameworkPropertyMetadata(1, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsArrange), + ValidateCountCallback); + + public int ColumnCount + { + get { return (int)GetValue(ColumnCountProperty); } + set { SetValue(ColumnCountProperty, value); } + } + + private static bool ValidateCountCallback(object value) + { + if (value is int) + { + int count = (int)value; + return count > 0; + } + + return false; + } + + private int GetRow(UIElement e) + { + int row = (int)e.GetValue(Grid.RowProperty); + if (row >= RowCount) + row = RowCount - 1; + return row; + } + + private int GetColumn(UIElement e) + { + int column = (int)e.GetValue(Grid.ColumnProperty); + if (column >= ColumnCount) + column = ColumnCount - 1; + return column; + } + + protected override Size MeasureOverride(Size availableSize) + { + double w = availableSize.Width; + double h = availableSize.Height; + + // if there is Infinity size dimension + if (Double.IsInfinity(w) || Double.IsInfinity(h)) + { + // determine maximum desired size + h = 0; + w = 0; + foreach (UIElement e in InternalChildren) + { + e.Measure(availableSize); + Size s = e.DesiredSize; + if (s.Height > h) + h = s.Height; + if (s.Width > w) + w = s.Width; + } + + // multiply maximum size to RowCount and ColumnCount to get total size + if (Orientation == Orientation.Horizontal) + return new Size(w * (ColumnCount * 3 + 1) / 4, h * (RowCount * 2 + 1) / 2); + + return new Size(w * (ColumnCount * 2 + 1) / 2, h * (RowCount * 3 + 1) / 4); + } + + return availableSize; + } + + #region HasShift + private void HasShift(out bool first, out bool last) + { + if (Orientation == Orientation.Horizontal) + HasRowShift(out first, out last); + else + HasColumnShift(out first, out last); + } + + private void HasRowShift(out bool firstRow, out bool lastRow) + { + firstRow = lastRow = true; + + UIElementCollection elements = base.InternalChildren; + for (int i = 0; i < elements.Count && (firstRow || lastRow); i++) + { + UIElement e = elements[i]; + if (e.Visibility == Visibility.Collapsed) + continue; + + int row = GetRow(e); + int column = GetColumn(e); + + int mod = column % 2; + + if (row == 0 && mod == 0) + firstRow = false; + + if (row == RowCount - 1 && mod == 1) + lastRow = false; + } + } + + private void HasColumnShift(out bool firstColumn, out bool lastColumn) + { + firstColumn = lastColumn = true; + + UIElementCollection elements = base.InternalChildren; + for (int i = 0; i < elements.Count && (firstColumn || lastColumn); i++) + { + UIElement e = elements[i]; + if (e.Visibility == Visibility.Collapsed) + continue; + + int row = GetRow(e); + int column = GetColumn(e); + + int mod = row % 2; + + if (column == 0 && mod == 0) + firstColumn = false; + + if (column == ColumnCount - 1 && mod == 1) + lastColumn = false; + } + } + #endregion + + #region GetHexSize + private Size GetHexSize(Size gridSize) + { + double minH = 0; + double minW = 0; + + foreach (UIElement e in InternalChildren) + { + FrameworkElement f = e as FrameworkElement; + if (f != null) + { + if (f.MinHeight > minH) + minH = f.MinHeight; + if (f.MinWidth > minW) + minW = f.MinWidth; + } + } + + bool first, last; + HasShift(out first, out last); + + Size possibleSize = GetPossibleSize(gridSize); + double possibleW = possibleSize.Width; + double possibleH = possibleSize.Height; + + double w = Math.Max(minW, possibleW); + double h = Math.Max(minH, possibleH); + + return new Size(w, h); + } + + private Size GetPossibleSize(Size gridSize) + { + bool first, last; + HasShift(out first, out last); + + if (Orientation == Orientation.Horizontal) + return GetPossibleSizeHorizontal(gridSize, first, last); + + return GetPossibleSizeVertical(gridSize, first, last); + } + + private Size GetPossibleSizeVertical(Size gridSize, bool first, bool last) + { + int columns = ((first ? 0 : 1) + 2 * ColumnCount - (last ? 1 : 0)); + double w = 2 * (gridSize.Width / columns); + + int rows = 1 + 3 * RowCount; + double h = 4 * (gridSize.Height / rows); + + return new Size(w, h); + } + + private Size GetPossibleSizeHorizontal(Size gridSize, bool first, bool last) + { + int columns = 1 + 3 * ColumnCount; + double w = 4 * (gridSize.Width / columns); + + int rows = (first ? 0 : 1) + 2 * RowCount - (last ? 1 : 0); + double h = 2 * (gridSize.Height / rows); + + return new Size(w, h); + } + #endregion + + protected override Size ArrangeOverride(Size finalSize) + { + bool first, last; + HasShift(out first, out last); + Size hexSize = GetHexSize(finalSize); + double columnWidth, rowHeight; + if (Orientation == Orientation.Horizontal) + { + rowHeight = 0.50 * hexSize.Height; + columnWidth = 0.25 * hexSize.Width; + } + else + { + rowHeight = 0.25 * hexSize.Height; + columnWidth = 0.50 * hexSize.Width; + } + UIElementCollection elements = base.InternalChildren; + for (int i = 0; i < elements.Count; i++) + { + if (elements[i].Visibility == Visibility.Collapsed) + continue; + ArrangeElement(elements[i], hexSize, columnWidth, rowHeight, first); + } + + return finalSize; + } + + private void ArrangeElement(UIElement e, Size hexSize, double columnWidth, double rowHeight, bool shift) + { + int row = GetRow(e); + int column = GetColumn(e); + + double x; + double y; + + if (Orientation == Orientation.Horizontal) + { + x = 3 * columnWidth * column; + y = rowHeight * (2 * row + (column % 2 == 1 ? 1 : 0) + (shift ? -1 : 0)); + } + else + { + x = columnWidth * (2 * column + (row % 2 == 1 ? 1 : 0) + (shift ? -1 : 0)); + y = 3 * rowHeight * row; + } + + e.Arrange(new Rect(x, y, hexSize.Width, hexSize.Height)); + } + } + + + public class HexItem : ListBoxItem + { + static HexItem() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(HexItem), new FrameworkPropertyMetadata(typeof(HexItem))); + } + + public static readonly DependencyProperty OrientationProperty = HexGrid.OrientationProperty.AddOwner(typeof(HexItem)); + + public Orientation Orientation + { + get { return (Orientation)GetValue(OrientationProperty); } + set { SetValue(OrientationProperty, value); } + } + + } + + + public class HexList : ListBox + { + static HexList() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(HexList), new FrameworkPropertyMetadata(typeof(HexList))); + } + + public static readonly DependencyProperty OrientationProperty = HexGrid.OrientationProperty.AddOwner(typeof(HexList)); + + public static readonly DependencyProperty RowCountProperty = HexGrid.RowCountProperty.AddOwner(typeof(HexList)); + + public static readonly DependencyProperty ColumnCountProperty = HexGrid.ColumnCountProperty.AddOwner(typeof(HexList)); + + public Orientation Orientation + { + get { return (Orientation)GetValue(OrientationProperty); } + set { SetValue(OrientationProperty, value); } + } + + public int RowCount + { + get { return (int)GetValue(RowCountProperty); } + set { SetValue(RowCountProperty, value); } + } + + public int ColumnCount + { + get { return (int)GetValue(ColumnCountProperty); } + set { SetValue(ColumnCountProperty, value); } + } + + protected override bool IsItemItsOwnContainerOverride(object item) + { + return (item is HexItem); + } + + protected override DependencyObject GetContainerForItemOverride() + { + return new HexItem(); + } + } + + + public class HexClipConverter : IMultiValueConverter + { + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + { + double w = (double)values[0]; + double h = (double)values[1]; + Orientation o = (Orientation)values[2]; + + if (w <= 0 || h <= 0) + return null; + + PathFigure figure = o == Orientation.Horizontal + ? new PathFigure + { + StartPoint = new Point(0, h * 0.5), + Segments = + { + new LineSegment {Point = new Point(w*0.25, 0)}, + new LineSegment {Point = new Point(w*0.75, 0)}, + new LineSegment {Point = new Point(w, h*0.5)}, + new LineSegment {Point = new Point(w*0.75, h)}, + new LineSegment {Point = new Point(w*0.25, h)}, + } + } + : new PathFigure + { + StartPoint = new Point(w * 0.5, 0), + Segments = + { + new LineSegment {Point = new Point(w, h*0.25)}, + new LineSegment {Point = new Point(w, h*0.75)}, + new LineSegment {Point = new Point(w*0.5, h)}, + new LineSegment {Point = new Point(0, h*0.75)}, + new LineSegment {Point = new Point(0, h*0.25)}, + } + }; + return new PathGeometry { Figures = { figure } }; + } + + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/Source/Controls/H.Controls.Panel/Panel/ArcPanel.cs b/Source/Controls/H.Controls.Panel/Panel/ArcPanel.cs new file mode 100644 index 00000000..628bd931 --- /dev/null +++ b/Source/Controls/H.Controls.Panel/Panel/ArcPanel.cs @@ -0,0 +1,131 @@ +// Copyright © 2022 By HeBianGu(QQ:908293466) https://github.com/HeBianGu/WPF-ControlBase + +using System.Windows; +using System.Windows.Media; + +namespace H.Controls.Panel +{ + public class ArcPanel : PanelBase + { + public double Len + { + get { return (double)GetValue(LenProperty); } + set { SetValue(LenProperty, value); } + } + + // Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc... + public static readonly DependencyProperty LenProperty = + DependencyProperty.Register("Len", typeof(double), typeof(ArcPanel), new PropertyMetadata(100.0, (d, e) => + { + ArcPanel control = d as ArcPanel; + + if (control == null) return; + + //double config = e.NewValue as double; + + control.InvalidateArrange(); + + })); + + + public bool AngleToCenter + { + get { return (bool)GetValue(AngleToCenterProperty); } + set { SetValue(AngleToCenterProperty, value); } + } + + // Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc... + public static readonly DependencyProperty AngleToCenterProperty = + DependencyProperty.Register("AngleToCenter", typeof(bool), typeof(ArcPanel), new PropertyMetadata(default(bool), (d, e) => + { + ArcPanel control = d as ArcPanel; + + if (control == null) return; + + //bool config = e.NewValue as bool; + + control.InvalidateArrange(); + + })); + + public double StartAngle + { + get { return (double)GetValue(StartAngleProperty); } + set { SetValue(StartAngleProperty, value); } + } + + // Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc... + public static readonly DependencyProperty StartAngleProperty = + DependencyProperty.Register("StartAngle", typeof(double), typeof(ArcPanel), new PropertyMetadata(0.0, (d, e) => + { + ArcPanel control = d as ArcPanel; + + if (control == null) return; + + //double config = e.NewValue as double; + + control.InvalidateArrange(); + + })); + + + public double EndAngle + { + get { return (double)GetValue(EndAngleProperty); } + set { SetValue(EndAngleProperty, value); } + } + + // Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc... + public static readonly DependencyProperty EndAngleProperty = + DependencyProperty.Register("EndAngle", typeof(double), typeof(ArcPanel), new PropertyMetadata(360.0, (d, e) => + { + ArcPanel control = d as ArcPanel; + + if (control == null) return; + + //double config = e.NewValue as double; + + control.InvalidateArrange(); + + })); + + + protected override Size ArrangeOverride(Size finalSize) + { + System.Collections.Generic.List children = this.GetChildren(); + Point center = new Point(finalSize.Width / 2, finalSize.Height / 2); + Point start = new Point(finalSize.Width / 2 + Len, finalSize.Height / 2); + double totalAngle = this.EndAngle - this.StartAngle; + for (int i = 0; i < children.Count; i++) + { + UIElement elment = children[i]; + double angle = -this.StartAngle - (totalAngle / (children.Count - 1)) * i; + Matrix matrix = new Matrix(); + matrix.RotateAt(angle, center.X, center.Y); + Point end = matrix.Transform(start); + elment.Measure(finalSize); + elment.RenderTransformOrigin = new Point(0.5, 0.5); + end = new Point(end.X - elment.DesiredSize.Width / 2, end.Y - elment.DesiredSize.Height / 2); + elment.Arrange(new Rect(end, elment.DesiredSize)); + } + return base.ArrangeOverride(finalSize); + } + + protected override Size MeasureOverride(Size availableSize) + { + Size value = base.MeasureOverride(availableSize); + bool isAll = false; + + foreach (UIElement item in this.Children) + { + if (item.Visibility == Visibility.Visible) + { + isAll = true; break; + } + } + if (!isAll) + return value; + return new Size(this.Len * 2, this.Len * 2); + } + } +} diff --git a/Source/Controls/H.Controls.Panel/Panel/CirclePanel.cs b/Source/Controls/H.Controls.Panel/Panel/CirclePanel.cs new file mode 100644 index 00000000..5a47fc82 --- /dev/null +++ b/Source/Controls/H.Controls.Panel/Panel/CirclePanel.cs @@ -0,0 +1,88 @@ +// Copyright © 2022 By HeBianGu(QQ:908293466) https://github.com/HeBianGu/WPF-ControlBase + +using System.Windows; +using System.Windows.Media; + +namespace H.Controls.Panel +{ + public class CirclePanel : PanelBase + { + + public CirclePanel() + { + this.Loaded += (l, k) => + { + this.InvalidateArrange(); + }; + + } + public double Len + { + get { return (double)GetValue(LenProperty); } + set { SetValue(LenProperty, value); } + } + + public static readonly DependencyProperty LenProperty = + DependencyProperty.Register("Len", typeof(double), typeof(CirclePanel), new PropertyMetadata(100.0, (d, e) => + { + CirclePanel control = d as CirclePanel; + if (control == null) + return; + control.InvalidateArrange(); + })); + + + public bool AngleToCenter + { + get { return (bool)GetValue(AngleToCenterProperty); } + set { SetValue(AngleToCenterProperty, value); } + } + + public static readonly DependencyProperty AngleToCenterProperty = + DependencyProperty.Register("AngleToCenter", typeof(bool), typeof(CirclePanel), new PropertyMetadata(default(bool), (d, e) => + { + CirclePanel control = d as CirclePanel; + if (control == null) + return; + control.InvalidateArrange(); + + })); + + + protected override Size ArrangeOverride(Size finalSize) + { + System.Collections.Generic.List children = this.GetChildren(); + Point center = new Point(finalSize.Width / 2, finalSize.Height / 2); + Point start = new Point(finalSize.Width / 2 + Len, finalSize.Height / 2); + for (int i = 0; i < children.Count; i++) + { + UIElement elment = children[i]; + double angle = -(360.0 / children.Count) * i; + Matrix matrix = new Matrix(); + matrix.RotateAt(angle, center.X, center.Y); + Point end = matrix.Transform(start); + elment.Measure(finalSize); + elment.RenderTransformOrigin = new Point(0.5, 0.5); + end = new Point(end.X - elment.DesiredSize.Width / 2, end.Y - elment.DesiredSize.Height / 2); + elment.Arrange(new Rect(end, elment.DesiredSize)); + } + return base.ArrangeOverride(finalSize); + } + protected override Size MeasureOverride(Size availableSize) + { + Size value = base.MeasureOverride(availableSize); + bool isAll = false; + foreach (UIElement item in this.Children) + { + if (item.Visibility == Visibility.Visible) + { + isAll = true; + break; + } + } + if (!isAll) + return value; + return new Size(this.Len * 2, this.Len * 2); + } + } +} diff --git a/Source/Controls/H.Controls.Panel/Panel/CoverPanel.cs b/Source/Controls/H.Controls.Panel/Panel/CoverPanel.cs new file mode 100644 index 00000000..af18f43f --- /dev/null +++ b/Source/Controls/H.Controls.Panel/Panel/CoverPanel.cs @@ -0,0 +1,149 @@ +// Copyright © 2022 By HeBianGu(QQ:908293466) https://github.com/HeBianGu/WPF-ControlBase + + +using H.Providers.Mvvm; +using System; +using System.Windows; +using System.Windows.Input; + +namespace H.Controls.Panel +{ + /// 环状布局容器 + public class CoverPanel : PanelBase + { + public CoverPanel() + { + // Do :定位到中心位置 + { + CommandBinding binding = new CommandBinding(Commands.Selected); + + this.CommandBindings.Add(binding); + + binding.Executed += (l, k) => + { + if (k.Parameter is UIElement element) + { + int index = this.Children.IndexOf(element); + + if (index >= 0) + { + if (index > (this.Children.Count - 1) / 2) + { + this.StartIndex = (index - (this.Children.Count - 1) / 2); + } + else + { + this.StartIndex = this.Children.Count - ((this.Children.Count - 1) / 2 - index); + } + } + } + }; + } + } + + public double ScaleMin + { + get { return (double)GetValue(ScaleMinProperty); } + set { SetValue(ScaleMinProperty, value); } + } + + // Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc... + public static readonly DependencyProperty ScaleMinProperty = + DependencyProperty.Register("ScaleMin", typeof(double), typeof(CoverPanel), new PropertyMetadata(0.3, (d, e) => + { + CoverPanel control = d as CoverPanel; + + if (control == null) return; + + //double config = e.NewValue as double; + + control.InvalidateArrange(); + + })); + + + public double PressValue + { + get { return (double)GetValue(PressValueProperty); } + set { SetValue(PressValueProperty, value); } + } + + // Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc... + public static readonly DependencyProperty PressValueProperty = + DependencyProperty.Register("PressValue", typeof(double), typeof(CoverPanel), new PropertyMetadata(0.6, (d, e) => + { + CoverPanel control = d as CoverPanel; + + if (control == null) return; + + //double config = e.NewValue as double; + + control.InvalidateArrange(); + + })); + + + public VerticalAlignment VerticalContentAlignment + { + get { return (VerticalAlignment)GetValue(VerticalContentAlignmentProperty); } + set { SetValue(VerticalContentAlignmentProperty, value); } + } + + // Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc... + public static readonly DependencyProperty VerticalContentAlignmentProperty = + DependencyProperty.Register("VerticalContentAlignment", typeof(VerticalAlignment), typeof(CoverPanel), new PropertyMetadata(VerticalAlignment.Center, (d, e) => + { + CoverPanel control = d as CoverPanel; + + if (control == null) return; + + //VerticalAlignment config = e.NewValue as VerticalAlignment; + + control.InvalidateArrange(); + + })); + protected override Size ArrangeOverride(Size finalSize) + { + System.Collections.Generic.List children = this.GetChildren(); + Point center = new Point(finalSize.Width / 2, finalSize.Height / 2); + double len = finalSize.Width / (children.Count - 1); + double v = (children.Count - 1) / 2.0; + for (int i = 0; i < children.Count; i++) + { + UIElement elment = children[i]; + double centerAccent = v - i; + double centerValue = Math.Abs(centerAccent); + double scalePercent = (1 - this.ScaleMin) * (v - centerValue) / v + this.ScaleMin; + elment.RenderTransformOrigin = new Point(0.5, 0.5); + if (this.VerticalContentAlignment == VerticalAlignment.Bottom) + { + elment.RenderTransformOrigin = new Point(0.5, 1.0); + + } + else if (this.VerticalContentAlignment == VerticalAlignment.Top) + { + elment.RenderTransformOrigin = new Point(0.5, 0.0); + } + else + { + elment.RenderTransformOrigin = new Point(0.5, 0.5); + } + + System.Windows.Controls.Panel.SetZIndex(elment, children.Count - (int)centerValue); + Point end = new Point(center.X - centerAccent * len * this.PressValue, finalSize.Height / 2); + elment.Measure(finalSize); + end = new Point(end.X - elment.DesiredSize.Width / 2, end.Y - elment.DesiredSize.Height / 2); + elment.Arrange(new Rect(end, elment.DesiredSize)); + } + + return base.ArrangeOverride(finalSize); + } + + protected override Size MeasureOverride(Size availableSize) + { + return base.MeasureOverride(availableSize); + } + } + + +} diff --git a/Source/Controls/H.Controls.Panel/Panel/CrossPanel.cs b/Source/Controls/H.Controls.Panel/Panel/CrossPanel.cs new file mode 100644 index 00000000..f5cdff0b --- /dev/null +++ b/Source/Controls/H.Controls.Panel/Panel/CrossPanel.cs @@ -0,0 +1,178 @@ +// Copyright © 2022 By HeBianGu(QQ:908293466) https://github.com/HeBianGu/WPF-ControlBase + + +using H.Providers.Mvvm; +using System; +using System.Linq; +using System.Windows; +using System.Windows.Input; + +namespace H.Controls.Panel +{ + public class CrossPanel : PanelBase + { + public CrossPanel() + { + CommandBinding binding = new CommandBinding(Commands.Selected); + this.CommandBindings.Add(binding); + binding.Executed += (l, k) => + { + if (k.Parameter is UIElement element) + { + int index = this.Children.IndexOf(element); + + this.StartIndex = index; + } + }; + binding.CanExecute += (l, k) => + { + k.CanExecute = true; + }; + } + + public double SmallSize + { + get { return (double)GetValue(SmallSizeProperty); } + set { SetValue(SmallSizeProperty, value); } + } + + public static readonly DependencyProperty SmallSizeProperty = + DependencyProperty.Register("SmallSize", typeof(double), typeof(CrossPanel), new PropertyMetadata(100.0, (d, e) => + { + CrossPanel control = d as CrossPanel; + if (control == null) + return; + control.InvalidateArrange(); + })); + + + public int? RowCount + { + get { return (int?)GetValue(RowCountProperty); } + set { SetValue(RowCountProperty, value); } + } + + public static readonly DependencyProperty RowCountProperty = + DependencyProperty.Register("RowCount", typeof(int?), typeof(CrossPanel), new PropertyMetadata(null, (d, e) => + { + CrossPanel control = d as CrossPanel; + if (control == null) + return; + control.InvalidateArrange(); + })); + + + public int? ColumnCount + { + get { return (int?)GetValue(ColumnCountProperty); } + set { SetValue(ColumnCountProperty, value); } + } + + public static readonly DependencyProperty ColumnCountProperty = + DependencyProperty.Register("ColumnCount", typeof(int?), typeof(CrossPanel), new PropertyMetadata(null, (d, e) => + { + CrossPanel control = d as CrossPanel; + if (control == null) + return; + control.InvalidateArrange(); + + })); + + /// 最后一列平铺满 + public bool IsFull + { + get { return (bool)GetValue(IsFullProperty); } + set { SetValue(IsFullProperty, value); } + } + + public static readonly DependencyProperty IsFullProperty = + DependencyProperty.Register("IsFull", typeof(bool), typeof(CrossPanel), new PropertyMetadata(true, (d, e) => + { + CrossPanel control = d as CrossPanel; + if (control == null) + return; + control.InvalidateArrange(); + + })); + + protected override Size ArrangeOverride(Size finalSize) + { + System.Collections.Generic.List children = this.GetChildren(); + + Action action = (elment, end) => + { + elment.Arrange(new Rect(end, elment.DesiredSize)); + }; + FrameworkElement center = children[0] as FrameworkElement; + double cx = finalSize.Width / 2; + double cy = finalSize.Height / 2; + center.Width = finalSize.Width - this.SmallSize * 2; + center.Height = finalSize.Height - this.SmallSize * 2; + center.Measure(finalSize); + Point cp = new Point(cx - center.DesiredSize.Width / 2, cy - center.DesiredSize.Height / 2); + action(center, cp); + int count = (children.Count - 1); + double height = finalSize.Height - this.SmallSize * 2; + int xcount = this.ColumnCount.HasValue ? this.ColumnCount.Value : (int)(count * (finalSize.Width / (height + finalSize.Width))); + xcount = xcount + xcount % 2; + int ycount = this.RowCount.HasValue ? this.RowCount.Value : count - xcount; + ycount = ycount + ycount % 2; + Func GetPoint = (index, element) => + { + if (index < xcount / 2) + { + double len = finalSize.Width / (xcount / 2); + double ix = index * len + len / 2; + double iy = this.SmallSize / 2; + element.Width = len; + element.Height = this.SmallSize; + return new Point(ix, iy); + } + else if (index < xcount / 2 + ycount / 2) + { + double len = height / (ycount / 2); + double ix = finalSize.Width - this.SmallSize / 2; + double iy = (index - (xcount / 2)) * len + len / 2 + this.SmallSize; + element.Width = this.SmallSize; + element.Height = len; + return new Point(ix, iy); + } + else if (index < xcount + ycount / 2) + { + double len = finalSize.Width / (xcount / 2); + double ix = (index - (xcount / 2 + ycount / 2)) * len + len / 2; + double iy = finalSize.Height - this.SmallSize / 2; + element.Width = len; + element.Height = this.SmallSize; + return new Point(ix, iy); + } + else + { + // Do :最后一个单独处理,平铺满 + int yc = this.RowCount.HasValue ? this.RowCount.Value : count - xcount; + double len = this.IsFull && yc % 2 == 1 ? height / (yc - (ycount / 2)) : height / (ycount / 2); + double ix = this.SmallSize / 2; + double iy = (index - (xcount + ycount / 2)) * len + len / 2 + this.SmallSize; + element.Width = this.SmallSize; + element.Height = len; + return new Point(ix, iy); + } + }; + + { + int index = 0; + foreach (FrameworkElement item in children.Cast()) + { + if (item == center) + continue; + Point c = GetPoint(index, item); + item.Measure(finalSize); + Point ip = new Point(c.X - item.DesiredSize.Width / 2, c.Y - item.DesiredSize.Height / 2); + action(item, ip); + index++; + } + } + return base.ArrangeOverride(finalSize); + } + } +} diff --git a/Source/Controls/H.Controls.Panel/Panel/DelayStackPanel.cs b/Source/Controls/H.Controls.Panel/Panel/DelayStackPanel.cs new file mode 100644 index 00000000..5655d30f --- /dev/null +++ b/Source/Controls/H.Controls.Panel/Panel/DelayStackPanel.cs @@ -0,0 +1,167 @@ +// Copyright © 2022 By HeBianGu(QQ:908293466) https://github.com/HeBianGu/WPF-ControlBase + +using System; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Threading; + +namespace H.Controls.Panel +{ + /// + /// 用于加载时延时布局 + /// + public class DelayStackPanel : StackPanel + { + + public DispatcherPriority DispatcherPriority + { + get { return (DispatcherPriority)GetValue(DispatcherPriorityProperty); } + set { SetValue(DispatcherPriorityProperty, value); } + } + + // Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc... + public static readonly DependencyProperty DispatcherPriorityProperty = + DependencyProperty.Register("DispatcherPriority", typeof(DispatcherPriority), typeof(DelayStackPanel), new FrameworkPropertyMetadata(DispatcherPriority.Input, (d, e) => + { + DelayStackPanel control = d as DelayStackPanel; + + if (control == null) return; + + if (e.OldValue is DispatcherPriority o) + { + + } + + if (e.NewValue is DispatcherPriority n) + { + + } + + })); + + //protected override Size ArrangeOverride(Size arrangeSize) + //{ + // try + // { + // UIElementCollection children = this.InternalChildren; + // bool fHorizontal = (this.Orientation == Orientation.Horizontal); + // Rect rcChild = new Rect(arrangeSize); + // double previousChildSize = 0.0; + // this.Dispatcher.DelayInvoke(children, x => + // { + // UIElement child = x as UIElement; + // if (child == null) + // return; + // if (fHorizontal) + // { + // rcChild.X += previousChildSize; + // previousChildSize = child.DesiredSize.Width; + // rcChild.Width = previousChildSize; + // rcChild.Height = Math.Max(arrangeSize.Height, child.DesiredSize.Height); + // } + // else + // { + // rcChild.Y += previousChildSize; + // previousChildSize = child.DesiredSize.Height; + // rcChild.Height = previousChildSize; + // rcChild.Width = Math.Max(arrangeSize.Width, child.DesiredSize.Width); + // } + // child.Arrange(rcChild); + // }, null, this.DispatcherPriority); + // return arrangeSize; + // } + // finally + // { + + // } + //} + private bool _isRefreshing; + protected override Size ArrangeOverride(Size arrangeSize) + { + if (_isRefreshing) + return arrangeSize; + _isRefreshing = true; + + this.Dispatcher.BeginInvoke(this.DispatcherPriority, new Action(() => + { + System.Diagnostics.Debug.WriteLine("DelayInvoke:" + DateTime.Now); + _isRefreshing = false; + try + { + UIElementCollection children = this.InternalChildren; + bool fHorizontal = (this.Orientation == Orientation.Horizontal); + Rect rcChild = new Rect(arrangeSize); + double previousChildSize = 0.0; + for (int i = 0, count = children.Count; i < count; ++i) + { + UIElement child = (UIElement)children[i]; + + if (child == null) { continue; } + + Application.Current.Dispatcher.BeginInvoke(this.DispatcherPriority, new Action(() => + { + if (_isRefreshing) + return; + if (fHorizontal) + { + rcChild.X += previousChildSize; + previousChildSize = child.DesiredSize.Width; + rcChild.Width = previousChildSize; + rcChild.Height = Math.Max(arrangeSize.Height, child.DesiredSize.Height); + } + else + { + rcChild.Y += previousChildSize; + previousChildSize = child.DesiredSize.Height; + rcChild.Height = previousChildSize; + rcChild.Width = Math.Max(arrangeSize.Width, child.DesiredSize.Width); + } + child.Arrange(rcChild); + //System.Diagnostics.Debug.WriteLine("DelayInvokeItem:" + DateTime.Now); + + })); + } + } + finally + { + + } + })); + + return arrangeSize; + } + + + //private bool _isRefreshing; + //protected override Size ArrangeOverride(Size arrangeSize) + //{ + // if (_isRefreshing) + // return arrangeSize; + // _isRefreshing = true; + + // this.Dispatcher.BeginInvoke(this.DispatcherPriority, new Action(() => + // { + // System.Diagnostics.Debug.WriteLine("DelayInvoke:" + DateTime.Now); + // _isRefreshing = false; + // base.ArrangeOverride(arrangeSize); + // })); + // return arrangeSize; + //} + + //private bool _isRefreshing11; + //protected override Size MeasureOverride(Size constraint) + //{ + // if (_isRefreshing11) + // return constraint; + // _isRefreshing11 = true; + + // this.Dispatcher.BeginInvoke(this.DispatcherPriority, new Action(() => + // { + // System.Diagnostics.Debug.WriteLine("DelayInvoke:" + DateTime.Now); + // _isRefreshing11 = false; + // base.MeasureOverride(constraint); + // })); + // return constraint; + //} + } +} diff --git a/Source/Controls/H.Controls.Panel/Panel/DockPanel.cs b/Source/Controls/H.Controls.Panel/Panel/DockPanel.cs new file mode 100644 index 00000000..710593d3 --- /dev/null +++ b/Source/Controls/H.Controls.Panel/Panel/DockPanel.cs @@ -0,0 +1,463 @@ +// Copyright © 2022 By HeBianGu(QQ:908293466) https://github.com/HeBianGu/WPF-ControlBase + + +using H.Providers.Mvvm; +using System; +using System.Linq; +using System.Windows; +using System.Windows.Input; + +namespace H.Controls.Panel +{ + /// 停靠控件 + public class DockPanel : PanelBase + { + public DockPanel() + { + // Do :定位到中心位置 + { + CommandBinding binding = new CommandBinding(Commands.Selected); + + this.CommandBindings.Add(binding); + + binding.Executed += (l, k) => + { + if (k.Parameter is UIElement element) + { + int index = this.Children.IndexOf(element); + + //if (index >= 0) + //{ + // if (index > (this.Children.Count - 1) / 2) + // { + // this.StartIndex = (index - (this.Children.Count - 1) / 2); + // } + // else + // { + // this.StartIndex = this.Children.Count - ((this.Children.Count - 1) / 2 - index); + // } + //} + + this.StartIndex = index; + } + }; + + binding.CanExecute += (l, k) => { k.CanExecute = true; }; + + } + } + + + public DockType Dock + { + get { return (DockType)GetValue(DockProperty); } + set { SetValue(DockProperty, value); } + } + + // Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc... + public static readonly DependencyProperty DockProperty = + DependencyProperty.Register("Dock", typeof(DockType), typeof(DockPanel), new PropertyMetadata(default(DockType), (d, e) => + { + DockPanel control = d as DockPanel; + + if (control == null) return; + + //Dock config = e.NewValue as Dock; + control.InvalidateArrange(); + })); + + + public bool IsFull + { + get { return (bool)GetValue(IsFullProperty); } + set { SetValue(IsFullProperty, value); } + } + + // Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc... + public static readonly DependencyProperty IsFullProperty = + DependencyProperty.Register("IsFull", typeof(bool), typeof(DockPanel), new PropertyMetadata(true, (d, e) => + { + DockPanel control = d as DockPanel; + + if (control == null) return; + + //bool config = e.NewValue as bool; + + control.InvalidateArrange(); + })); + + + public double SmallSize + { + get { return (double)GetValue(SmallSizeProperty); } + set { SetValue(SmallSizeProperty, value); } + } + + // Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc... + public static readonly DependencyProperty SmallSizeProperty = + DependencyProperty.Register("SmallSize", typeof(double), typeof(DockPanel), new PropertyMetadata(100.0, (d, e) => + { + DockPanel control = d as DockPanel; + + if (control == null) return; + + //double config = e.NewValue as double; + + control.InvalidateArrange(); + + })); + + + + protected override Size ArrangeOverride(Size finalSize) + { + System.Collections.Generic.List children = this.GetChildren(); + + Action action = (elment, end) => + { + elment.Arrange(new Rect(end, elment.DesiredSize)); + }; + + if (this.Dock == DockType.Bottom) + { + // Do :放置大图 + FrameworkElement center = children[0] as FrameworkElement; + + double cx = finalSize.Width / 2; + double cy = (finalSize.Height - this.SmallSize) / 2; + + center.Width = finalSize.Width; + + center.Height = finalSize.Height - this.SmallSize; + + center.Measure(finalSize); + + Point cp = new Point(cx - center.DesiredSize.Width / 2, cy - center.DesiredSize.Height / 2); + + action(center, cp); + + // Do :放置缩略图 + + int count = children.Count - 1; + + double len = finalSize.Width / count; + + int index = 0; + + foreach (FrameworkElement item in children.Cast()) + { + if (item == center) continue; + + double ix = index * len + len / 2; + + double iy = finalSize.Height - this.SmallSize / 2; + + item.Width = len; + + item.Height = this.SmallSize; + + item.Measure(finalSize); + + Point ip = new Point(ix - item.DesiredSize.Width / 2, iy - item.DesiredSize.Height / 2); + + action(item, ip); + + index++; + } + + } + + else if (this.Dock == DockType.Top) + { + // Do :放置大图 + FrameworkElement center = children[0] as FrameworkElement; + + double cx = finalSize.Width / 2; + double cy = (finalSize.Height - this.SmallSize) / 2 + this.SmallSize; + + center.Width = finalSize.Width; + + center.Height = finalSize.Height - this.SmallSize; + + center.Measure(finalSize); + + Point cp = new Point(cx - center.DesiredSize.Width / 2, cy - center.DesiredSize.Height / 2); + + action(center, cp); + + // Do :放置缩略图 + + int count = children.Count - 1; + + double len = finalSize.Width / count; + + int index = 0; + + foreach (FrameworkElement item in children.Cast()) + { + if (item == center) continue; + + double ix = index * len + len / 2; + + double iy = this.SmallSize / 2; + + item.Width = len; + + item.Height = this.SmallSize; + + item.Measure(finalSize); + + Point ip = new Point(ix - item.DesiredSize.Width / 2, iy - item.DesiredSize.Height / 2); + + action(item, ip); + + index++; + } + + } + + else if (this.Dock == DockType.TopAndBottom) + { + // Do :放置大图 + FrameworkElement center = children[0] as FrameworkElement; + + double cx = finalSize.Width / 2; + + double cy = finalSize.Height / 2; + + center.Width = finalSize.Width; + + center.Height = finalSize.Height - this.SmallSize * 2; + + center.Measure(finalSize); + + Point cp = new Point(cx - center.DesiredSize.Width / 2, cy - center.DesiredSize.Height / 2); + + action(center, cp); + + // Do :放置底部缩略图 + + int count = (children.Count - 1) / 2 + (children.Count - 1) % 2; + + Func GetLen = index => + { + if (IsFull && (children.Count - 1) % 2 == 1 && index >= count) + { + return finalSize.Width / (count - 1); + } + else + { + return finalSize.Width / count; + } + }; + + { + int index = 0; + + foreach (FrameworkElement item in children.Cast()) + { + if (item == center) continue; + + double len = GetLen(index); + + double ix = index < count ? index * len + len / 2 : (index - count) * len + len / 2; + + double iy = index < count ? finalSize.Height - this.SmallSize / 2 : this.SmallSize / 2; + + item.Width = len; + + item.Height = this.SmallSize; + + item.Measure(finalSize); + + Point ip = new Point(ix - item.DesiredSize.Width / 2, iy - item.DesiredSize.Height / 2); + + action(item, ip); + + index++; + } + } + } + + else if (this.Dock == DockType.Left) + { + // Do :放置大图 + FrameworkElement center = children[0] as FrameworkElement; + + double cx = (finalSize.Width - this.SmallSize) / 2 + this.SmallSize; + + double cy = finalSize.Height / 2; + + center.Width = finalSize.Width - this.SmallSize; + + center.Height = finalSize.Height; + + center.Measure(finalSize); + + Point cp = new Point(cx - center.DesiredSize.Width / 2, cy - center.DesiredSize.Height / 2); + + action(center, cp); + + // Do :放置缩略图 + + int count = children.Count - 1; + + double len = finalSize.Height / count; + + int index = 0; + + foreach (FrameworkElement item in children.Cast()) + { + if (item == center) continue; + + double ix = this.SmallSize / 2; + + double iy = index * len + len / 2; + + item.Height = len; + + item.Width = this.SmallSize; + + item.Measure(finalSize); + + Point ip = new Point(ix - item.DesiredSize.Width / 2, iy - item.DesiredSize.Height / 2); + + action(item, ip); + + index++; + } + + } + + else if (this.Dock == DockType.Right) + { + // Do :放置大图 + FrameworkElement center = children[0] as FrameworkElement; + + double cx = (finalSize.Width - this.SmallSize) / 2; + + double cy = finalSize.Height / 2; + + center.Width = finalSize.Width - this.SmallSize; + + center.Height = finalSize.Height; + + center.Measure(finalSize); + + Point cp = new Point(cx - center.DesiredSize.Width / 2, cy - center.DesiredSize.Height / 2); + + action(center, cp); + + // Do :放置缩略图 + + int count = children.Count - 1; + + double len = finalSize.Height / count; + + int index = 0; + + foreach (FrameworkElement item in children.Cast()) + { + if (item == center) continue; + + double ix = finalSize.Width - this.SmallSize / 2; + + double iy = index * len + len / 2; + + item.Height = len; + + item.Width = this.SmallSize; + + item.Measure(finalSize); + + Point ip = new Point(ix - item.DesiredSize.Width / 2, iy - item.DesiredSize.Height / 2); + + action(item, ip); + + index++; + } + + } + + else if (this.Dock == DockType.LeftAndRight) + { + // Do :放置大图 + FrameworkElement center = children[0] as FrameworkElement; + + double cx = finalSize.Width / 2; + + double cy = finalSize.Height / 2; + + center.Width = finalSize.Width - this.SmallSize * 2; + + center.Height = finalSize.Height; + + center.Measure(finalSize); + + Point cp = new Point(cx - center.DesiredSize.Width / 2, cy - center.DesiredSize.Height / 2); + + action(center, cp); + + // Do :放置底部缩略图 + + int count = (children.Count - 1) / 2 + (children.Count - 1) % 2; + + Func GetLen = index => + { + if (IsFull && (children.Count - 1) % 2 == 1 && index >= count) + { + return finalSize.Height / (count - 1); + } + else + { + return finalSize.Height / count; + } + }; + + { + int index = 0; + + foreach (FrameworkElement item in children.Cast()) + { + if (item == center) continue; + + double len = GetLen(index); + + double ix = index < count ? finalSize.Width - this.SmallSize / 2 : this.SmallSize / 2; + + double iy = index < count ? index * len + len / 2 : (index - count) * len + len / 2; + + item.Width = this.SmallSize; + + item.Height = len; + + item.Measure(finalSize); + + Point ip = new Point(ix - item.DesiredSize.Width / 2, iy - item.DesiredSize.Height / 2); + + action(item, ip); + + index++; + } + } + } + + return base.ArrangeOverride(finalSize); + } + + + + // Do :用于设置整个容器的大小 + protected override Size MeasureOverride(Size availableSize) + { + return base.MeasureOverride(availableSize); + } + } + + public enum DockType + { + Left, Right, Bottom, Top, LeftAndRight, TopAndBottom + } + + +} diff --git a/Source/Controls/H.Controls.Panel/Panel/DragPanel.cs b/Source/Controls/H.Controls.Panel/Panel/DragPanel.cs new file mode 100644 index 00000000..f69dd406 --- /dev/null +++ b/Source/Controls/H.Controls.Panel/Panel/DragPanel.cs @@ -0,0 +1,477 @@ +// Copyright © 2022 By HeBianGu(QQ:908293466) https://github.com/HeBianGu/WPF-ControlBase + +using System; +using System.Collections; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; + +namespace H.Controls.Panel +{ + public partial class DragPanel : System.Windows.Controls.Panel + { + #region private vars + private Size _calculatedSize; + private bool _isNotFirstArrange = false; + private int columns, rows; + #endregion + static DragPanel() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(DragPanel), new FrameworkPropertyMetadata(typeof(DragPanel))); + } + + public DragPanel() : base() + { + this.AddHandler(Mouse.MouseMoveEvent, new MouseEventHandler(OnMouseMove), false); + this.MouseLeftButtonUp += OnMouseUp; + this.LostMouseCapture += OnLostMouseCapture; + } + + private UIElement GetChildThatHasMouseOver() + { + return GetParent(Mouse.DirectlyOver as DependencyObject, (ve) => Children.Contains(ve as UIElement)) as UIElement; + } + + private Point GetItemVisualPoint(UIElement element) + { + TransformGroup group = (TransformGroup)element.RenderTransform; + TranslateTransform trans = (TranslateTransform)group.Children[0]; + + return new Point(trans.X, trans.Y); + } + + private int GetIndexFromPoint(double x, double y) + { + int columnIndex = (int)Math.Truncate(x / itemContainterWidth); + int rowIndex = (int)Math.Truncate(y / itemContainterHeight); + return columns * rowIndex + columnIndex; + } + + private int GetIndexFromPoint(Point p) + { + return GetIndexFromPoint(p.X, p.Y); + } + + #region dependency properties + public static readonly DependencyProperty ItemsWidthProperty = DependencyProperty.Register( + "ItemsWidth", + typeof(double), + typeof(DragPanel), + new FrameworkPropertyMetadata(150d)); + + public static readonly DependencyProperty ItemsHeightProperty = DependencyProperty.Register( + "ItemsHeight", + typeof(double), + typeof(DragPanel), + new FrameworkPropertyMetadata(60d)); + + public static readonly DependencyProperty ItemSeparationProperty = DependencyProperty.Register( + "ItemSeparation", + typeof(Thickness), + typeof(DragPanel), + new FrameworkPropertyMetadata()); + + // Using a DependencyProperty as the backing store for AnimationMilliseconds. This enables animation, styling, binding, etc... + public static readonly DependencyProperty AnimationMillisecondsProperty = DependencyProperty.Register("AnimationMilliseconds", typeof(int), typeof(DragPanel), new FrameworkPropertyMetadata(200)); + + public static readonly DependencyProperty SwapCommandProperty = DependencyProperty.Register( + "SwapCommand", + typeof(ICommand), + typeof(DragPanel), + new FrameworkPropertyMetadata(null)); + + #endregion + + #region properties + public double ItemsWidth + { + get { return (double)GetValue(ItemsWidthProperty); } + set { SetValue(ItemsWidthProperty, value); } + } + public double ItemsHeight + { + get { return (double)GetValue(ItemsHeightProperty); } + set { SetValue(ItemsHeightProperty, value); } + } + public Thickness ItemSeparation + { + get { return (Thickness)this.GetValue(ItemSeparationProperty); } + set { this.SetValue(ItemSeparationProperty, value); } + } + public int AnimationMilliseconds + { + get { return (int)GetValue(AnimationMillisecondsProperty); } + set { SetValue(AnimationMillisecondsProperty, value); } + } + private double itemContainterHeight + { + get { return ItemSeparation.Top + ItemsHeight + ItemSeparation.Bottom; } + } + private double itemContainterWidth + { + get { return ItemSeparation.Left + ItemsWidth + ItemSeparation.Right; } + } + public ICommand SwapCommand + { + get { return (ICommand)GetValue(SwapCommandProperty); } + set { SetValue(SwapCommandProperty, value); } + } + #endregion + + #region transformation things + private void AnimateAll() + { + //Apply exactly the same algorithm, but instide of Arrange a call AnimateTo method + double colPosition = 0; + double rowPosition = 0; + foreach (UIElement child in Children) + { + if (child != _draggedElement) + AnimateTo(child, colPosition + ItemSeparation.Left, rowPosition + ItemSeparation.Top, _isNotFirstArrange ? AnimationMilliseconds : 0); + //drag will locate dragged element + colPosition += itemContainterWidth; + if (colPosition + 1 > _calculatedSize.Width) + { + colPosition = 0; + rowPosition += itemContainterHeight; + } + } + } + + private void AnimateTo(UIElement child, double x, double y, int duration) + { + TransformGroup group = (TransformGroup)child.RenderTransform; + TranslateTransform trans = (TranslateTransform)group.Children.First((groupElement) => groupElement is TranslateTransform); + trans.BeginAnimation(TranslateTransform.XProperty, MakeAnimation(x, duration)); + trans.BeginAnimation(TranslateTransform.YProperty, MakeAnimation(y, duration)); + } + + private DoubleAnimation MakeAnimation(double to, int duration) + { + DoubleAnimation anim = new DoubleAnimation(to, TimeSpan.FromMilliseconds(duration)); + anim.AccelerationRatio = 0.2; + anim.DecelerationRatio = 0.7; + return anim; + } + #endregion + + #region measure + protected override Size MeasureOverride(Size availableSize) + { + Size itemContainerSize = new Size(itemContainterWidth, itemContainterHeight); + int count = 0; //for not call it again + foreach (UIElement child in Children) + { + child.Measure(itemContainerSize); + count++; + } + if (availableSize.Width < itemContainterWidth) + _calculatedSize = new Size(itemContainterWidth, count * itemContainterHeight); //the size of nX1 + else + { + columns = (int)Math.Truncate(availableSize.Width / itemContainterWidth); + rows = count / columns; + if (count % columns != 0) + rows++; + _calculatedSize = new Size(columns * itemContainterWidth, rows * itemContainterHeight); + } + return _calculatedSize; + } + #endregion + + #region arrange + protected override Size ArrangeOverride(Size finalSize) + { + Size _finalItemSize = new Size(ItemsWidth, ItemsHeight); + //if is animated then arrange elements to 0,0, and then put them on its location using the transform + foreach (UIElement child in InternalChildren) + { + // If this is the first time we've seen this child, add our transforms + if (child.RenderTransform as TransformGroup == null) + { + child.RenderTransformOrigin = new Point(0.5, 0.5); + TransformGroup group = new TransformGroup(); + child.RenderTransform = group; + group.Children.Add(new TranslateTransform()); + } + //locate all children in 0,0 point//TODO: use infinity and then scale each element to items size + child.Arrange(new Rect(new Point(0, 0), _finalItemSize)); //when use transformations change to childs.DesireSize + } + AnimateAll(); + + if (!_isNotFirstArrange) + _isNotFirstArrange = true; + + return _calculatedSize; + } + #endregion + + #region Static + //this can be an extension method + public static DependencyObject GetParent(DependencyObject o, Func matchFunction) + { + DependencyObject t = o; + do + { + t = VisualTreeHelper.GetParent(t); + } while (t != null && !matchFunction.Invoke(t)); + return t; + } + #endregion + + //TODO: Add IsEditing property + //TODO: Add Scale transform to items for fill items area + } + + public partial class DragPanel + { + #region const drag + private const double mouseDif = 2d; + private const int mouseTimeDif = 25; + #endregion + + #region private + private UIElement __draggedElement; + + public UIElement _draggedElement + { + get { return __draggedElement; } + set + { + __draggedElement = value; + } + } + + private int _draggedIndex; + private bool _firstScrollRequest = true; + private ScrollViewer _scrollContainer; + + private ScrollViewer scrollViewer + { + get + { + if (_firstScrollRequest && _scrollContainer == null) + { + _firstScrollRequest = false; + _scrollContainer = (ScrollViewer)GetParent(this, (ve) => ve is ScrollViewer); + } + return _scrollContainer; + } + } + #endregion + + #region private drag + private double _lastMousePosX; + private double _lastMousePosY; + private int _lastMouseMoveTime; + private double _x; + private double _y; + private Rect _rectOnDrag; + #endregion + + + private void OnMouseMove(object sender, MouseEventArgs e) + { + if (e.LeftButton == MouseButtonState.Pressed && _draggedElement == null && !this.IsMouseCaptured) + StartDrag(e); + else if (_draggedElement != null) + OnDragOver(e); + } + + private void OnDragOver(MouseEventArgs e) + { + Point mousePos = Mouse.GetPosition(this); + double difX = mousePos.X - _lastMousePosX; + double difY = mousePos.Y - _lastMousePosY; + + int timeDif = e.Timestamp - _lastMouseMoveTime; + if ((Math.Abs(difX) > mouseDif || Math.Abs(difY) > mouseDif) && timeDif > mouseTimeDif) + { + //this lines is for keepn draged item inside control bounds + DoScroll(); + + if (_x + difX < _rectOnDrag.Location.X) + _x = 0; + else if (ItemsWidth + _x + difX > _rectOnDrag.Location.X + _rectOnDrag.Width) + _x = _rectOnDrag.Location.X + _rectOnDrag.Width - ItemsWidth; + else if (mousePos.X > _rectOnDrag.Location.X && mousePos.X < _rectOnDrag.Location.X + _rectOnDrag.Width) + _x += difX; + if (_y + difY < _rectOnDrag.Location.Y) + _y = 0; + else if (ItemsHeight + _y + difY > _rectOnDrag.Location.Y + _rectOnDrag.Height) + _y = _rectOnDrag.Location.Y + _rectOnDrag.Height - ItemsHeight; + else if (mousePos.Y > _rectOnDrag.Location.Y && mousePos.Y < _rectOnDrag.Location.Y + _rectOnDrag.Height) + _y += difY; + //lines ends + + AnimateTo(_draggedElement, _x, _y, 0); + _lastMousePosX = mousePos.X; + _lastMousePosY = mousePos.Y; + _lastMouseMoveTime = e.Timestamp; + SwapElement(_x + ItemsWidth / 2, _y + ItemsHeight / 2); + } + } + + private void StartDrag(MouseEventArgs e) + { + Point mousePos = Mouse.GetPosition(this); + _draggedElement = GetChildThatHasMouseOver(); + if (_draggedElement == null) + return; + _draggedIndex = Children.IndexOf(_draggedElement); + _rectOnDrag = VisualTreeHelper.GetDescendantBounds(this); + Point p = GetItemVisualPoint(_draggedElement); + _x = p.X; + _y = p.Y; + SetZIndex(_draggedElement, 1000); + _lastMousePosX = mousePos.X; + _lastMousePosY = mousePos.Y; + _lastMouseMoveTime = e.Timestamp; + this.InvalidateArrange(); + e.Handled = true; + this.CaptureMouse(); + } + + private void OnMouseUp(object sender, MouseEventArgs e) + { + if (this.IsMouseCaptured) + ReleaseMouseCapture(); + } + + private void SwapElement(double x, double y) + { + int index = GetIndexFromPoint(x, y); + + if (index == _draggedIndex || index < 0) return; + + if (index >= Children.Count) + index = Children.Count - 1; + + int[] parameter = new int[] { _draggedIndex, index }; + + if (SwapCommand != null && SwapCommand.CanExecute(parameter)) + { + SwapCommand.Execute(parameter); + + _draggedElement = Children[index]; + + //this is bcause after changing the collection the element is other + FillNewDraggedChild(_draggedElement); + + _draggedIndex = index; + } + else + { + // Do :设置更新ListBox数据 + if (this.AutoSwapItems(_draggedIndex, index)) + { + _draggedElement = Children[index]; + + //this is bcause after changing the collection the element is other + FillNewDraggedChild(_draggedElement); + + _draggedIndex = index; + } + } + + this.InvalidateArrange(); + } + + private bool AutoSwapItems(int oldIndex, int newIndex) + { + ItemsControl list = ItemsControl.GetItemsOwner(this); + + if (list == null) return false; + + object elementSource = list.Items[newIndex]; + + object dragged = list.Items[oldIndex]; + + if (list.ItemsSource is IList l) + { + l.Remove(dragged); + l.Insert(newIndex, dragged); + } + else + { + list.Items.Remove(dragged); + list.Items.Insert(newIndex, dragged); + } + + + //if (oldIndex > newIndex) + //{ + // list.Items.Remove(dragged); + // list.Items.Insert(newIndex, dragged); + //} + //else + //{ + // list.Items.Remove(dragged); + // list.Items.Insert(newIndex, dragged); + //} + + return true; + + } + + private void FillNewDraggedChild(UIElement child) + { + if (child.RenderTransform as TransformGroup == null) + { + child.RenderTransformOrigin = new Point(0.5, 0.5); + TransformGroup group = new TransformGroup(); + child.RenderTransform = group; + group.Children.Add(new TranslateTransform()); + } + SetZIndex(child, 1000); + AnimateTo(child, _x, _y, 0); //need relocate the element + } + + private void OnLostMouseCapture(object sender, MouseEventArgs e) + { + FinishDrag(); + } + + private void FinishDrag() + { + if (_draggedElement != null) + { + SetZIndex(_draggedElement, 0); + _draggedElement = null; + this.InvalidateArrange(); + } + } + + private void DoScroll() + { + if (scrollViewer != null) + { + Point position = Mouse.GetPosition(scrollViewer); + double scrollMargin = Math.Min(scrollViewer.FontSize * 2, scrollViewer.ActualHeight / 2); + + if (position.X >= scrollViewer.ActualWidth - scrollMargin && + scrollViewer.HorizontalOffset < scrollViewer.ExtentWidth - scrollViewer.ViewportWidth) + { + scrollViewer.LineRight(); + } + else if (position.X < scrollMargin && scrollViewer.HorizontalOffset > 0) + { + scrollViewer.LineLeft(); + } + else if (position.Y >= scrollViewer.ActualHeight - scrollMargin && + scrollViewer.VerticalOffset < scrollViewer.ExtentHeight - scrollViewer.ViewportHeight) + { + scrollViewer.LineDown(); + } + else if (position.Y < scrollMargin && scrollViewer.VerticalOffset > 0) + { + scrollViewer.LineUp(); + } + } + } + } + +} diff --git a/Source/Controls/H.Controls.Panel/Panel/GridAreaAttach.cs b/Source/Controls/H.Controls.Panel/Panel/GridAreaAttach.cs new file mode 100644 index 00000000..8088a357 --- /dev/null +++ b/Source/Controls/H.Controls.Panel/Panel/GridAreaAttach.cs @@ -0,0 +1,574 @@ +// Copyright © 2022 By HeBianGu(QQ:908293466) https://github.com/HeBianGu/WPF-ControlBase + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Windows; +using System.Windows.Controls; + +namespace H.Controls.Panel +{ + using LayoutUpdateEventHandler = EventHandler; + + public class AreaDefinition + { + public int Row { get; set; } + public int Column { get; set; } + public int RowSpan { get; set; } + public int ColumnSpan { get; set; } + + public AreaDefinition(int row, int column, int rowSpan, int columnSpan) + { + this.Row = row; + this.Column = column; + this.RowSpan = rowSpan; + this.ColumnSpan = columnSpan; + } + } + + public class NamedAreaDefinition : AreaDefinition + { + public string Name { get; set; } + + public NamedAreaDefinition(string name, int row, int column, int rowSpan, int columnSpan) + : base(row, column, rowSpan, columnSpan) + { + this.Name = name; + } + } + + internal struct GridLengthDefinition + { + public GridLength GridLength; + public double? Min; + public double? Max; + } + + public static class GridAreaAttach + { + public static Orientation GetAutoFillOrientation(DependencyObject obj) + { + return (Orientation)obj.GetValue(AutoFillOrientationProperty); + } + public static void SetAutoFillOrientation(DependencyObject obj, Orientation value) + { + obj.SetValue(AutoFillOrientationProperty, value); + } + public static readonly DependencyProperty AutoFillOrientationProperty = + DependencyProperty.RegisterAttached("AutoFillOrientation", typeof(Orientation), typeof(GridAreaAttach), new PropertyMetadata(Orientation.Horizontal)); + + + public static bool GetAutoFillChildren(DependencyObject obj) + { + return (bool)obj.GetValue(AutoFillChildrenProperty); + } + public static void SetAutoFillChildren(DependencyObject obj, bool value) + { + obj.SetValue(AutoFillChildrenProperty, value); + } + public static readonly DependencyProperty AutoFillChildrenProperty = + DependencyProperty.RegisterAttached("AutoFillChildren", typeof(bool), typeof(GridAreaAttach), new PropertyMetadata(false, OnAutoFillChildrenChanged)); + + private static void OnAutoFillChildrenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + Grid grid = d as Grid; + bool isEnabled = (bool)e.NewValue; + if (grid == null) { return; } + + if (isEnabled) + { + LayoutUpdateEventHandler layoutUpdateCallback = CreateLayoutUpdateHandler(grid); + grid.LayoutUpdated += layoutUpdateCallback; + SetLayoutUpdatedCallback(grid, layoutUpdateCallback); + + AutoFill(grid); + } + else + { + LayoutUpdateEventHandler callback = GetLayoutUpdatedCallback(grid); + grid.LayoutUpdated -= callback; + + ClearAutoFill(grid); + } + } + + private static LayoutUpdateEventHandler CreateLayoutUpdateHandler(Grid grid) + { + int prevCount = 0; + int prevColumn = grid.ColumnDefinitions.Count; + int prevRow = grid.RowDefinitions.Count; + Orientation prevOrientation = GetAutoFillOrientation(grid); + + LayoutUpdateEventHandler layoutUpdateCallback = new LayoutUpdateEventHandler((sender, args) => + { + int count = grid.Children.Count; + int column = grid.ColumnDefinitions.Count; + int row = grid.RowDefinitions.Count; + Orientation orientation = GetAutoFillOrientation(grid); + + if (count != prevCount || + column != prevColumn || + row != prevRow || + orientation != prevOrientation) + { + AutoFill(grid); + prevCount = count; + prevColumn = column; + prevRow = row; + prevOrientation = orientation; + } + }); + + return layoutUpdateCallback; + } + + public static LayoutUpdateEventHandler GetLayoutUpdatedCallback(DependencyObject obj) + { + return (LayoutUpdateEventHandler)obj.GetValue(LayoutUpdatedCallbackProperty); + } + private static void SetLayoutUpdatedCallback(DependencyObject obj, LayoutUpdateEventHandler value) + { + obj.SetValue(LayoutUpdatedCallbackProperty, value); + } + public static readonly DependencyProperty LayoutUpdatedCallbackProperty = + DependencyProperty.RegisterAttached("LayoutUpdatedCallback", typeof(LayoutUpdateEventHandler), typeof(GridAreaAttach), new PropertyMetadata(null)); + + private static void AutoFill(Grid grid) + { + bool isEnabled = GetAutoFillChildren(grid); + int rowCount = grid.RowDefinitions.Count; + int columnCount = grid.ColumnDefinitions.Count; + Orientation orientation = GetAutoFillOrientation(grid); + + if (!isEnabled || rowCount == 0 || columnCount == 0) return; + + bool[,] area = new bool[rowCount, columnCount]; + + List autoLayoutList = new List(); + foreach (FrameworkElement child in grid.Children) + { + AreaDefinition region = GetAreaNameRegion(child) ?? GetAreaRegion(child); + bool isFixed = region != null; + + if (isFixed) + { + int row = region.Row; + int column = region.Column; + int rowSpan = region.RowSpan; + int columnSpan = region.ColumnSpan; + + for (int i = row; i < row + rowSpan; i++) + for (int j = column; j < column + columnSpan; j++) + { + if (columnCount <= j || rowCount <= i) { continue; } + area[i, j] = true; + } + } + else + { + autoLayoutList.Add(child); + } + + } + + int count = 0; + int numOfCell = rowCount * columnCount; + bool isHorizontal = orientation == Orientation.Horizontal; + bool isOverflow = false; + foreach (FrameworkElement child in autoLayoutList) + { + if (child.Visibility == Visibility.Collapsed) + { + continue; + } + + while (true) + { + int x = isHorizontal ? count % columnCount : count / rowCount; + int y = isHorizontal ? count / columnCount : count % rowCount; + bool canArrange = isOverflow ? true : !area[y, x]; + if (canArrange) + { + Grid.SetRow(child, y); + Grid.SetColumn(child, x); + Grid.SetRowSpan(child, 1); + Grid.SetColumnSpan(child, 1); + } + + if (count + 1 < numOfCell) + { + count++; + } + else + { + isOverflow = true; + } + + if (canArrange) + { + break; + } + } + + } + } + + private static void ClearAutoFill(Grid grid) + { + foreach (FrameworkElement child in grid.Children) + { + child.ClearValue(Grid.RowProperty); + child.ClearValue(Grid.ColumnProperty); + child.ClearValue(Grid.RowSpanProperty); + child.ClearValue(Grid.ColumnSpanProperty); + + UpdateItemPosition(child); + } + } + + public static string GetColumnDefinition(DependencyObject obj) + { + return (string)obj.GetValue(ColumnDefinitionProperty); + } + + public static void SetColumnDefinition(DependencyObject obj, string value) + { + obj.SetValue(ColumnDefinitionProperty, value); + } + public static readonly DependencyProperty ColumnDefinitionProperty = + DependencyProperty.RegisterAttached("ColumnDefinition", typeof(string), typeof(GridAreaAttach), new PropertyMetadata(null, OnColumnDefinitionChanged)); + + private static void OnColumnDefinitionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + Grid grid = d as Grid; + string param = e.NewValue as string; + + InitializeColumnDefinition(grid, param); + + string template = GetTemplateArea(grid); + if (template != null) + { + InitializeTemplateArea(grid, template); + } + } + + private static void InitializeColumnDefinition(Grid grid, string param) + { + if (grid == null || param == null) + { + return; + } + + grid.ColumnDefinitions.Clear(); + + IEnumerable list = param.Split(',') + .Select(o => o.Trim()); + + foreach (string item in list) + { + GridLengthDefinition def = StringToGridLengthDefinition(item); + ColumnDefinition columnDefinition = new ColumnDefinition() { Width = def.GridLength }; + if (def.Max != null) { columnDefinition.MaxWidth = def.Max.Value; } + if (def.Min != null) { columnDefinition.MinWidth = def.Min.Value; } + grid.ColumnDefinitions.Add(columnDefinition); + } + } + + public static string GetRowDefinition(DependencyObject obj) + { + return (string)obj.GetValue(RowDefinitionProperty); + } + public static void SetRowDefinition(DependencyObject obj, string value) + { + obj.SetValue(RowDefinitionProperty, value); + } + public static readonly DependencyProperty RowDefinitionProperty = + DependencyProperty.RegisterAttached("RowDefinition", typeof(string), typeof(GridAreaAttach), new PropertyMetadata(null, OnRowDefinitionChanged)); + + private static void OnRowDefinitionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + Grid grid = d as Grid; + string param = e.NewValue as string; + + InitializeRowDefinition(grid, param); + + string template = GetTemplateArea(grid); + if (template != null) + { + InitializeTemplateArea(grid, template); + } + } + + private static void InitializeRowDefinition(Grid grid, string param) + { + if (grid == null || param == null) + { + return; + } + + grid.RowDefinitions.Clear(); + + IEnumerable list = param.Split(',') + .Select(o => o.Trim()); + + foreach (string item in list) + { + GridLengthDefinition def = StringToGridLengthDefinition(item); + RowDefinition rowDefinition = new RowDefinition() { Height = def.GridLength }; + if (def.Max != null) { rowDefinition.MaxHeight = def.Max.Value; } + if (def.Min != null) { rowDefinition.MinHeight = def.Min.Value; } + grid.RowDefinitions.Add(rowDefinition); + } + } + + public static IList GetAreaDefinitions(DependencyObject obj) + { + return (IList)obj.GetValue(AreaDefinitionsProperty); + } + private static void SetAreaDefinitions(DependencyObject obj, IList value) + { + obj.SetValue(AreaDefinitionsProperty, value); + } + + public static readonly DependencyProperty AreaDefinitionsProperty = + DependencyProperty.RegisterAttached("AreaDefinitions", typeof(IList), typeof(GridAreaAttach), new PropertyMetadata(null)); + + public static string GetTemplateArea(DependencyObject obj) + { + return (string)obj.GetValue(TemplateAreaProperty); + } + public static void SetTemplateArea(DependencyObject obj, string value) + { + obj.SetValue(TemplateAreaProperty, value); + } + + public static readonly DependencyProperty TemplateAreaProperty = + DependencyProperty.RegisterAttached("TemplateArea", typeof(string), typeof(GridAreaAttach), new PropertyMetadata(null, OnTemplateAreaChanged)); + + private static void OnTemplateAreaChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + Grid grid = d as Grid; + string param = e.NewValue as string; + + if (d == null) + { + return; + } + + grid.RowDefinitions.Clear(); + grid.ColumnDefinitions.Clear(); + + InitializeRowDefinition(grid, GetRowDefinition(grid)); + InitializeColumnDefinition(grid, GetColumnDefinition(grid)); + + if (param != null) + { + InitializeTemplateArea(grid, param); + } + } + + private static void InitializeTemplateArea(Grid grid, string param) + { + IEnumerable columns = param.Split(new[] { '\n', '/' }) + .Select(o => o.Trim()) + .Where(o => !string.IsNullOrWhiteSpace(o)) + .Select(o => o.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)); + + int num = columns.FirstOrDefault().Count(); + bool isValidRowColumn = columns.All(o => o.Count() == num); + if (!isValidRowColumn) + { + throw new ArgumentException("Invalid Row/Column definition."); + } + + int rowShortage = columns.Count() - grid.RowDefinitions.Count; + for (int i = 0; i < rowShortage; i++) + { + grid.RowDefinitions.Add(new RowDefinition()); + } + + int columnShortage = num - grid.ColumnDefinitions.Count; + for (int i = 0; i < columnShortage; i++) + { + grid.ColumnDefinitions.Add(new ColumnDefinition()); + } + + IList areaList = ParseAreaDefinition(columns); + SetAreaDefinitions(grid, areaList); + + foreach (FrameworkElement child in grid.Children) + { + UpdateItemPosition(child); + } + } + + private static IList ParseAreaDefinition(IEnumerable columns) + { + List result = new List(); + + var flatten = columns.SelectMany( + (item, index) => item.Select((o, xIndex) => new { row = index, column = xIndex, name = o }) + ); + + var groups = flatten.GroupBy(o => o.name); + foreach (var group in groups) + { + int left = group.Min(o => o.column); + int top = group.Min(o => o.row); + int right = group.Max(o => o.column); + int bottom = group.Max(o => o.row); + + bool isValid = true; + for (int y = top; y <= bottom; y++) + for (int x = left; x <= right; x++) + { + isValid = isValid && group.Any(o => o.column == x && o.row == y); + } + + if (!isValid) + { + throw new ArgumentException($"\"{group.Key}\" is invalid area definition."); + } + + result.Add(new NamedAreaDefinition(group.Key, top, left, bottom - top + 1, right - left + 1)); + } + + return result; + } + + private static GridLengthDefinition StringToGridLengthDefinition(string source) + { + System.Text.RegularExpressions.Regex r = new System.Text.RegularExpressions.Regex(@"(^[^\(\)]+)(?:\((.*)-(.*)\))?"); + System.Text.RegularExpressions.Match m = r.Match(source); + + string length = m.Groups[1].Value; + string min = m.Groups[2].Value; + string max = m.Groups[3].Value; + + double temp; + GridLengthDefinition result = new GridLengthDefinition() + { + GridLength = StringToGridLength(length), + Min = double.TryParse(min, out temp) ? temp : (double?)null, + Max = double.TryParse(max, out temp) ? temp : (double?)null + }; + + return result; + } + + private static GridLength StringToGridLength(string source) + { + TypeConverter glc = TypeDescriptor.GetConverter(typeof(GridLength)); + return (GridLength)glc.ConvertFromString(source); + } + + public static string GetAreaName(DependencyObject obj) + { + return (string)obj.GetValue(AreaNameProperty); + } + public static void SetAreaName(DependencyObject obj, string value) + { + obj.SetValue(AreaNameProperty, value); + } + + public static readonly DependencyProperty AreaNameProperty = + DependencyProperty.RegisterAttached("AreaName", typeof(string), typeof(GridAreaAttach), new PropertyMetadata(null, OnAreaNameChanged)); + + private static void OnAreaNameChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + FrameworkElement ctrl = d as FrameworkElement; + + if (ctrl == null) + { + return; + } + + UpdateItemPosition(ctrl); + + Grid grid = ctrl.Parent as Grid; + bool isAutoFill = GetAutoFillChildren(grid); + if (isAutoFill) + { + AutoFill(grid); + } + } + + public static string GetArea(DependencyObject obj) + { + return (string)obj.GetValue(AreaProperty); + } + public static void SetArea(DependencyObject obj, string value) + { + obj.SetValue(AreaProperty, value); + } + + public static readonly DependencyProperty AreaProperty = + DependencyProperty.RegisterAttached("Area", typeof(string), typeof(GridAreaAttach), new PropertyMetadata(null, OnAreaChanged)); + + private static void OnAreaChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + FrameworkElement ctrl = d as FrameworkElement; + + if (d == null) + { + return; + } + + UpdateItemPosition(ctrl); + + Grid grid = ctrl.Parent as Grid; + if (grid == null) + { + return; + } + + bool isAutoFill = GetAutoFillChildren(grid); + if (isAutoFill) + { + AutoFill(grid); + } + } + + private static void UpdateItemPosition(FrameworkElement element) + { + AreaDefinition area = GetAreaNameRegion(element) ?? GetAreaRegion(element); + if (area != null) + { + Grid.SetRow(element, area.Row); + Grid.SetColumn(element, area.Column); + Grid.SetRowSpan(element, area.RowSpan); + Grid.SetColumnSpan(element, area.ColumnSpan); + } + } + + private static AreaDefinition GetAreaNameRegion(FrameworkElement element) + { + string name = GetAreaName(element); + Grid grid = element.Parent as Grid; + if (grid == null || name == null) { return null; } + IList areaList = GetAreaDefinitions(grid); + if (areaList == null) { return null; } + + NamedAreaDefinition area = areaList.FirstOrDefault(o => o.Name == name); + if (area == null) { return null; } + + return new AreaDefinition(area.Row, area.Column, area.RowSpan, area.ColumnSpan); + } + + private static AreaDefinition GetAreaRegion(FrameworkElement element) + { + string param = GetArea(element); + if (param == null) { return null; } + + List list = param.Split(',') + .Select(o => o.Trim()) + .Select(o => int.Parse(o)) + .ToList(); + + if (list.Count() != 4) { return null; } + + return new AreaDefinition(list[0], list[1], list[2], list[3]); + } + } +} diff --git a/Source/Controls/H.Controls.Panel/Panel/PanelBase.cs b/Source/Controls/H.Controls.Panel/Panel/PanelBase.cs new file mode 100644 index 00000000..bed4811e --- /dev/null +++ b/Source/Controls/H.Controls.Panel/Panel/PanelBase.cs @@ -0,0 +1,138 @@ +// Copyright © 2022 By HeBianGu(QQ:908293466) https://github.com/HeBianGu/WPF-ControlBase + + +using H.Providers.Mvvm; +using System.Collections.Generic; +using System.Linq; +using System.Windows; +using System.Windows.Input; + +namespace H.Controls.Panel +{ + public abstract class PanelBase : System.Windows.Controls.Panel + { + public PanelBase() + { + // Do :下一项 + { + CommandBinding binding = new CommandBinding(Commands.Next); + + this.CommandBindings.Add(binding); + + binding.Executed += (l, k) => this.Next(); + } + + // Do :上一项 + { + CommandBinding binding = new CommandBinding(Commands.Prev); + + this.CommandBindings.Add(binding); + + binding.Executed += (l, k) => this.Previous(); + } + + this.SizeChanged += (l, k) => + { + this.InvalidateArrange(); + + }; + + } + + protected virtual void Next() + { + this.StartIndex = this.StartIndex + 1; + + this.StartIndex = this.StartIndex > this.Children.Count - 1 ? 0 : this.StartIndex; + + //System.Diagnostics.Debug.WriteLine(this.StartIndex); + + // Do :刷新 + this.InvalidateArrange(); + } + + protected virtual void Previous() + { + this.StartIndex = this.StartIndex - 1; + + this.StartIndex = this.StartIndex < 0 ? this.Children.Count - 1 : this.StartIndex; + + // Do :刷新 + this.InvalidateArrange(); + } + + public int StartIndex + { + get { return (int)GetValue(StartIndexProperty); } + set { SetValue(StartIndexProperty, value); } + } + + // Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc... + public static readonly DependencyProperty StartIndexProperty = + DependencyProperty.Register("StartIndex", typeof(int), typeof(PanelBase), new PropertyMetadata(0, (d, e) => + { + PanelBase control = d as PanelBase; + + if (control == null) return; + + //int config = e.NewValue as int; + + // Do :刷新 + control.InvalidateArrange(); + + })); + + + public int DisplayCount + { + get { return (int)GetValue(DisplayCountProperty); } + set { SetValue(DisplayCountProperty, value); } + } + + // Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc... + public static readonly DependencyProperty DisplayCountProperty = + DependencyProperty.Register("DisplayCount", typeof(int), typeof(PanelBase), new PropertyMetadata(-1, (d, e) => + { + PanelBase control = d as PanelBase; + + if (control == null) return; + + //int config = e.NewValue as int; + + control.InvalidateArrange(); + + })); + + + protected List GetChildren() + { + int count = this.DisplayCount < 0 || this.DisplayCount > this.Children.Count ? this.Children.Count : this.DisplayCount; + + // Do :重新排序 + UIElement[] orders = new UIElement[this.Children.Count]; + + for (int i = 0; i < this.Children.Count; i++) + { + int index = this.StartIndex + i; + + index = index > this.Children.Count - 1 ? index - this.Children.Count : index; + + orders[i] = this.Children[index]; + + orders[i].Arrange(new Rect(new Point(0, 0), orders[i].DesiredSize)); + + //orders[i].Arrange(new Rect(0, 0, 0, 0)); + + //orders[i].Opacity = 0.0; + } + + List result = orders?.ToList()?.Take(count)?.ToList(); + + + //result.ForEach(l => l.Opacity = 1); + + return result; + } + + } +} diff --git a/Source/Controls/H.Controls.Panel/Panel/RandomPanel.cs b/Source/Controls/H.Controls.Panel/Panel/RandomPanel.cs new file mode 100644 index 00000000..58fbf670 --- /dev/null +++ b/Source/Controls/H.Controls.Panel/Panel/RandomPanel.cs @@ -0,0 +1,114 @@ +// Copyright © 2022 By HeBianGu(QQ:908293466) https://github.com/HeBianGu/WPF-ControlBase + +using System; +using System.Timers; +using System.Windows; + +namespace H.Controls.Panel +{ + public class RandomPanel : PanelBase + { + private static Random _random = new Random(); + + protected override Size ArrangeOverride(Size finalSize) + { + System.Collections.Generic.List children = this.GetChildren(); + Point center = new Point(finalSize.Width / 2, finalSize.Height / 2); + for (int i = 0; i < children.Count; i++) + { + UIElement elment = children[i]; + elment.Measure(finalSize); + elment.RenderTransformOrigin = new Point(0.5, 0.5); + Point end = new Point(); + end.X = finalSize.Width * _random.NextDouble() - elment.DesiredSize.Width / 2; + end.Y = finalSize.Height * _random.NextDouble() - elment.DesiredSize.Height / 2; + elment.Arrange(new Rect(end, elment.DesiredSize)); + } + + return base.ArrangeOverride(finalSize); + } + + private Timer _timer = new Timer(); + + public RandomPanel() + { + _timer.Interval = this.Interval; + _timer.Elapsed += (l, k) => + { + Application.Current.Dispatcher.Invoke(() => + { + this.InvalidateArrange(); + }); + + }; + } + + private void Start() + { + _timer.Start(); + } + + private void Stop() + { + _timer.Stop(); + } + + public bool Forever + { + get { return (bool)GetValue(ForeverProperty); } + set { SetValue(ForeverProperty, value); } + } + + // Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc... + public static readonly DependencyProperty ForeverProperty = + DependencyProperty.Register("Forever", typeof(bool), typeof(RandomPanel), new FrameworkPropertyMetadata(default(bool), (d, e) => + { + RandomPanel control = d as RandomPanel; + + if (control == null) return; + + if (e.OldValue is bool o) + { + + } + + if (e.NewValue is bool n) + { + if (n) + { + control.Start(); + } + else + { + control.Stop(); + } + } + + })); + + public double Interval + { + get { return (double)GetValue(IntervalProperty); } + set { SetValue(IntervalProperty, value); } + } + + // Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc... + public static readonly DependencyProperty IntervalProperty = + DependencyProperty.Register("Interval", typeof(double), typeof(RandomPanel), new FrameworkPropertyMetadata(1000.0, (d, e) => + { + RandomPanel control = d as RandomPanel; + + if (control == null) return; + + if (e.OldValue is double o) + { + + } + + if (e.NewValue is double n) + { + control._timer.Interval = n; + } + })); + } +} diff --git a/Source/Controls/H.Controls.Panel/Themes/Generic.xaml b/Source/Controls/H.Controls.Panel/Themes/Generic.xaml new file mode 100644 index 00000000..48d2fa36 --- /dev/null +++ b/Source/Controls/H.Controls.Panel/Themes/Generic.xaml @@ -0,0 +1,11 @@ + + + + + + + +