diff --git a/BPASmart.RecipeManagement/App.xaml b/BPASmart.RecipeManagement/App.xaml
index 196cf0ec..3d9f99d4 100644
--- a/BPASmart.RecipeManagement/App.xaml
+++ b/BPASmart.RecipeManagement/App.xaml
@@ -87,7 +87,7 @@
-
+
diff --git a/BPASmartClient.MilkWithTea/App.xaml.cs b/BPASmartClient.MilkWithTea/App.xaml.cs
index 266bb6c8..aba6737f 100644
--- a/BPASmartClient.MilkWithTea/App.xaml.cs
+++ b/BPASmartClient.MilkWithTea/App.xaml.cs
@@ -1,11 +1,22 @@
-using BPASmartClient.Helper;
-using System;
-using System.Collections.Generic;
-using System.Configuration;
-using System.Data;
-using System.Linq;
-using System.Threading.Tasks;
-using System.Windows;
+global using BPA.Message;
+global using BPA.Message.Enum;
+global using BPASmartClient.Device;
+global using BPASmartClient.EventBus;
+global using BPASmartClient.Helper;
+global using BPASmartClient.MilkWithTea.Model;
+global using BPASmartClient.Model;
+global using BPASmartClient.MorkMOC;
+global using System;
+global using System.Collections.Generic;
+global using System.Collections.ObjectModel;
+global using System.Linq;
+global using System.Text;
+global using System.Threading.Tasks;
+global using System.Windows;
+global using System.Configuration;
+global using System.Data;
+
+
namespace BPASmartClient.MilkWithTea
{
diff --git a/BPASmartClient.MilkWithTea/BPASmartClient.MilkWithTea.csproj b/BPASmartClient.MilkWithTea/BPASmartClient.MilkWithTea.csproj
index 43e4c5d7..7b248a82 100644
--- a/BPASmartClient.MilkWithTea/BPASmartClient.MilkWithTea.csproj
+++ b/BPASmartClient.MilkWithTea/BPASmartClient.MilkWithTea.csproj
@@ -23,14 +23,19 @@
+
+
+
+
+
+
-
diff --git a/BPASmartClient.MilkWithTea/Control/ContextMenuToggleButton.cs b/BPASmartClient.MilkWithTea/Control/ContextMenuToggleButton.cs
new file mode 100644
index 00000000..9ffe859b
--- /dev/null
+++ b/BPASmartClient.MilkWithTea/Control/ContextMenuToggleButton.cs
@@ -0,0 +1,30 @@
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+
+
+namespace BPASmartClient.MilkWithTea.Control;
+
+///
+/// 带上下文菜单的切换按钮
+///
+public class ContextMenuToggleButton : ToggleButton
+{
+ public ContextMenu Menu { get; set; }
+
+ protected override void OnClick()
+ {
+ base.OnClick();
+ if (Menu != null)
+ {
+ if (IsChecked == true)
+ {
+ Menu.PlacementTarget = this;
+ Menu.IsOpen = true;
+ }
+ else
+ {
+ Menu.IsOpen = false;
+ }
+ }
+ }
+}
diff --git a/BPASmartClient.MilkWithTea/Control/ScrollViewer.cs b/BPASmartClient.MilkWithTea/Control/ScrollViewer.cs
new file mode 100644
index 00000000..f289b71a
--- /dev/null
+++ b/BPASmartClient.MilkWithTea/Control/ScrollViewer.cs
@@ -0,0 +1,225 @@
+using BPASmartClient.MilkWithTea.Data;
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+
+namespace BPASmartClient.MilkWithTea.Control;
+
+public class ScrollViewer : System.Windows.Controls.ScrollViewer
+{
+ private double _totalVerticalOffset;
+
+ private double _totalHorizontalOffset;
+
+ private bool _isRunning;
+
+ ///
+ /// 是否响应鼠标滚轮操作
+ ///
+ public static readonly DependencyProperty CanMouseWheelProperty = DependencyProperty.Register(
+ "CanMouseWheel", typeof(bool), typeof(ScrollViewer), new PropertyMetadata(ValueBoxes.TrueBox));
+
+ ///
+ /// 是否响应鼠标滚轮操作
+ ///
+ public bool CanMouseWheel
+ {
+ get => (bool) GetValue(CanMouseWheelProperty);
+ set => SetValue(CanMouseWheelProperty, ValueBoxes.BooleanBox(value));
+ }
+
+ protected override void OnMouseWheel(MouseWheelEventArgs e)
+ {
+ if (!CanMouseWheel) return;
+
+ if (!IsInertiaEnabled)
+ {
+ if (ScrollViewerAttach.GetOrientation(this) == Orientation.Vertical)
+ {
+ base.OnMouseWheel(e);
+ }
+ else
+ {
+ _totalHorizontalOffset = HorizontalOffset;
+ CurrentHorizontalOffset = HorizontalOffset;
+ _totalHorizontalOffset = Math.Min(Math.Max(0, _totalHorizontalOffset - e.Delta), ScrollableWidth);
+ CurrentHorizontalOffset = _totalHorizontalOffset;
+ }
+ return;
+ }
+ e.Handled = true;
+
+ if (ScrollViewerAttach.GetOrientation(this) == Orientation.Vertical)
+ {
+ if (!_isRunning)
+ {
+ _totalVerticalOffset = VerticalOffset;
+ CurrentVerticalOffset = VerticalOffset;
+ }
+ _totalVerticalOffset = Math.Min(Math.Max(0, _totalVerticalOffset - e.Delta), ScrollableHeight);
+ ScrollToVerticalOffsetWithAnimation(_totalVerticalOffset);
+ }
+ else
+ {
+ if (!_isRunning)
+ {
+ _totalHorizontalOffset = HorizontalOffset;
+ CurrentHorizontalOffset = HorizontalOffset;
+ }
+ _totalHorizontalOffset = Math.Min(Math.Max(0, _totalHorizontalOffset - e.Delta), ScrollableWidth);
+ ScrollToHorizontalOffsetWithAnimation(_totalHorizontalOffset);
+ }
+ }
+
+ internal void ScrollToTopInternal(double milliseconds = 500)
+ {
+ if (!_isRunning)
+ {
+ _totalVerticalOffset = VerticalOffset;
+ CurrentVerticalOffset = VerticalOffset;
+ }
+ ScrollToVerticalOffsetWithAnimation(0, milliseconds);
+ }
+
+ public void ScrollToVerticalOffsetWithAnimation(double offset, double milliseconds = 500)
+ {
+ var animation = CreateAnimation(offset, milliseconds);
+ animation.EasingFunction = new CubicEase
+ {
+ EasingMode = EasingMode.EaseOut
+ };
+ animation.FillBehavior = FillBehavior.Stop;
+ animation.Completed += (s, e1) =>
+ {
+ CurrentVerticalOffset = offset;
+ _isRunning = false;
+ };
+ _isRunning = true;
+
+ BeginAnimation(CurrentVerticalOffsetProperty, animation, HandoffBehavior.Compose);
+ }
+
+ public void ScrollToHorizontalOffsetWithAnimation(double offset, double milliseconds = 500)
+ {
+ var animation = CreateAnimation(offset, milliseconds);
+ animation.EasingFunction = new CubicEase
+ {
+ EasingMode = EasingMode.EaseOut
+ };
+ animation.FillBehavior = FillBehavior.Stop;
+ animation.Completed += (s, e1) =>
+ {
+ CurrentHorizontalOffset = offset;
+ _isRunning = false;
+ };
+ _isRunning = true;
+
+ BeginAnimation(CurrentHorizontalOffsetProperty, animation, HandoffBehavior.Compose);
+ }
+
+ protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters) =>
+ IsPenetrating ? null : base.HitTestCore(hitTestParameters);
+
+ ///
+ /// 是否支持惯性
+ ///
+ public static readonly DependencyProperty IsInertiaEnabledProperty = DependencyProperty.RegisterAttached(
+ "IsInertiaEnabled", typeof(bool), typeof(ScrollViewer), new PropertyMetadata(ValueBoxes.FalseBox));
+
+ public static void SetIsInertiaEnabled(DependencyObject element, bool value) => element.SetValue(IsInertiaEnabledProperty, ValueBoxes.BooleanBox(value));
+
+ public static bool GetIsInertiaEnabled(DependencyObject element) => (bool) element.GetValue(IsInertiaEnabledProperty);
+
+ ///
+ /// 是否支持惯性
+ ///
+ public bool IsInertiaEnabled
+ {
+ get => (bool) GetValue(IsInertiaEnabledProperty);
+ set => SetValue(IsInertiaEnabledProperty, ValueBoxes.BooleanBox(value));
+ }
+
+ ///
+ /// 控件是否可以穿透点击
+ ///
+ public static readonly DependencyProperty IsPenetratingProperty = DependencyProperty.RegisterAttached(
+ "IsPenetrating", typeof(bool), typeof(ScrollViewer), new PropertyMetadata(ValueBoxes.FalseBox));
+
+ ///
+ /// 控件是否可以穿透点击
+ ///
+ public bool IsPenetrating
+ {
+ get => (bool) GetValue(IsPenetratingProperty);
+ set => SetValue(IsPenetratingProperty, ValueBoxes.BooleanBox(value));
+ }
+
+ public static void SetIsPenetrating(DependencyObject element, bool value) => element.SetValue(IsPenetratingProperty, ValueBoxes.BooleanBox(value));
+
+ public static bool GetIsPenetrating(DependencyObject element) => (bool) element.GetValue(IsPenetratingProperty);
+
+ ///
+ /// 当前垂直滚动偏移
+ ///
+ internal static readonly DependencyProperty CurrentVerticalOffsetProperty = DependencyProperty.Register(
+ "CurrentVerticalOffset", typeof(double), typeof(ScrollViewer), new PropertyMetadata(ValueBoxes.Double0Box, OnCurrentVerticalOffsetChanged));
+
+ private static void OnCurrentVerticalOffsetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ if (d is ScrollViewer ctl && e.NewValue is double v)
+ {
+ ctl.ScrollToVerticalOffset(v);
+ }
+ }
+
+ ///
+ /// 当前垂直滚动偏移
+ ///
+ internal double CurrentVerticalOffset
+ {
+ // ReSharper disable once UnusedMember.Local
+ get => (double) GetValue(CurrentVerticalOffsetProperty);
+ set => SetValue(CurrentVerticalOffsetProperty, value);
+ }
+
+ ///
+ /// 当前水平滚动偏移
+ ///
+ internal static readonly DependencyProperty CurrentHorizontalOffsetProperty = DependencyProperty.Register(
+ "CurrentHorizontalOffset", typeof(double), typeof(ScrollViewer), new PropertyMetadata(ValueBoxes.Double0Box, OnCurrentHorizontalOffsetChanged));
+
+ private static void OnCurrentHorizontalOffsetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ if (d is ScrollViewer ctl && e.NewValue is double v)
+ {
+ ctl.ScrollToHorizontalOffset(v);
+ }
+ }
+
+ ///
+ /// 当前水平滚动偏移
+ ///
+ internal double CurrentHorizontalOffset
+ {
+ get => (double) GetValue(CurrentHorizontalOffsetProperty);
+ set => SetValue(CurrentHorizontalOffsetProperty, value);
+ }
+
+
+ ///
+ /// 创建一个Double动画
+ ///
+ ///
+ ///
+ ///
+ public DoubleAnimation CreateAnimation(double toValue, double milliseconds = 200)
+ {
+ return new(toValue, new Duration(TimeSpan.FromMilliseconds(milliseconds)))
+ {
+ EasingFunction = new PowerEase { EasingMode = EasingMode.EaseInOut }
+ };
+ }
+}
diff --git a/BPASmartClient.MilkWithTea/Control/ScrollViewerAttach.cs b/BPASmartClient.MilkWithTea/Control/ScrollViewerAttach.cs
new file mode 100644
index 00000000..f943614b
--- /dev/null
+++ b/BPASmartClient.MilkWithTea/Control/ScrollViewerAttach.cs
@@ -0,0 +1,111 @@
+using BPASmartClient.MilkWithTea.Data;
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Media;
+
+namespace BPASmartClient.MilkWithTea.Control;
+
+public class ScrollViewerAttach
+{
+ public static readonly DependencyProperty AutoHideProperty = DependencyProperty.RegisterAttached(
+ "AutoHide", typeof(bool), typeof(ScrollViewerAttach), new FrameworkPropertyMetadata(ValueBoxes.TrueBox, FrameworkPropertyMetadataOptions.Inherits));
+
+ public static void SetAutoHide(DependencyObject element, bool value)
+ => element.SetValue(AutoHideProperty, ValueBoxes.BooleanBox(value));
+
+ public static bool GetAutoHide(DependencyObject element)
+ => (bool) element.GetValue(AutoHideProperty);
+
+ public static readonly DependencyProperty OrientationProperty = DependencyProperty.RegisterAttached(
+ "Orientation", typeof(Orientation), typeof(ScrollViewerAttach), new FrameworkPropertyMetadata(ValueBoxes.VerticalBox, FrameworkPropertyMetadataOptions.Inherits, OnOrientationChanged));
+
+ private static void OnOrientationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ if (d is ScrollViewer)
+ {
+ return;
+ }
+
+ if (d is System.Windows.Controls.ScrollViewer scrollViewer)
+ {
+ if ((Orientation) e.NewValue == Orientation.Horizontal)
+ {
+ scrollViewer.PreviewMouseWheel += ScrollViewerPreviewMouseWheel;
+ }
+ else
+ {
+ scrollViewer.PreviewMouseWheel -= ScrollViewerPreviewMouseWheel;
+ }
+ }
+
+ void ScrollViewerPreviewMouseWheel(object sender, MouseWheelEventArgs args)
+ {
+ var scrollViewerNative = (System.Windows.Controls.ScrollViewer) sender;
+ scrollViewerNative.ScrollToHorizontalOffset(Math.Min(Math.Max(0, scrollViewerNative.HorizontalOffset - args.Delta), scrollViewerNative.ScrollableWidth));
+
+ args.Handled = true;
+ }
+ }
+
+ public static void SetOrientation(DependencyObject element, Orientation value)
+ => element.SetValue(OrientationProperty, ValueBoxes.OrientationBox(value));
+
+ public static Orientation GetOrientation(DependencyObject element)
+ => (Orientation) element.GetValue(OrientationProperty);
+
+ public static readonly DependencyProperty IsDisabledProperty = DependencyProperty.RegisterAttached(
+ "IsDisabled", typeof(bool), typeof(ScrollViewerAttach), new PropertyMetadata(ValueBoxes.FalseBox, OnIsDisabledChanged));
+
+ private static void OnIsDisabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ if (d is UIElement element)
+ {
+ if ((bool) e.NewValue)
+ {
+ element.PreviewMouseWheel += ScrollViewerPreviewMouseWheel;
+ }
+ else
+ {
+ element.PreviewMouseWheel -= ScrollViewerPreviewMouseWheel;
+ }
+ }
+
+ void ScrollViewerPreviewMouseWheel(object sender, MouseWheelEventArgs args)
+ {
+ if (args.Handled)
+ {
+ return;
+ }
+
+ args.Handled = true;
+
+ if (GetParent((UIElement) sender) is { } scrollViewer)
+ {
+ scrollViewer.RaiseEvent(new MouseWheelEventArgs(args.MouseDevice, args.Timestamp, args.Delta)
+ {
+ RoutedEvent = UIElement.MouseWheelEvent,
+ Source = sender
+ });
+ }
+ }
+
+ static T GetParent(DependencyObject d) where T : DependencyObject =>
+ d switch
+ {
+ null => default,
+ T t => t,
+ Window _ => null,
+ _ => GetParent(VisualTreeHelper.GetParent(d))
+ };
+ }
+
+ public static void SetIsDisabled(DependencyObject element, bool value)
+ => element.SetValue(IsDisabledProperty, ValueBoxes.BooleanBox(value));
+
+ public static bool GetIsDisabled(DependencyObject element)
+ => (bool) element.GetValue(IsDisabledProperty);
+
+
+}
diff --git a/BPASmartClient.MilkWithTea/Control/TabControl.cs b/BPASmartClient.MilkWithTea/Control/TabControl.cs
new file mode 100644
index 00000000..342f4127
--- /dev/null
+++ b/BPASmartClient.MilkWithTea/Control/TabControl.cs
@@ -0,0 +1,426 @@
+using BPASmartClient.MilkWithTea.Data;
+using System;
+using System.Collections;
+using System.Collections.Specialized;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using System.Windows.Input;
+
+namespace BPASmartClient.MilkWithTea.Control;
+
+[TemplatePart(Name = HeaderPanelKey, Type = typeof(TabPanel))]
+[TemplatePart(Name = OverflowScrollviewer, Type = typeof(ScrollViewer))]
+[TemplatePart(Name = ScrollButtonLeft, Type = typeof(ButtonBase))]
+[TemplatePart(Name = ScrollButtonRight, Type = typeof(ButtonBase))]
+[TemplatePart(Name = HeaderBorder, Type = typeof(Border))]
+public class TabControl : System.Windows.Controls.TabControl
+{
+ private const string OverflowButtonKey = "PART_OverflowButton";
+
+ private const string HeaderPanelKey = "PART_HeaderPanel";
+
+ private const string OverflowScrollviewer = "PART_OverflowScrollviewer";
+
+ private const string ScrollButtonLeft = "PART_ScrollButtonLeft";
+
+ private const string ScrollButtonRight = "PART_ScrollButtonRight";
+
+ private const string HeaderBorder = "PART_HeaderBorder";
+
+ private ContextMenuToggleButton _buttonOverflow;
+
+ internal TabPanel HeaderPanel { get; private set; }
+
+ private ScrollViewer _scrollViewerOverflow;
+
+ private ButtonBase _buttonScrollLeft;
+
+ private ButtonBase _buttonScrollRight;
+
+ private Border _headerBorder;
+
+ ///
+ /// 是否为内部操作
+ ///
+ internal bool IsInternalAction;
+
+ ///
+ /// 是否启用动画
+ ///
+ public static readonly DependencyProperty IsAnimationEnabledProperty = DependencyProperty.Register(
+ "IsAnimationEnabled", typeof(bool), typeof(TabControl), new PropertyMetadata(ValueBoxes.FalseBox));
+
+ ///
+ /// 是否启用动画
+ ///
+ public bool IsAnimationEnabled
+ {
+ get => (bool) GetValue(IsAnimationEnabledProperty);
+ set => SetValue(IsAnimationEnabledProperty, ValueBoxes.BooleanBox(value));
+ }
+
+ ///
+ /// 是否可以拖动
+ ///
+ public static readonly DependencyProperty IsDraggableProperty = DependencyProperty.Register(
+ "IsDraggable", typeof(bool), typeof(TabControl), new PropertyMetadata(ValueBoxes.FalseBox));
+
+ ///
+ /// 是否可以拖动
+ ///
+ public bool IsDraggable
+ {
+ get => (bool) GetValue(IsDraggableProperty);
+ set => SetValue(IsDraggableProperty, ValueBoxes.BooleanBox(value));
+ }
+
+ ///
+ /// 是否显示关闭按钮
+ ///
+ public static readonly DependencyProperty ShowCloseButtonProperty = DependencyProperty.RegisterAttached(
+ "ShowCloseButton", typeof(bool), typeof(TabControl), new FrameworkPropertyMetadata(ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.Inherits));
+
+ public static void SetShowCloseButton(DependencyObject element, bool value)
+ => element.SetValue(ShowCloseButtonProperty, ValueBoxes.BooleanBox(value));
+
+ public static bool GetShowCloseButton(DependencyObject element)
+ => (bool) element.GetValue(ShowCloseButtonProperty);
+
+ ///
+ /// 是否显示关闭按钮
+ ///
+ public bool ShowCloseButton
+ {
+ get => (bool) GetValue(ShowCloseButtonProperty);
+ set => SetValue(ShowCloseButtonProperty, ValueBoxes.BooleanBox(value));
+ }
+
+ ///
+ /// 是否显示上下文菜单
+ ///
+ public static readonly DependencyProperty ShowContextMenuProperty = DependencyProperty.RegisterAttached(
+ "ShowContextMenu", typeof(bool), typeof(TabControl), new FrameworkPropertyMetadata(ValueBoxes.TrueBox, FrameworkPropertyMetadataOptions.Inherits));
+
+ public static void SetShowContextMenu(DependencyObject element, bool value)
+ => element.SetValue(ShowContextMenuProperty, ValueBoxes.BooleanBox(value));
+
+ public static bool GetShowContextMenu(DependencyObject element)
+ => (bool) element.GetValue(ShowContextMenuProperty);
+
+ ///
+ /// 是否显示上下文菜单
+ ///
+ public bool ShowContextMenu
+ {
+ get => (bool) GetValue(ShowContextMenuProperty);
+ set => SetValue(ShowContextMenuProperty, ValueBoxes.BooleanBox(value));
+ }
+
+ ///
+ /// 是否将标签填充
+ ///
+ public static readonly DependencyProperty IsTabFillEnabledProperty = DependencyProperty.Register(
+ "IsTabFillEnabled", typeof(bool), typeof(TabControl), new PropertyMetadata(ValueBoxes.FalseBox));
+
+ ///
+ /// 是否将标签填充
+ ///
+ public bool IsTabFillEnabled
+ {
+ get => (bool) GetValue(IsTabFillEnabledProperty);
+ set => SetValue(IsTabFillEnabledProperty, ValueBoxes.BooleanBox(value));
+ }
+
+ ///
+ /// 标签宽度
+ ///
+ public static readonly DependencyProperty TabItemWidthProperty = DependencyProperty.Register(
+ "TabItemWidth", typeof(double), typeof(TabControl), new PropertyMetadata(200.0));
+
+ ///
+ /// 标签宽度
+ ///
+ public double TabItemWidth
+ {
+ get => (double) GetValue(TabItemWidthProperty);
+ set => SetValue(TabItemWidthProperty, value);
+ }
+
+ ///
+ /// 标签高度
+ ///
+ public static readonly DependencyProperty TabItemHeightProperty = DependencyProperty.Register(
+ "TabItemHeight", typeof(double), typeof(TabControl), new PropertyMetadata(30.0));
+
+ ///
+ /// 标签高度
+ ///
+ public double TabItemHeight
+ {
+ get => (double) GetValue(TabItemHeightProperty);
+ set => SetValue(TabItemHeightProperty, value);
+ }
+
+ ///
+ /// 是否可以滚动
+ ///
+ public static readonly DependencyProperty IsScrollableProperty = DependencyProperty.Register(
+ "IsScrollable", typeof(bool), typeof(TabControl), new PropertyMetadata(ValueBoxes.FalseBox));
+
+ ///
+ /// 是否可以滚动
+ ///
+ public bool IsScrollable
+ {
+ get => (bool) GetValue(IsScrollableProperty);
+ set => SetValue(IsScrollableProperty, ValueBoxes.BooleanBox(value));
+ }
+
+ ///
+ /// 是否显示溢出按钮
+ ///
+ public static readonly DependencyProperty ShowOverflowButtonProperty = DependencyProperty.Register(
+ "ShowOverflowButton", typeof(bool), typeof(TabControl), new PropertyMetadata(ValueBoxes.TrueBox));
+
+ ///
+ /// 是否显示溢出按钮
+ ///
+ public bool ShowOverflowButton
+ {
+ get => (bool) GetValue(ShowOverflowButtonProperty);
+ set => SetValue(ShowOverflowButtonProperty, ValueBoxes.BooleanBox(value));
+ }
+
+ ///
+ /// 是否显示滚动按钮
+ ///
+ public static readonly DependencyProperty ShowScrollButtonProperty = DependencyProperty.Register(
+ "ShowScrollButton", typeof(bool), typeof(TabControl), new PropertyMetadata(ValueBoxes.FalseBox));
+
+ ///
+ /// 是否显示滚动按钮
+ ///
+ public bool ShowScrollButton
+ {
+ get => (bool) GetValue(ShowScrollButtonProperty);
+ set => SetValue(ShowScrollButtonProperty, ValueBoxes.BooleanBox(value));
+ }
+
+ ///
+ /// 可见的标签数量
+ ///
+ private int _itemShowCount;
+
+ protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
+ {
+ base.OnItemsChanged(e);
+
+ if (HeaderPanel == null)
+ {
+ IsInternalAction = false;
+ return;
+ }
+
+ UpdateOverflowButton();
+
+ if (IsInternalAction)
+ {
+ IsInternalAction = false;
+ return;
+ }
+
+ if (e.Action == NotifyCollectionChangedAction.Add)
+ {
+ for (var i = 0; i < Items.Count; i++)
+ {
+ if (ItemContainerGenerator.ContainerFromIndex(i) is not TabItem item) return;
+ item.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
+ item.TabPanel = HeaderPanel;
+ }
+ }
+
+ _headerBorder?.InvalidateMeasure();
+ IsInternalAction = false;
+ }
+
+ public override void OnApplyTemplate()
+ {
+ if (_buttonOverflow != null)
+ {
+ if (_buttonOverflow.Menu != null)
+ {
+ _buttonOverflow.Menu.Closed -= Menu_Closed;
+ _buttonOverflow.Menu = null;
+ }
+
+ _buttonOverflow.Click -= ButtonOverflow_Click;
+ }
+
+ if (_buttonScrollLeft != null) _buttonScrollLeft.Click -= ButtonScrollLeft_Click;
+ if (_buttonScrollRight != null) _buttonScrollRight.Click -= ButtonScrollRight_Click;
+
+ base.OnApplyTemplate();
+ HeaderPanel = GetTemplateChild(HeaderPanelKey) as TabPanel;
+
+ if (IsTabFillEnabled) return;
+
+ _buttonOverflow = GetTemplateChild(OverflowButtonKey) as ContextMenuToggleButton;
+ _scrollViewerOverflow = GetTemplateChild(OverflowScrollviewer) as ScrollViewer;
+ _buttonScrollLeft = GetTemplateChild(ScrollButtonLeft) as ButtonBase;
+ _buttonScrollRight = GetTemplateChild(ScrollButtonRight) as ButtonBase;
+ _headerBorder = GetTemplateChild(HeaderBorder) as Border;
+
+ if (_buttonScrollLeft != null) _buttonScrollLeft.Click += ButtonScrollLeft_Click;
+ if (_buttonScrollRight != null) _buttonScrollRight.Click += ButtonScrollRight_Click;
+
+ if (_buttonOverflow != null)
+ {
+ var menu = new ContextMenu
+ {
+ Placement = PlacementMode.Bottom,
+ PlacementTarget = _buttonOverflow
+ };
+ menu.Closed += Menu_Closed;
+ _buttonOverflow.Menu = menu;
+ _buttonOverflow.Click += ButtonOverflow_Click;
+ }
+ }
+
+ protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
+ {
+ base.OnRenderSizeChanged(sizeInfo);
+ UpdateOverflowButton();
+ }
+
+ private void UpdateOverflowButton()
+ {
+ if (!IsTabFillEnabled)
+ {
+ _itemShowCount = (int) (ActualWidth / TabItemWidth);
+ //_buttonOverflow?.Show(ShowOverflowButton && Items.Count > 0 && Items.Count >= _itemShowCount);
+ }
+ }
+
+ private void Menu_Closed(object sender, RoutedEventArgs e) => _buttonOverflow.IsChecked = false;
+
+ private void ButtonScrollRight_Click(object sender, RoutedEventArgs e) =>
+ _scrollViewerOverflow.ScrollToHorizontalOffsetWithAnimation(Math.Min(
+ _scrollViewerOverflow.CurrentHorizontalOffset + TabItemWidth, _scrollViewerOverflow.ScrollableWidth));
+
+ private void ButtonScrollLeft_Click(object sender, RoutedEventArgs e) =>
+ _scrollViewerOverflow.ScrollToHorizontalOffsetWithAnimation(Math.Max(
+ _scrollViewerOverflow.CurrentHorizontalOffset - TabItemWidth, 0));
+
+ private void ButtonOverflow_Click(object sender, RoutedEventArgs e)
+ {
+ if (_buttonOverflow.IsChecked == true)
+ {
+ _buttonOverflow.Menu.Items.Clear();
+ for (var i = 0; i < Items.Count; i++)
+ {
+ if (ItemContainerGenerator.ContainerFromIndex(i) is not TabItem item) continue;
+
+ var menuItem = new MenuItem
+ {
+ HeaderStringFormat = ItemStringFormat,
+ HeaderTemplate = ItemTemplate,
+ HeaderTemplateSelector = ItemTemplateSelector,
+ Header = item.Header,
+ Width = TabItemWidth,
+ IsChecked = item.IsSelected,
+ IsCheckable = true,
+ IsEnabled = item.IsEnabled
+ };
+
+ menuItem.Click += delegate
+ {
+ _buttonOverflow.IsChecked = false;
+
+ var list = GetActualList();
+ if (list == null) return;
+
+ var actualItem = ItemContainerGenerator.ItemFromContainer(item);
+ if (actualItem == null) return;
+
+ var index = list.IndexOf(actualItem);
+ if (index >= _itemShowCount)
+ {
+ list.Remove(actualItem);
+ list.Insert(0, actualItem);
+ HeaderPanel.SetValue(TabPanel.FluidMoveDurationPropertyKey,
+ IsAnimationEnabled
+ ? new Duration(TimeSpan.FromMilliseconds(200))
+ : new Duration(TimeSpan.FromMilliseconds(0)));
+ HeaderPanel.ForceUpdate = true;
+ HeaderPanel.Measure(new Size(HeaderPanel.DesiredSize.Width, ActualHeight));
+ HeaderPanel.ForceUpdate = false;
+ SetCurrentValue(SelectedIndexProperty, ValueBoxes.Int0Box);
+ }
+
+ item.IsSelected = true;
+ };
+ _buttonOverflow.Menu.Items.Add(menuItem);
+ }
+ }
+ }
+
+ internal double GetHorizontalOffset() => _scrollViewerOverflow?.CurrentHorizontalOffset ?? 0;
+
+ internal void UpdateScroll() => _scrollViewerOverflow?.RaiseEvent(new MouseWheelEventArgs(Mouse.PrimaryDevice, Environment.TickCount, 0)
+ {
+ RoutedEvent = MouseWheelEvent
+ });
+
+ internal void CloseAllItems() => CloseOtherItems(null);
+
+ internal void CloseOtherItems(TabItem currentItem)
+ {
+ var actualItem = currentItem != null ? ItemContainerGenerator.ItemFromContainer(currentItem) : null;
+
+ var list = GetActualList();
+ if (list == null) return;
+
+ IsInternalAction = true;
+
+ for (var i = 0; i < Items.Count; i++)
+ {
+ var item = list[i];
+ if (!Equals(item, actualItem) && item != null)
+ {
+ var argsClosing = new CancelRoutedEventArgs(TabItem.ClosingEvent, item);
+
+ if (ItemContainerGenerator.ContainerFromItem(item) is not TabItem tabItem) continue;
+
+ tabItem.RaiseEvent(argsClosing);
+ if (argsClosing.Cancel) return;
+
+ tabItem.RaiseEvent(new RoutedEventArgs(TabItem.ClosedEvent, item));
+ list.Remove(item);
+
+ i--;
+ }
+ }
+
+ SetCurrentValue(SelectedIndexProperty, Items.Count == 0 ? -1 : 0);
+ }
+
+ internal IList GetActualList()
+ {
+ IList list;
+ if (ItemsSource != null)
+ {
+ list = ItemsSource as IList;
+ }
+ else
+ {
+ list = Items;
+ }
+
+ return list;
+ }
+
+ protected override bool IsItemItsOwnContainerOverride(object item) => item is TabItem;
+
+ protected override DependencyObject GetContainerForItemOverride() => new TabItem();
+}
diff --git a/BPASmartClient.MilkWithTea/Control/TabItem.cs b/BPASmartClient.MilkWithTea/Control/TabItem.cs
new file mode 100644
index 00000000..d911285e
--- /dev/null
+++ b/BPASmartClient.MilkWithTea/Control/TabItem.cs
@@ -0,0 +1,509 @@
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using System.Windows.Data;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using BPASmartClient.MilkWithTea.Control;
+using BPASmartClient.MilkWithTea.Data;
+using TabControl = BPASmartClient.MilkWithTea.Control.TabControl;
+
+namespace BPASmartClient.MilkWithTea.Control;
+
+public class TabItem : System.Windows.Controls.TabItem
+{
+ ///
+ /// 动画速度
+ ///
+ private const int AnimationSpeed = 150;
+
+ ///
+ /// 选项卡是否处于拖动状态
+ ///
+ private static bool ItemIsDragging;
+
+ ///
+ /// 选项卡是否等待被拖动
+ ///
+ private bool _isWaiting;
+
+ ///
+ /// 拖动中的选项卡坐标
+ ///
+ private Point _dragPoint;
+
+ ///
+ /// 鼠标按下时选项卡位置
+ ///
+ private int _mouseDownIndex;
+
+ ///
+ /// 鼠标按下时选项卡横向偏移
+ ///
+ private double _mouseDownOffsetX;
+
+ ///
+ /// 鼠标按下时的坐标
+ ///
+ private Point _mouseDownPoint;
+
+ ///
+ /// 右侧可移动的最大值
+ ///
+ private double _maxMoveRight;
+
+ ///
+ /// 左侧可移动的最大值
+ ///
+ private double _maxMoveLeft;
+
+ ///
+ /// 选项卡宽度
+ ///
+ public double ItemWidth { get; internal set; }
+
+ ///
+ /// 选项卡拖动等待距离(在鼠标移动了超过20个像素无关单位后,选项卡才开始被拖动)
+ ///
+ private const double WaitLength = 20;
+
+ ///
+ /// 选项卡是否处于拖动状态
+ ///
+ private bool _isDragging;
+
+ ///
+ /// 选项卡是否已经被拖动
+ ///
+ private bool _isDragged;
+
+ ///
+ /// 目标横向位移
+ ///
+ internal double TargetOffsetX { get; set; }
+
+ ///
+ /// 当前编号
+ ///
+ private int _currentIndex;
+
+ ///
+ /// 标签容器横向滚动距离
+ ///
+ private double _scrollHorizontalOffset;
+
+
+ private TabPanel _tabPanel;
+
+
+ ///
+ /// 标签容器
+ ///
+ internal TabPanel TabPanel
+ {
+ get
+ {
+ if (_tabPanel == null && TabControlParent != null)
+ {
+ _tabPanel = TabControlParent.HeaderPanel;
+ }
+
+ return _tabPanel;
+ }
+ set => _tabPanel = value;
+ }
+
+ private TabControl TabControlParent => ItemsControl.ItemsControlFromItemContainer(this) as TabControl;
+
+
+ ///
+ /// 当前编号
+ ///
+ internal int CurrentIndex
+ {
+ get => _currentIndex;
+ set
+ {
+ if (_currentIndex == value || value < 0) return;
+ var oldIndex = _currentIndex;
+ _currentIndex = value;
+ UpdateItemOffsetX(oldIndex);
+ }
+ }
+
+ ///
+ /// 是否显示关闭按钮
+ ///
+ public static readonly DependencyProperty ShowCloseButtonProperty =
+ TabControl.ShowCloseButtonProperty.AddOwner(typeof(TabItem));
+
+ ///
+ /// 是否显示关闭按钮
+ ///
+ public bool ShowCloseButton
+ {
+ get => (bool) GetValue(ShowCloseButtonProperty);
+ set => SetValue(ShowCloseButtonProperty, ValueBoxes.BooleanBox(value));
+ }
+
+ public static void SetShowCloseButton(DependencyObject element, bool value)
+ => element.SetValue(ShowCloseButtonProperty, ValueBoxes.BooleanBox(value));
+
+ public static bool GetShowCloseButton(DependencyObject element)
+ => (bool) element.GetValue(ShowCloseButtonProperty);
+
+ ///
+ /// 是否显示上下文菜单
+ ///
+ public static readonly DependencyProperty ShowContextMenuProperty =
+ TabControl.ShowContextMenuProperty.AddOwner(typeof(TabItem), new FrameworkPropertyMetadata(OnShowContextMenuChanged));
+
+ private static void OnShowContextMenuChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ var ctl = (TabItem) d;
+ if (ctl.Menu != null)
+ {
+ var show = (bool) e.NewValue;
+ ctl.Menu.IsEnabled = show;
+ //ctl.Menu.Show(show);
+ }
+ }
+
+ ///
+ /// 是否显示上下文菜单
+ ///
+ public bool ShowContextMenu
+ {
+ get => (bool) GetValue(ShowContextMenuProperty);
+ set => SetValue(ShowContextMenuProperty, ValueBoxes.BooleanBox(value));
+ }
+
+ public static void SetShowContextMenu(DependencyObject element, bool value)
+ => element.SetValue(ShowContextMenuProperty, ValueBoxes.BooleanBox(value));
+
+ public static bool GetShowContextMenu(DependencyObject element)
+ => (bool) element.GetValue(ShowContextMenuProperty);
+
+ public static readonly DependencyProperty MenuProperty = DependencyProperty.Register(
+ "Menu", typeof(ContextMenu), typeof(TabItem), new PropertyMetadata(default(ContextMenu), OnMenuChanged));
+
+ private static void OnMenuChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ var ctl = (TabItem) d;
+ //ctl.OnMenuChanged(e.NewValue as ContextMenu);
+ }
+
+ //private void OnMenuChanged(ContextMenu menu)
+ //{
+ // if (IsLoaded && menu != null)
+ // {
+ // var parent = TabControlParent;
+ // if (parent == null) return;
+
+ // var item = parent.ItemContainerGenerator.ItemFromContainer(this);
+
+ // menu.DataContext = item;
+ // menu.SetBinding(IsEnabledProperty, new Binding(ShowContextMenuProperty.Name)
+ // {
+ // Source = this
+ // });
+ // menu.SetBinding(VisibilityProperty, new Binding(ShowContextMenuProperty.Name)
+ // {
+ // Source = this,
+ // Converter = GetResourceInternal(ResourceToken.Boolean2VisibilityConverter)
+ // });
+ // }
+ //}
+
+
+
+ public ContextMenu Menu
+ {
+ get => (ContextMenu) GetValue(MenuProperty);
+ set => SetValue(MenuProperty, value);
+ }
+
+ ///
+ /// 更新选项卡横向偏移
+ ///
+ ///
+ private void UpdateItemOffsetX(int oldIndex)
+ {
+ if (!_isDragging || CurrentIndex >= TabPanel.ItemDic.Count)
+ {
+ return;
+ }
+
+ var moveItem = TabPanel.ItemDic[CurrentIndex];
+ moveItem.CurrentIndex -= CurrentIndex - oldIndex;
+ var offsetX = moveItem.TargetOffsetX;
+ var resultX = offsetX + (oldIndex - CurrentIndex) * ItemWidth;
+ TabPanel.ItemDic[CurrentIndex] = this;
+ TabPanel.ItemDic[moveItem.CurrentIndex] = moveItem;
+ moveItem.CreateAnimation(offsetX, resultX);
+ }
+
+ public TabItem()
+ {
+ //CommandBindings.Add(new CommandBinding(ControlCommands.Close, (s, e) => Close()));
+ //CommandBindings.Add(new CommandBinding(ControlCommands.CloseAll,
+ // (s, e) => { TabControlParent.CloseAllItems(); }));
+ //CommandBindings.Add(new CommandBinding(ControlCommands.CloseOther,
+ // (s, e) => { TabControlParent.CloseOtherItems(this); }));
+
+ //Loaded += (s, e) => OnMenuChanged(Menu);
+ }
+
+
+ protected override void OnMouseRightButtonDown(MouseButtonEventArgs e)
+ {
+ base.OnMouseRightButtonDown(e);
+
+ if (VisualTreeHelper.HitTest(this, e.GetPosition(this)) == null) return;
+ IsSelected = true;
+ Focus();
+ }
+
+ protected override void OnHeaderChanged(object oldHeader, object newHeader)
+ {
+ base.OnHeaderChanged(oldHeader, newHeader);
+
+ if (TabPanel != null)
+ {
+ TabPanel.ForceUpdate = true;
+ InvalidateMeasure();
+ TabPanel.ForceUpdate = true;
+ }
+ }
+
+ internal void Close()
+ {
+ var parent = TabControlParent;
+ if (parent == null) return;
+
+ var item = parent.ItemContainerGenerator.ItemFromContainer(this);
+
+ var argsClosing = new CancelRoutedEventArgs(ClosingEvent, item);
+ RaiseEvent(argsClosing);
+ if (argsClosing.Cancel) return;
+
+ TabPanel.SetValue(TabPanel.FluidMoveDurationPropertyKey, parent.IsAnimationEnabled
+ ? new Duration(TimeSpan.FromMilliseconds(200))
+ : new Duration(TimeSpan.FromMilliseconds(1)));
+
+ parent.IsInternalAction = true;
+ RaiseEvent(new RoutedEventArgs(ClosedEvent, item));
+
+ var list = parent.GetActualList();
+ list?.Remove(item);
+ }
+
+ protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
+ {
+ base.OnMouseLeftButtonDown(e);
+
+ if (VisualTreeHelper.HitTest(this, e.GetPosition(this)) == null) return;
+ var parent = TabControlParent;
+ if (parent == null) return;
+
+ if (parent.IsDraggable && !ItemIsDragging && !_isDragging)
+ {
+ parent.UpdateScroll();
+ TabPanel.SetValue(TabPanel.FluidMoveDurationPropertyKey, new Duration(TimeSpan.FromSeconds(0)));
+ _mouseDownOffsetX = RenderTransform.Value.OffsetX;
+ _scrollHorizontalOffset = parent.GetHorizontalOffset();
+ var mx = TranslatePoint(new Point(), parent).X + _scrollHorizontalOffset;
+ _mouseDownIndex = CalLocationIndex(mx);
+ var subIndex = _mouseDownIndex - CalLocationIndex(_scrollHorizontalOffset);
+ _maxMoveLeft = -subIndex * ItemWidth;
+ _maxMoveRight = parent.ActualWidth - ActualWidth + _maxMoveLeft;
+
+ _isDragging = true;
+ ItemIsDragging = true;
+ _isWaiting = true;
+ _dragPoint = e.GetPosition(parent);
+ _dragPoint = new Point(_dragPoint.X + _scrollHorizontalOffset, _dragPoint.Y);
+ _mouseDownPoint = _dragPoint;
+ CaptureMouse();
+ }
+ }
+
+ protected override void OnMouseMove(MouseEventArgs e)
+ {
+ base.OnMouseMove(e);
+
+ if (ItemIsDragging && _isDragging)
+ {
+ var parent = TabControlParent;
+ if (parent == null) return;
+
+ var subX = TranslatePoint(new Point(), parent).X + _scrollHorizontalOffset;
+ CurrentIndex = CalLocationIndex(subX);
+
+ var p = e.GetPosition(parent);
+ p = new Point(p.X + _scrollHorizontalOffset, p.Y);
+
+ var subLeft = p.X - _dragPoint.X;
+ var totalLeft = p.X - _mouseDownPoint.X;
+
+ if (Math.Abs(subLeft) <= WaitLength && _isWaiting) return;
+
+ _isWaiting = false;
+ _isDragged = true;
+
+ var left = subLeft + RenderTransform.Value.OffsetX;
+ if (totalLeft < _maxMoveLeft)
+ {
+ left = _maxMoveLeft + _mouseDownOffsetX;
+ }
+ else if (totalLeft > _maxMoveRight)
+ {
+ left = _maxMoveRight + _mouseDownOffsetX;
+ }
+
+ var t = new TranslateTransform(left, 0);
+ RenderTransform = t;
+ _dragPoint = p;
+ }
+ }
+
+ protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
+ {
+ base.OnMouseLeftButtonUp(e);
+
+ ReleaseMouseCapture();
+
+ if (_isDragged)
+ {
+ var parent = TabControlParent;
+ if (parent == null) return;
+
+ var subX = TranslatePoint(new Point(), parent).X + _scrollHorizontalOffset;
+ var index = CalLocationIndex(subX);
+ var left = index * ItemWidth;
+ var offsetX = RenderTransform.Value.OffsetX;
+ CreateAnimation(offsetX, offsetX - subX + left, index);
+ }
+
+ _isDragging = false;
+ ItemIsDragging = false;
+ _isDragged = false;
+ }
+
+ protected override void OnMouseDown(MouseButtonEventArgs e)
+ {
+ if (e.ChangedButton == MouseButton.Middle && e.ButtonState == MouseButtonState.Pressed)
+ {
+ if (ShowCloseButton || ShowContextMenu)
+ {
+ Close();
+ }
+ }
+ }
+
+ ///
+ /// 创建动画
+ ///
+ internal void CreateAnimation(double offsetX, double resultX, int index = -1)
+ {
+ var parent = TabControlParent;
+
+ void AnimationCompleted()
+ {
+ RenderTransform = new TranslateTransform(resultX, 0);
+ if (index == -1) return;
+
+ var list = parent.GetActualList();
+ if (list == null) return;
+
+ var item = parent.ItemContainerGenerator.ItemFromContainer(this);
+ if (item == null) return;
+
+ TabPanel.CanUpdate = false;
+ parent.IsInternalAction = true;
+
+ list.Remove(item);
+ parent.IsInternalAction = true;
+ list.Insert(index, item);
+ _tabPanel.SetValue(TabPanel.FluidMoveDurationPropertyKey, new Duration(TimeSpan.FromMilliseconds(0)));
+ TabPanel.CanUpdate = true;
+ TabPanel.ForceUpdate = true;
+ TabPanel.Measure(new Size(TabPanel.DesiredSize.Width, ActualHeight));
+ TabPanel.ForceUpdate = false;
+
+ Focus();
+ IsSelected = true;
+
+ if (!IsMouseCaptured)
+ {
+ parent.SetCurrentValue(Selector.SelectedIndexProperty, _currentIndex);
+ }
+ }
+
+ TargetOffsetX = resultX;
+ if (!parent.IsAnimationEnabled)
+ {
+ AnimationCompleted();
+ return;
+ }
+
+ var animation = CreateAnimation(resultX, AnimationSpeed);
+ animation.FillBehavior = FillBehavior.Stop;
+ animation.Completed += (s1, e1) => AnimationCompleted();
+ var f = new TranslateTransform(offsetX, 0);
+ RenderTransform = f;
+ f.BeginAnimation(TranslateTransform.XProperty, animation, HandoffBehavior.Compose);
+ }
+ ///
+ /// 创建一个Double动画
+ ///
+ ///
+ ///
+ ///
+ public static DoubleAnimation CreateAnimation(double toValue, double milliseconds = 200)
+ {
+ return new(toValue, new Duration(TimeSpan.FromMilliseconds(milliseconds)))
+ {
+ EasingFunction = new PowerEase { EasingMode = EasingMode.EaseInOut }
+ };
+ }
+
+
+ ///
+ /// 计算选项卡当前合适的位置编号
+ ///
+ ///
+ ///
+ private int CalLocationIndex(double left)
+ {
+ if (_isWaiting)
+ {
+ return CurrentIndex;
+ }
+
+ var maxIndex = TabControlParent.Items.Count - 1;
+ var div = (int) (left / ItemWidth);
+ var rest = left % ItemWidth;
+ var result = rest / ItemWidth > .5 ? div + 1 : div;
+
+ return result > maxIndex ? maxIndex : result;
+ }
+
+ public static readonly RoutedEvent ClosingEvent = EventManager.RegisterRoutedEvent("Closing", RoutingStrategy.Bubble, typeof(EventHandler), typeof(TabItem));
+
+ public event EventHandler Closing
+ {
+ add => AddHandler(ClosingEvent, value);
+ remove => RemoveHandler(ClosingEvent, value);
+ }
+
+ public static readonly RoutedEvent ClosedEvent = EventManager.RegisterRoutedEvent("Closed", RoutingStrategy.Bubble, typeof(EventHandler), typeof(TabItem));
+
+ public event EventHandler Closed
+ {
+ add => AddHandler(ClosedEvent, value);
+ remove => RemoveHandler(ClosedEvent, value);
+ }
+}
diff --git a/BPASmartClient.MilkWithTea/Control/TabPanel.cs b/BPASmartClient.MilkWithTea/Control/TabPanel.cs
new file mode 100644
index 00000000..82efc317
--- /dev/null
+++ b/BPASmartClient.MilkWithTea/Control/TabPanel.cs
@@ -0,0 +1,200 @@
+using BPASmartClient.MilkWithTea.Data;
+using System;
+using System.Collections.Generic;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+
+
+namespace BPASmartClient.MilkWithTea.Control;
+
+public class TabPanel : Panel
+{
+ private int _itemCount;
+
+ ///
+ /// 是否可以更新
+ ///
+ internal bool CanUpdate = true;
+
+ ///
+ /// 选项卡字典
+ ///
+ internal Dictionary ItemDic = new();
+
+ public static readonly DependencyPropertyKey FluidMoveDurationPropertyKey =
+ DependencyProperty.RegisterReadOnly("FluidMoveDuration", typeof(Duration), typeof(TabPanel),
+ new PropertyMetadata(new Duration(TimeSpan.FromMilliseconds(0))));
+
+ ///
+ /// 流式行为持续时间
+ ///
+ public static readonly DependencyProperty FluidMoveDurationProperty =
+ FluidMoveDurationPropertyKey.DependencyProperty;
+
+ ///
+ /// 流式行为持续时间
+ ///
+ public Duration FluidMoveDuration
+ {
+ get => (Duration) GetValue(FluidMoveDurationProperty);
+ set => SetValue(FluidMoveDurationProperty, value);
+ }
+
+ ///
+ /// 是否将标签填充
+ ///
+ public static readonly DependencyProperty IsTabFillEnabledProperty = DependencyProperty.Register(
+ "IsTabFillEnabled", typeof(bool), typeof(TabPanel), new PropertyMetadata(ValueBoxes.FalseBox));
+
+ ///
+ /// 是否将标签填充
+ ///
+ public bool IsTabFillEnabled
+ {
+ get => (bool) GetValue(IsTabFillEnabledProperty);
+ set => SetValue(IsTabFillEnabledProperty, ValueBoxes.BooleanBox(value));
+ }
+
+ ///
+ /// 标签宽度
+ ///
+ public static readonly DependencyProperty TabItemWidthProperty = DependencyProperty.Register(
+ "TabItemWidth", typeof(double), typeof(TabPanel), new PropertyMetadata(200.0));
+
+ ///
+ /// 标签宽度
+ ///
+ public double TabItemWidth
+ {
+ get => (double) GetValue(TabItemWidthProperty);
+ set => SetValue(TabItemWidthProperty, value);
+ }
+
+ ///
+ /// 标签高度
+ ///
+ public static readonly DependencyProperty TabItemHeightProperty = DependencyProperty.Register(
+ "TabItemHeight", typeof(double), typeof(TabPanel), new PropertyMetadata(30.0));
+
+ ///
+ /// 标签高度
+ ///
+ public double TabItemHeight
+ {
+ get => (double) GetValue(TabItemHeightProperty);
+ set => SetValue(TabItemHeightProperty, value);
+ }
+
+ ///
+ /// 是否可以强制更新
+ ///
+ internal bool ForceUpdate;
+
+ private Size _oldSize;
+
+ ///
+ /// 是否已经加载
+ ///
+ private bool _isLoaded;
+
+ protected override Size MeasureOverride(Size constraint)
+ {
+ if ((_itemCount == InternalChildren.Count || !CanUpdate) && !ForceUpdate && !IsTabFillEnabled) return _oldSize;
+ constraint.Height = TabItemHeight;
+ _itemCount = InternalChildren.Count;
+
+ var size = new Size();
+
+ ItemDic.Clear();
+
+ var count = InternalChildren.Count;
+ if (count == 0)
+ {
+ _oldSize = new Size();
+ return _oldSize;
+ }
+ constraint.Width += InternalChildren.Count;
+
+ var itemWidth = .0;
+ var arr = new int[count];
+
+ if (!IsTabFillEnabled)
+ {
+ itemWidth = TabItemWidth;
+ }
+ else
+ {
+ if (TemplatedParent is TabControl tabControl)
+ {
+ arr = DivideInt2Arr((int) tabControl.ActualWidth + InternalChildren.Count, count);
+ }
+ }
+
+ for (var index = 0; index < count; index++)
+ {
+ if (IsTabFillEnabled)
+ {
+ itemWidth = arr[index];
+ }
+ if (InternalChildren[index] is TabItem tabItem)
+ {
+ tabItem.RenderTransform = new TranslateTransform();
+ tabItem.MaxWidth = itemWidth;
+ var rect = new Rect
+ {
+ X = size.Width - tabItem.BorderThickness.Left,
+ Width = itemWidth,
+ Height = TabItemHeight
+ };
+ tabItem.Arrange(rect);
+ tabItem.ItemWidth = itemWidth - tabItem.BorderThickness.Left;
+ tabItem.CurrentIndex = index;
+ tabItem.TargetOffsetX = 0;
+ ItemDic[index] = tabItem;
+ size.Width += tabItem.ItemWidth;
+ }
+ }
+ size.Height = constraint.Height;
+ _oldSize = size;
+ return _oldSize;
+ }
+
+ ///
+ /// 平分一个整数到一个数组中
+ ///
+ ///
+ ///
+ ///
+ public static int[] DivideInt2Arr(int num, int count)
+ {
+ var arr = new int[count];
+ var div = num / count;
+ var rest = num % count;
+ for (var i = 0; i < count; i++)
+ {
+ arr[i] = div;
+ }
+ for (var i = 0; i < rest; i++)
+ {
+ arr[i] += 1;
+ }
+ return arr;
+ }
+
+ public TabPanel()
+ {
+ Loaded += (s, e) =>
+ {
+ if (_isLoaded) return;
+ ForceUpdate = true;
+ Measure(new Size(DesiredSize.Width, ActualHeight));
+ ForceUpdate = false;
+ foreach (var item in ItemDic.Values)
+ {
+ item.TabPanel = this;
+ }
+ _isLoaded = true;
+ };
+ }
+}
diff --git a/BPASmartClient.MilkWithTea/Data/CancelRoutedEventArgs.cs b/BPASmartClient.MilkWithTea/Data/CancelRoutedEventArgs.cs
new file mode 100644
index 00000000..b5a10ba4
--- /dev/null
+++ b/BPASmartClient.MilkWithTea/Data/CancelRoutedEventArgs.cs
@@ -0,0 +1,12 @@
+using System.Windows;
+
+namespace BPASmartClient.MilkWithTea.Data;
+
+public class CancelRoutedEventArgs : RoutedEventArgs
+{
+ public CancelRoutedEventArgs(RoutedEvent routedEvent, object source) : base(routedEvent, source)
+ {
+ }
+
+ public bool Cancel { get; set; }
+}
diff --git a/BPASmartClient.MilkWithTea/Data/ValueBoxes.cs b/BPASmartClient.MilkWithTea/Data/ValueBoxes.cs
new file mode 100644
index 00000000..84495fab
--- /dev/null
+++ b/BPASmartClient.MilkWithTea/Data/ValueBoxes.cs
@@ -0,0 +1,69 @@
+using System;
+using System.Windows;
+using System.Windows.Controls;
+
+namespace BPASmartClient.MilkWithTea.Data;
+
+///
+/// 装箱后的值类型(用于提高效率)
+///
+internal static class ValueBoxes
+{
+ internal static object TrueBox = true;
+
+ internal static object FalseBox = false;
+
+ internal static object VerticalBox = Orientation.Vertical;
+
+ internal static object HorizontalBox = Orientation.Horizontal;
+
+ internal static object VisibleBox = Visibility.Visible;
+
+ internal static object CollapsedBox = Visibility.Collapsed;
+
+ internal static object HiddenBox = Visibility.Hidden;
+
+ internal static object Double01Box = .1;
+
+ internal static object Double0Box = .0;
+
+ internal static object Double1Box = 1.0;
+
+ internal static object Double10Box = 10.0;
+
+ internal static object Double20Box = 20.0;
+
+ internal static object Double100Box = 100.0;
+
+ internal static object Double200Box = 200.0;
+
+ internal static object Double300Box = 300.0;
+
+ internal static object DoubleNeg1Box = -1.0;
+
+ internal static object Int0Box = 0;
+
+ internal static object Int1Box = 1;
+
+ internal static object Int2Box = 2;
+
+ internal static object Int5Box = 5;
+
+ internal static object Int99Box = 99;
+
+ internal static object BooleanBox(bool value) => value ? TrueBox : FalseBox;
+
+ internal static object OrientationBox(Orientation value) =>
+ value == Orientation.Horizontal ? HorizontalBox : VerticalBox;
+
+ internal static object VisibilityBox(Visibility value)
+ {
+ return value switch
+ {
+ Visibility.Visible => VisibleBox,
+ Visibility.Hidden => HiddenBox,
+ Visibility.Collapsed => CollapsedBox,
+ _ => throw new ArgumentOutOfRangeException(nameof(value), value, null)
+ };
+ }
+}
diff --git a/BPASmartClient.MilkWithTea/GLobal.cs b/BPASmartClient.MilkWithTea/GLobal.cs
index 386fbb90..a3a2dfe8 100644
--- a/BPASmartClient.MilkWithTea/GLobal.cs
+++ b/BPASmartClient.MilkWithTea/GLobal.cs
@@ -1,5 +1,5 @@
using BPASmartClient.Model;
-using Model;
+using BPASmartClient.MorkMOC;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
@@ -17,46 +17,15 @@ namespace BPASmartClient.MilkWithTea
public static string recipePath = string.Empty;
public static string posionPath = string.Empty;
- public static bool makeEnable = false;
-
- public static ObservableCollection MaterialRecipes { get; set; } = new ObservableCollection();
+ public static bool makeEnable = true;
///
- /// 设备信息列表
+ /// 设备信息集合
///
public static ObservableCollection deviceConfig { get; set; } = new ObservableCollection();
- ///
- /// 获取Json文件内容,转换成ObservableCollection
- ///
- ///
- ///
- ///
- public static ObservableCollection GetJsonToT(string path)
- {
- if (!File.Exists(path))
- {
- //创建该文件
- File.Create(path);
- return default;
- }
- else
- {
- using (StreamReader recipeReader = new StreamReader(path))//读取json文件
- {
- string datacache = "";
- string line;
- while ((line = recipeReader.ReadLine()) != null) //循环将每一行数据拼接为一个完整的字符串
- {
- datacache = datacache + line;
- }
- var res = JsonConvert.DeserializeObject>(datacache); //将string转换为class类,从而达到json文件转换的目的
- if (res != null)
- return res;
- else return new ObservableCollection { };
- }
- }
- }
+
+
}
}
diff --git a/BPASmartClient.MilkWithTea/MainWindow.xaml.cs b/BPASmartClient.MilkWithTea/MainWindow.xaml.cs
index 8d2e064e..9ee91d82 100644
--- a/BPASmartClient.MilkWithTea/MainWindow.xaml.cs
+++ b/BPASmartClient.MilkWithTea/MainWindow.xaml.cs
@@ -7,6 +7,7 @@ using BPASmartClient.IoT;
using BPASmartClient.Message;
using BPASmartClient.Peripheral;
using BPASmartClient.ViewModel;
+using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
@@ -36,8 +37,10 @@ namespace BPASmartClient.MilkWithTea
public MainWindow()
{
InitializeComponent();
-
- Initialize();
+
+ TextHelper.GetInstance.WriteTextInfo("MOC", "StartShop", "DeviceConfig");
+ CreateDevice();
+ //Initialize();
}
@@ -45,6 +48,50 @@ namespace BPASmartClient.MilkWithTea
DoubleAnimation yd1 = new DoubleAnimation();//实例化浮点动画
DoubleAnimation yd2 = new DoubleAnimation();
+ private void CreateDevice()
+ {
+ DirectoryInfo directoryInfo = new DirectoryInfo(LocaPath.GetInstance().GetDeviceConfigPath);
+ if(!File.Exists($"{directoryInfo}Moc.json"))
+ {
+ File.WriteAllText($"{directoryInfo}Moc.json", JsonConvert.SerializeObject(GLobal.deviceConfig));
+ }
+ string JsonString = File.ReadAllText($"{directoryInfo}MOC.json");
+ var result = JsonConvert.DeserializeObject>(JsonString);
+ if (result != null)
+ {
+ ShopDeviceConfigViewModel.deviceConfig.Clear();
+ foreach (var item in result)
+ {
+ GLobal.deviceConfig.Add(item);
+ }
+ }
+
+ if(GLobal.deviceConfig.Count <1)
+ {
+ GLobal.deviceConfig.Add(new DeviceConfigModelJson());
+ if(GLobal.deviceConfig.ElementAtOrDefault(0).deviceModels.Count <1)
+ {
+ GLobal.deviceConfig.ElementAtOrDefault(0).deviceModels.Add(new DeviceModel());
+ if(GLobal.deviceConfig.ElementAtOrDefault(0).deviceModels.ElementAtOrDefault(0).communicationDevcies.Count <1)
+ {
+ GLobal.deviceConfig.ElementAtOrDefault(0).deviceModels.ElementAtOrDefault(0).communicationDevcies.Add(new CommunicationModel());
+ }
+ }
+ }
+ if (GLobal.deviceConfig.ElementAt(0).deviceModels.ElementAt(0).communicationDevcies.ElementAt(0).variables.Count < 1)
+ {
+ for (int i = 0; i < 20; i++)
+ {
+ GLobal.deviceConfig.ElementAt(0).deviceModels.ElementAt(0).communicationDevcies.ElementAt(0).variables.Add(new Variable()
+ {
+ Id = GLobal.deviceConfig.ElementAt(0).deviceModels.ElementAt(0).communicationDevcies.ElementAt(0).variables.Count,
+ Address = string.Empty,
+ ReadLeng = 0
+ });
+ }
+ File.WriteAllText($"{LocaPath.GetInstance().GetDeviceConfigPath}MOC.json", JsonConvert.SerializeObject(GLobal.deviceConfig));
+ }
+ }
private void Initialize()
{
diff --git a/BPASmartClient.MilkWithTea/Model/JsonDeviceConfig.cs b/BPASmartClient.MilkWithTea/Model/JsonDeviceConfig.cs
new file mode 100644
index 00000000..8d8efb1c
--- /dev/null
+++ b/BPASmartClient.MilkWithTea/Model/JsonDeviceConfig.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace BPASmartClient.MilkWithTea.Model
+{
+ public class JsonDeviceConfig
+ {
+ public ObservableCollection deviceConfig { get; set; } = new ObservableCollection();
+ }
+}
diff --git a/BPASmartClient.MilkWithTea/Model/JsonLocalRecipes.cs b/BPASmartClient.MilkWithTea/Model/JsonLocalRecipes.cs
new file mode 100644
index 00000000..e5482a0e
--- /dev/null
+++ b/BPASmartClient.MilkWithTea/Model/JsonLocalRecipes.cs
@@ -0,0 +1,23 @@
+using BPASmartClient.MorkMOC;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace BPASmartClient.MilkWithTea.Model
+{
+ partial class JsonLocalRecipes
+ {
+ ///
+ /// 本地奶茶配方
+ ///
+ public ObservableCollection localRecipes { get; set; } = new ObservableCollection();
+ ///
+ /// 本地原料
+ ///
+ public ObservableCollection localMaterails { get; set; } = new ObservableCollection();
+
+ }
+}
diff --git a/BPASmartClient.MilkWithTea/View/LocalConfigureView.xaml b/BPASmartClient.MilkWithTea/View/LocalConfigureView.xaml
index 9b6ed672..4bcf9727 100644
--- a/BPASmartClient.MilkWithTea/View/LocalConfigureView.xaml
+++ b/BPASmartClient.MilkWithTea/View/LocalConfigureView.xaml
@@ -11,48 +11,6 @@
-
-
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ -->
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
+
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/BPASmartClient.MilkWithTea/View/LocalConfigureView.xaml.cs b/BPASmartClient.MilkWithTea/View/LocalConfigureView.xaml.cs
index e3d3a769..6099ee62 100644
--- a/BPASmartClient.MilkWithTea/View/LocalConfigureView.xaml.cs
+++ b/BPASmartClient.MilkWithTea/View/LocalConfigureView.xaml.cs
@@ -1,4 +1,5 @@
-using System;
+using BPASmartClient.MilkWithTea.ViewModel;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -24,5 +25,11 @@ namespace BPASmartClient.MilkWithTea.View
{
InitializeComponent();
}
+
+ private void Button_Click(object sender, RoutedEventArgs e)
+ {
+ RecipeConfige recipeConfige = new RecipeConfige();
+ recipeConfige.ShowDialog();
+ }
}
}
diff --git a/BPASmartClient.MilkWithTea/View/MainControlView.xaml b/BPASmartClient.MilkWithTea/View/MainControlView.xaml
index d2aed96e..46e6263a 100644
--- a/BPASmartClient.MilkWithTea/View/MainControlView.xaml
+++ b/BPASmartClient.MilkWithTea/View/MainControlView.xaml
@@ -168,11 +168,11 @@
+ Foreground="LightSlateGray" SelectedValue="{Binding SelectedRecipe,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
-
@@ -181,7 +181,7 @@
@@ -191,7 +191,7 @@
-
+
diff --git a/BPASmartClient.MilkWithTea/View/MainControlView.xaml.cs b/BPASmartClient.MilkWithTea/View/MainControlView.xaml.cs
index 8593362a..8388f1cb 100644
--- a/BPASmartClient.MilkWithTea/View/MainControlView.xaml.cs
+++ b/BPASmartClient.MilkWithTea/View/MainControlView.xaml.cs
@@ -1,5 +1,4 @@
using BPASmartClient.MilkWithTea.ViewModel;
-using Model;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -29,9 +28,9 @@ namespace BPASmartClient.MilkWithTea.View
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
- if((sender as ListBox).SelectedItem is LocalTeaWithMilkConfig config)
+ if((sender as ListBox).SelectedItem is LocalRecipe config)
{
- recipeList.ItemsSource = config.materialRecipes;
+ recipeList.ItemsSource = config.localMaterails;
}
}
}
diff --git a/BPASmartClient.MilkWithTea/View/ParameterSetting.xaml b/BPASmartClient.MilkWithTea/View/ParameterSetting.xaml
index 281b45b3..77c36e79 100644
--- a/BPASmartClient.MilkWithTea/View/ParameterSetting.xaml
+++ b/BPASmartClient.MilkWithTea/View/ParameterSetting.xaml
@@ -11,13 +11,7 @@
-
+
@@ -40,18 +34,18 @@
-
- -->
+
-
+
-
+
-
+
+
+
+
+
+
+
+
+
diff --git a/WPFDemo/Window2.xaml.cs b/WPFDemo/Window2.xaml.cs
index ec78fc3b..530ef240 100644
--- a/WPFDemo/Window2.xaml.cs
+++ b/WPFDemo/Window2.xaml.cs
@@ -23,307 +23,9 @@ namespace WPFDemo
///
public partial class Window2 : Window
{
- TestModel testModel=new TestModel();
public Window2()
{
InitializeComponent();
- this.DataContext = testModel;
-
-
- testModel.ViewItems.Add(new PageModel {Name="张三", Ph="125486545" });
- testModel.ViewItems.Add(new PageModel { Name = "李四", Ph = "125486545" });
- testModel.ViewItems.Add(new PageModel { Name = "王麻子", Ph = "125486545" });
- testModel.ViewItems.Add(new PageModel { Name = "二货", Ph = "125486545" });
- }
-
- #region 移动事件
- ///
- /// 当前拖动子控件流
- ///
- private UIElement ChildElement;
- ///
- /// 是否已经按下
- ///
- private bool isDown = false;
- ///
- /// 按下记录初始鼠标点位
- /// 鼠标相对与拖动子控件的位置(偏移量)
- ///
- private Point InitPoint;
- ///
- /// 当前拖动的Pop窗体
- ///
- private Popup DropPopup = null;
-
-
- ///
- /// 鼠标按下事件
- ///
- ///
- ///
- private void Grid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
- {
- try
- {
- //isDown变量是防止多次操作。
- if (isDown)
- {
- isDown = false;
- return;
- }
-
- ChildElement = (UIElement)sender;
- ChildElement.CaptureMouse();//设置了鼠标捕获,这样它可以不受到其它控件的影响。
-
- InitPoint= new Point(e.GetPosition(ChildElement).X, e.GetPosition(ChildElement).Y);
-
- //加蒙层,表明已经开始拖动
- //暂时未加
-
- CreatePopup(ChildElement, e);
-
- isDown = true;
- }
- catch (Exception ex)
- {
-
- }
}
- ///
- /// 鼠标抬起事件
- ///
- ///
- ///
- private void lisbox_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
- {
- try
- {
- //鼠标未按下返回
- if (!isDown) return;
-
- isDown = false;
-
- //关闭Pop窗体
- if (this.DropPopup != null)
- {
- this.DropPopup.IsOpen = false;
- this.DropPopup.Child = null;
- this.DropPopup = null;
- }
-
- //蒙层关闭
-
- ChildElement.ReleaseMouseCapture();//当控件具有鼠标捕获的话,则释放该捕获。
-
- //double y = e.GetPosition(this).Y;//获取鼠标抬起时的控件的位置 Y值
- //double start = 0.0;
- //int row = 0;
- //foreach (RowDefinition rd in myGrid.RowDefinitions)
- //{
- // start += rd.ActualHeight;
- // if (y < start)
- // {
- // break;
- // }
- // row++;
- //}
- //double x = e.GetPosition(myGrid).X;//获取鼠标抬起时的控件的位置 X值
- //double cstart = 0.0;
- //int column = 0;
- //foreach (ColumnDefinition cd in myGrid.ColumnDefinitions)
- //{
- // cstart += cd.ActualWidth;
- // if (x < cstart)
- // {
- // break;
- // }
- // column++;
- //}
- //var initRow = Grid.GetRow(ultUE);//找到控件所在的行
- //var initCol = Grid.GetColumn(ultUE);//找到控件所在的列
- //UIElement uIElement = null;
- //if (row != initRow || column != initCol)
- //{
- // uIElement = GetChildren(myGrid, row, column);
- //}
- //if (uIElement != null)
- //{
- // //下面是交换两个控件的位置 (需要先移除再加载)
- // myGrid.Children.Remove(uIElement);
- // Grid.SetColumn(ultUE, column);//指定控件在grid中哪行哪例
- // Grid.SetRow(ultUE, row);
- // myGrid.Children.Add(uIElement);
- // Grid.SetColumn(uIElement, initCol);
- // Grid.SetRow(uIElement, initRow);
- //}
- }
- catch (Exception ex)
- {
-
- }
- }
- ///
- /// 鼠标移动事件
- ///
- ///
- ///
- private void ListView_PreviewMouseMove(object sender, MouseEventArgs e)
- {
- try
- {
- if (isDown == false) return;
-
- Point ptLeftUp = new Point(0, 0);
- Point ptRightDown = new Point(this.ActualWidth, this.ActualHeight);
-
- ptLeftUp = this.PointToScreen(ptLeftUp);
- ptRightDown = this.PointToScreen(ptRightDown);
-
- double y = e.GetPosition(lisbox).Y;
- double x = e.GetPosition(lisbox).X;
-
-
- if (DropPopup != null)
- {
- //下面两句是设置Popup控件的位置除以2是想让鼠标在它的中心
- DropPopup.HorizontalOffset = ptLeftUp.X + x - ((FrameworkElement)ChildElement).ActualWidth / 2;
- DropPopup.VerticalOffset = ptLeftUp.Y + y - ((FrameworkElement)ChildElement).ActualHeight / 2;
- }
-
- //double start = 0.0;
- //int row = 0;
- //foreach (RowDefinition rd in myGrid.RowDefinitions)
- //{
- // start += rd.ActualHeight;
- // if (y < start)
- // {
- // break;
- // }
- // row++;
- //}
- //double cstart = 0.0;
- //int column = 0;
- //foreach (ColumnDefinition cd in myGrid.ColumnDefinitions)
- //{
- // cstart += cd.ActualWidth;
- // if (x < cstart)
- // {
- // break;
- // }
- // column++;
- //}
- ////下面的代码是当鼠标移动到哪个格子哪个格子就会亮起来。
- //UIElement uIElement = GetChildren(myGrid, row, column);
- //foreach (UIElement element in myGrid.Children)
- //{
- // if (element != ultUE && element != uIElement)
- // {
- // element.Opacity = moveOpacity;
- // }
- // else
- // {
- // element.Opacity = 1;
- // }
- //}
- }
- catch (Exception ex)
- {
-
- }
- }
- ///
- /// 创建浮动窗口
- ///
- ///
- ///
- private void CreatePopup(Visual dragElement, MouseButtonEventArgs e)
- {
- //使用PointToScreen函数可以将点转换为屏幕坐标
- //首先获取当前窗体的左上角和右下角两点的坐标
- Point ptLeftUp = new Point(0, 0);
- //转换获取到这个窗口相对于屏幕两个坐标
- ptLeftUp = this.PointToScreen(ptLeftUp);
-
- //获取myGrid的实际宽高
- double y = e.GetPosition(this).Y;
- double x = e.GetPosition(this).X;
-
-
- this.DropPopup = new Popup();
- Border border = new Border();
- border.Margin = new Thickness(0, 0, 8, 8);
- DropShadowEffect effect = new DropShadowEffect();
- effect.Opacity = 1;
- effect.ShadowDepth = -14;
- effect.BlurRadius = 9;
- effect.Color = Color.FromArgb(100, 0, 0, 0);
- border.Effect = effect;
-
-
- Rectangle r = new Rectangle();
- r.Width = ((FrameworkElement)dragElement).ActualWidth;
- r.Height = ((FrameworkElement)dragElement).ActualHeight;
- r.Fill = new VisualBrush(dragElement);
- border.Child = r;
- this.DropPopup.Child = border;
- DropPopup.AllowsTransparency = true;
- DropPopup.HorizontalOffset = ptLeftUp.X + x - ((FrameworkElement)dragElement).ActualWidth / 2;
- DropPopup.VerticalOffset = ptLeftUp.Y + y - ((FrameworkElement)dragElement).ActualHeight / 2;
-
- this.DropPopup.IsOpen = true;
- }
- #endregion
- }
-
- public class TestModel : ObservableObject
- {
- private ObservableCollection _pageModels;
- public ObservableCollection ViewItems
- {
- get
- {
- return _pageModels;
- }
- set
- {
- _pageModels = value;
- OnPropertyChanged("ViewItems");
- }
- }
- public TestModel()
- {
- ViewItems=new ObservableCollection();
- }
- }
-
- public class PageModel : ObservableObject
- {
- private string _Name;
- public string Name
- {
- get
- {
- return _Name;
- }
- set
- {
- _Name = value;
- OnPropertyChanged("Name");
- }
- }
- private string _Ph;
- public string Ph
- {
- get
- {
- return _Ph;
- }
- set
- {
- _Ph = value;
- OnPropertyChanged("Ph");
- }
- }
-
}
}