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 @@
+
+
+
+
+
+
+
+