From b87c5d50fc7c4a2017b590d67165524dc3ab85b2 Mon Sep 17 00:00:00 2001 From: fyf Date: Sat, 15 Oct 2022 13:50:38 +0800 Subject: [PATCH 1/7] 1 --- WPFDemo/TheListBox.xaml | 29 +++ WPFDemo/TheListBox.xaml.cs | 356 +++++++++++++++++++++++++++++++++++++ WPFDemo/Window2.xaml | 29 +-- WPFDemo/Window2.xaml.cs | 298 ------------------------------- 4 files changed, 390 insertions(+), 322 deletions(-) create mode 100644 WPFDemo/TheListBox.xaml create mode 100644 WPFDemo/TheListBox.xaml.cs diff --git a/WPFDemo/TheListBox.xaml b/WPFDemo/TheListBox.xaml new file mode 100644 index 00000000..2c6eddc9 --- /dev/null +++ b/WPFDemo/TheListBox.xaml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + diff --git a/WPFDemo/TheListBox.xaml.cs b/WPFDemo/TheListBox.xaml.cs new file mode 100644 index 00000000..a95547a4 --- /dev/null +++ b/WPFDemo/TheListBox.xaml.cs @@ -0,0 +1,356 @@ +using Microsoft.Toolkit.Mvvm.ComponentModel; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Effects; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace WPFDemo +{ + /// + /// fyf 创建测试案例 + /// TheListBox.xaml 的交互逻辑 + /// + public partial class TheListBox : UserControl + { + /// + /// 数据model + /// + public ListBoxDataModel TheListBoxModel = new ListBoxDataModel(); + public TheListBox() + { + InitializeComponent(); + this.DataContext = TheListBoxModel; + TheListBoxModel.ViewItems.Add(new ItemModel { Name = "张三", Ph = "125486545" }); + TheListBoxModel.ViewItems.Add(new ItemModel { Name = "李四", Ph = "125486545" }); + TheListBoxModel.ViewItems.Add(new ItemModel { Name = "王麻子", Ph = "125486545" }); + TheListBoxModel.ViewItems.Add(new ItemModel { Name = "二货", Ph = "125486545" }); + TheListBoxModel.ViewItems.Add(new ItemModel { Name = "张三1", Ph = "125486545" }); + TheListBoxModel.ViewItems.Add(new ItemModel { Name = "李四2", Ph = "125486545" }); + TheListBoxModel.ViewItems.Add(new ItemModel { Name = "王麻子3", Ph = "125486545" }); + } + #region 移动事件 + /// + /// 当前拖动子控件流 + /// + private UIElement ChildElement; + /// + /// 当前拖拽子控件Item + /// + private ListBoxItem ChildListBoxItem; + /// + /// 是否已经按下 + /// + private bool isDown = false; + /// + /// 当前拖动的Pop窗体 + /// + private Popup DropPopup = null; + /// + /// 鼠标按下事件 + /// + /// + /// + private void Border_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) + { + try + { + //isDown变量是防止多次操作。 + if (isDown) + { + isDown = false; + return; + } + + ChildElement = (UIElement)sender; + ChildElement.CaptureMouse();//设置了鼠标捕获,这样它可以不受到其它控件的影响。 + ChildListBoxItem = Utils.FindVisualParent(VisualTreeHelper.HitTest(ChildElement, e.GetPosition(ChildElement)).VisualHit); + + //创建一个Pop,表明拖拽开始 + 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; + } + + //蒙层关闭,表明结束拖拽 + MoveListBoxStyle(null, false); + + //当控件具有鼠标捕获的话,则释放该捕获。 + ChildElement.ReleaseMouseCapture(); + } + catch (Exception ex) + { + + } + } + /// + /// 鼠标移动事件 + /// + /// + /// + private void ListView_PreviewMouseMove(object sender, MouseEventArgs e) + { + try + { + if (isDown == false) return; + + if (e.LeftButton != MouseButtonState.Pressed) + lisbox_PreviewMouseLeftButtonUp(null, null); + + 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(this).Y; + double x = e.GetPosition(this).X; + + if (DropPopup != null)//下面两句是设置Popup控件的位置除以2是想让鼠标在它的中心 + { + DropPopup.HorizontalOffset = ptLeftUp.X + x - ((FrameworkElement)ChildElement).ActualWidth / 2; + DropPopup.VerticalOffset = ptLeftUp.Y + y - ((FrameworkElement)ChildElement).ActualHeight / 2; + } + MoveListBoxStyle(e,true); + } + catch (Exception ex) + { + } + } + /// + /// 移动效果 + /// + /// + private void MoveListBoxStyle(MouseEventArgs e,bool isBool) + { + try + { + if (isBool)//为真,根据鼠标位置设置行透明度和显示状态 + { + //移动到某行减轻某行 暗黑 + foreach (ListBoxItem item in Utils.FindVisualChildren(listView)) + { + if (item != ChildListBoxItem)//这就是其他控件 + { + double item_width = item.ActualWidth; + double item_height = item.ActualHeight; + double item_x = e.GetPosition(item).X; + double item_y = e.GetPosition(item).Y; + double Child_x = e.GetPosition(ChildElement).X; + double Child_y = e.GetPosition(ChildElement).Y; + + if (item_y <= item_height && item_y > 0 && item_x > 0 && item_x <= item_width)//鼠标进入哪一行,则将那一行变灰 + { + item.Opacity = 0.5; + int lao_index = TheListBoxModel.ViewItems.IndexOf(item.Content as ItemModel); + int new_index = TheListBoxModel.ViewItems.IndexOf(ChildListBoxItem.Content as ItemModel); + TheListBoxModel.ViewItems.Move(lao_index, new_index); + } + else //鼠标没在哪一行,则保持原状 + { + item.Opacity = 1; + } + } + else + { + item.Visibility = Visibility.Hidden; + } + } + } + else//为假 恢复所有行透明度和显示状态 + { + //移动到某行减轻某行 暗黑 + foreach (ListBoxItem item in Utils.FindVisualChildren(listView)) + { + item.Opacity = 1; + item.Visibility = Visibility.Visible; + } + } + } + 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; + + //拖拽Popup框 + 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 + } + /// + /// 当前项数据Model + /// + public class ListBoxDataModel : ObservableObject + { + private ObservableCollection _ViewItems; + public ObservableCollection ViewItems + { + get + { + return _ViewItems; + } + set + { + _ViewItems = value; + OnPropertyChanged("ViewItems"); + } + } + public ListBoxDataModel() + { + ViewItems = new ObservableCollection(); + } + } + /// + /// 行数据Model + /// + public class ItemModel : 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"); + } + } + } + /// + /// 帮助类 + /// + internal static class Utils + { + /// + /// 根据子元素查找父元素 + /// + /// + /// + /// + public static T FindVisualParent(DependencyObject obj) where T : class + { + while (obj != null) + { + if (obj is T) + return obj as T; + + obj = VisualTreeHelper.GetParent(obj); + } + return null; + } + /// + /// 查询子控件 + /// + /// + /// + /// + public static IEnumerable FindVisualChildren(DependencyObject depObj) where T : DependencyObject + { + if (depObj != null) + { + for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++) + { + DependencyObject child = VisualTreeHelper.GetChild(depObj, i); + if (child != null && child is T) + { + yield return (T)child; + } + + foreach (T childOfChild in FindVisualChildren(child)) + { + yield return childOfChild; + } + } + } + } + } +} diff --git a/WPFDemo/Window2.xaml b/WPFDemo/Window2.xaml index 40f394a0..797d065b 100644 --- a/WPFDemo/Window2.xaml +++ b/WPFDemo/Window2.xaml @@ -10,29 +10,10 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + 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"); - } - } - } } From f36cdaff7e05a9326d3d5abb21bc88f4d760c43d Mon Sep 17 00:00:00 2001 From: fyf Date: Sat, 15 Oct 2022 13:55:00 +0800 Subject: [PATCH 2/7] 1 --- WPFDemo/TheListBox.xaml.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/WPFDemo/TheListBox.xaml.cs b/WPFDemo/TheListBox.xaml.cs index a95547a4..9b016e32 100644 --- a/WPFDemo/TheListBox.xaml.cs +++ b/WPFDemo/TheListBox.xaml.cs @@ -149,7 +149,9 @@ namespace WPFDemo DropPopup.HorizontalOffset = ptLeftUp.X + x - ((FrameworkElement)ChildElement).ActualWidth / 2; DropPopup.VerticalOffset = ptLeftUp.Y + y - ((FrameworkElement)ChildElement).ActualHeight / 2; } - MoveListBoxStyle(e,true); + + //蒙层打开,表明拖拽开始,设置透明度和显示状态 + MoveListBoxStyle(e, true); } catch (Exception ex) { @@ -159,7 +161,7 @@ namespace WPFDemo /// 移动效果 /// /// - private void MoveListBoxStyle(MouseEventArgs e,bool isBool) + private void MoveListBoxStyle(MouseEventArgs e, bool isBool) { try { @@ -170,12 +172,10 @@ namespace WPFDemo { if (item != ChildListBoxItem)//这就是其他控件 { - double item_width = item.ActualWidth; - double item_height = item.ActualHeight; - double item_x = e.GetPosition(item).X; - double item_y = e.GetPosition(item).Y; - double Child_x = e.GetPosition(ChildElement).X; - double Child_y = e.GetPosition(ChildElement).Y; + double item_width = item.ActualWidth; //当前行宽 + double item_height = item.ActualHeight; //当前行高 + double item_x = e.GetPosition(item).X; //鼠标相对当前行X位移 + double item_y = e.GetPosition(item).Y; //鼠标相对当前行Y位移 if (item_y <= item_height && item_y > 0 && item_x > 0 && item_x <= item_width)//鼠标进入哪一行,则将那一行变灰 { From a05e10a95dbee0e00bdc0beb3288690ab07bbd96 Mon Sep 17 00:00:00 2001 From: NXX <447201003@qq> Date: Sat, 15 Oct 2022 13:56:43 +0800 Subject: [PATCH 3/7] =?UTF-8?q?=E5=B0=8F=E7=82=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BPASmart.RecipeManagement/App.xaml | 14 +- .../BPASmart.RecipeManagement.csproj | 1 + .../View/RecipesConfigure.xaml | 3 +- .../ViewModel/RecipeManagerViewModel.cs | 7 +- BPASmartClient.MilkWithTea/App.xaml | 16 +- BPASmartClient.MilkWithTea/App.xaml.cs | 27 +- .../BPASmartClient.MilkWithTea.csproj | 7 +- .../Control/ContextMenuToggleButton.cs | 30 ++ .../Control/ScrollViewer.cs | 225 ++++++++ .../Control/ScrollViewerAttach.cs | 111 ++++ .../Control/TabControl.cs | 426 +++++++++++++++ BPASmartClient.MilkWithTea/Control/TabItem.cs | 509 ++++++++++++++++++ .../Control/TabPanel.cs | 200 +++++++ .../Data/CancelRoutedEventArgs.cs | 12 + BPASmartClient.MilkWithTea/Data/ValueBoxes.cs | 69 +++ BPASmartClient.MilkWithTea/GLobal.cs | 41 +- BPASmartClient.MilkWithTea/MainWindow.xaml.cs | 51 +- .../Model/JsonDeviceConfig.cs | 13 + .../Model/JsonLocalRecipes.cs | 23 + .../View/LocalConfigureView.xaml | 247 ++++----- .../View/LocalConfigureView.xaml.cs | 9 +- .../View/MainControlView.xaml | 8 +- .../View/MainControlView.xaml.cs | 5 +- .../View/ParameterSetting.xaml | 37 +- .../View/RecipeConfige.xaml | 135 +++++ .../View/RecipeConfige.xaml.cs | 174 ++++++ .../ViewModel/LocalConfigureViewModel.cs | 199 ++----- .../ViewModel/MainControlViewModel.cs | 65 ++- .../ViewModel/MainWindowVeiwModel.cs | 33 +- .../ViewModel/PatrameterSettiongViewModel.cs | 191 ++++--- .../ViewModel/RecipeConfigeViewModel.cs | 75 +++ .../BPASmartClient.Model.csproj | 4 + BPASmartClient.MorkF/Control_MorkF.cs | 243 +++------ BPASmartClient.MorkF/GVL_MorkF.cs | 38 ++ BPASmartClient.MorkF/View/DebugView.xaml | 56 +- .../ViewModel/DebugViewModel.cs | 125 +++-- .../BPASmartClient.MorkMOC.csproj | 7 +- BPASmartClient.MorkMOC/Control_MorkMOC.cs | 27 +- BPASmartClient.MorkMOC/Model/LocalMaterail.cs | 55 ++ BPASmartClient.MorkMOC/OrderLocInfo.cs | 3 + BPASmartClient.MorkS/Control_Morks.cs | 2 + BPASmartClient.MorkS/GVL_MORKS.cs | 1 + .../Model/LocalTeaWithMilkConfig.cs | 4 + BPASmartClient/App.config | 19 +- 44 files changed, 2769 insertions(+), 778 deletions(-) create mode 100644 BPASmartClient.MilkWithTea/Control/ContextMenuToggleButton.cs create mode 100644 BPASmartClient.MilkWithTea/Control/ScrollViewer.cs create mode 100644 BPASmartClient.MilkWithTea/Control/ScrollViewerAttach.cs create mode 100644 BPASmartClient.MilkWithTea/Control/TabControl.cs create mode 100644 BPASmartClient.MilkWithTea/Control/TabItem.cs create mode 100644 BPASmartClient.MilkWithTea/Control/TabPanel.cs create mode 100644 BPASmartClient.MilkWithTea/Data/CancelRoutedEventArgs.cs create mode 100644 BPASmartClient.MilkWithTea/Data/ValueBoxes.cs create mode 100644 BPASmartClient.MilkWithTea/Model/JsonDeviceConfig.cs create mode 100644 BPASmartClient.MilkWithTea/Model/JsonLocalRecipes.cs create mode 100644 BPASmartClient.MilkWithTea/View/RecipeConfige.xaml create mode 100644 BPASmartClient.MilkWithTea/View/RecipeConfige.xaml.cs create mode 100644 BPASmartClient.MilkWithTea/ViewModel/RecipeConfigeViewModel.cs create mode 100644 BPASmartClient.MorkMOC/Model/LocalMaterail.cs 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 @@ - - + + - - - - + + + + + +