翼度科技»论坛 编程开发 .net 查看内容

自定义可移动点二维坐标轴控件

9

主题

9

帖子

27

积分

新手上路

Rank: 1

积分
27
自定义可移动点二维坐标轴控件

目录

路由参数

X_YResultCollection为当前X轴对应Y轴值存储字典
  1. public class ResultCollectionChangedEventArgs(RoutedEvent routedEvent, object source, IDictionary<double, double> resultCollection) : RoutedEventArgs(routedEvent, source)
  2. {
  3.     public IDictionary<double, double> X_YResultCollection = resultCollection;
  4. }
复制代码
坐标轴控件定义
  1. class CoordinateSystemControl : UserControl
  2. {
  3.     #region 依赖属性
  4.     /// <summary>
  5.     /// 标注线颜色
  6.     /// </summary>
  7.     public Brush CalloutLineColor
  8.     {
  9.         get { return (Brush)GetValue(CalloutLineColorProperty); }
  10.         set { SetValue(CalloutLineColorProperty, value); }
  11.     }
  12.     /// <summary>
  13.     /// 坐标系颜色
  14.     /// </summary>
  15.     public Brush CoordinateSystemColor
  16.     {
  17.         get { return (Brush)GetValue(CoordinateSystemColorProperty); }
  18.         set { SetValue(CoordinateSystemColorProperty, value); }
  19.     }
  20.     /// <summary>
  21.     /// X轴名称
  22.     /// </summary>
  23.     public string X_AxisName
  24.     {
  25.         get { return (string)GetValue(X_AxisNameProperty); }
  26.         set { SetValue(X_AxisNameProperty, value); }
  27.     }
  28.     /// <summary>
  29.     /// Y轴名称
  30.     /// </summary>
  31.     public string Y_AxisName
  32.     {
  33.         get { return (string)GetValue(Y_AxisNameProperty); }
  34.         set { SetValue(Y_AxisNameProperty, value); }
  35.     }
  36.     /// <summary>
  37.     /// 选择点颜色
  38.     /// </summary>
  39.     public Brush SelectElementColor
  40.     {
  41.         get { return (Brush)GetValue(SelectElementColorProperty); }
  42.         set { SetValue(SelectElementColorProperty, value); }
  43.     }
  44.     /// <summary>
  45.     /// 选择点被选中图标
  46.     /// </summary>
  47.     public Brush SelectedElementColor
  48.     {
  49.         get { return (Brush)GetValue(SelectedElementColorProperty); }
  50.         set { SetValue(SelectedElementColorProperty, value); }
  51.     }
  52.     /// <summary>
  53.     /// Y轴箭头图标
  54.     /// </summary>
  55.     public FrameworkElement YAeeow
  56.     {
  57.         get { return (FrameworkElement)GetValue(YAeeowProperty); }
  58.         set { SetValue(YAeeowProperty, value); }
  59.     }
  60.     /// <summary>
  61.     /// X轴箭头图标
  62.     /// </summary>
  63.     public FrameworkElement XAeeow
  64.     {
  65.         get { return (FrameworkElement)GetValue(XAeeowProperty); }
  66.         set { SetValue(XAeeowProperty, value); }
  67.     }
  68.     /// <summary>
  69.     /// Y轴集合
  70.     /// </summary>
  71.     public IList<double> YCollection
  72.     {
  73.         get { return (IList<double>)GetValue(YCollectionProperty); }
  74.         set { SetValue(YCollectionProperty, value); }
  75.     }
  76.     /// <summary>
  77.     /// X轴集合
  78.     /// </summary>
  79.     public IList<double> XCollection
  80.     {
  81.         get { return (IList<double>)GetValue(XCollectionProperty); }
  82.         set { SetValue(XCollectionProperty, value); }
  83.     }
  84.     /// <summary>
  85.     /// X轴结点对应Y轴值
  86.     /// </summary>
  87.     public IDictionary<double, double> X_YResultCollection
  88.     {
  89.         get { return (IDictionary<double, double>)GetValue(X_YResultCollectionProperty); }
  90.         set { SetValue(X_YResultCollectionProperty, value); }
  91.     }
  92.     /// <summary>
  93.     /// 结果集合改变命令
  94.     /// </summary>
  95.     public ICommand Command
  96.     {
  97.         get { return (ICommand)GetValue(CommandProperty); }
  98.         set { SetValue(CommandProperty, value); }
  99.     }
  100.     #endregion
  101.     #region 路由事件
  102.     /// <summary>
  103.     /// 结果集合改变路由事件
  104.     /// </summary>
  105.     public static readonly RoutedEvent ResultCollectionChangedEvent = EventManager.RegisterRoutedEvent("ResultCollectionChanged", RoutingStrategy.Bubble, typeof(EventHandler<ResultCollectionChangedEventArgs>), typeof(CoordinateSystemControl));
  106.     public event RoutedEventHandler ResultCollectionChanged
  107.     {
  108.         add => AddHandler(ResultCollectionChangedEvent, value);
  109.         remove => RemoveHandler(ResultCollectionChangedEvent, value);
  110.     }
  111.     #endregion
  112.     #region 依赖属性注册
  113.     public static readonly DependencyProperty CalloutLineColorProperty =
  114.         DependencyProperty.Register("CalloutLineColor", typeof(Brush), typeof(CoordinateSystemControl), new FrameworkPropertyMetadata());
  115.     public static readonly DependencyProperty CoordinateSystemColorProperty =
  116.        DependencyProperty.Register("CoordinateSystemColor", typeof(Brush), typeof(CoordinateSystemControl), new PropertyMetadata());
  117.     public static readonly DependencyProperty X_AxisNameProperty =
  118.         DependencyProperty.Register("X_AxisName", typeof(string), typeof(CoordinateSystemControl), new PropertyMetadata("X轴"));
  119.     public static readonly DependencyProperty Y_AxisNameProperty =
  120.    DependencyProperty.Register("Y_AxisName", typeof(string), typeof(CoordinateSystemControl), new PropertyMetadata("Y轴"));
  121.     public static readonly DependencyProperty SelectElementColorProperty =
  122.     DependencyProperty.Register("SelectElementColor", typeof(Brush), typeof(CoordinateSystemControl), new PropertyMetadata());
  123.     public static readonly DependencyProperty SelectedElementColorProperty =
  124.         DependencyProperty.Register("SelectedElementColor", typeof(Brush), typeof(CoordinateSystemControl), new PropertyMetadata());
  125.     public static readonly DependencyProperty YAeeowProperty =
  126.         DependencyProperty.Register("YAeeow", typeof(FrameworkElement), typeof(CoordinateSystemControl), new FrameworkPropertyMetadata());
  127.     public static readonly DependencyProperty XAeeowProperty =
  128.         DependencyProperty.Register("XAeeow", typeof(FrameworkElement), typeof(CoordinateSystemControl), new FrameworkPropertyMetadata());
  129.     public static readonly DependencyProperty YCollectionProperty =
  130.         DependencyProperty.Register("YCollection", typeof(IList<double>), typeof(CoordinateSystemControl), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None, YCollectionCallback));
  131.     public static readonly DependencyProperty XCollectionProperty =
  132.         DependencyProperty.Register("XCollection", typeof(IList<double>), typeof(CoordinateSystemControl), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None, XCollectionCallback));
  133.     public static readonly DependencyProperty X_YResultCollectionProperty =
  134.         DependencyProperty.Register("X_YResultCollection", typeof(IDictionary<double, double>), typeof(CoordinateSystemControl), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, X_YResultCollectionChangedCallback));
  135.     public static readonly DependencyProperty CommandProperty =
  136.         DependencyProperty.Register("Command", typeof(ICommand), typeof(CoordinateSystemControl), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None, CommandChangedCallback));
  137.     #endregion
  138.     #region 私有字段
  139.     private bool _isFirstLoaded = false;
  140.     //坐标系顶点留空距离
  141.     private readonly int _spacedPoints = 15;
  142.     private readonly Canvas _canvas;
  143.     //X、Y轴线
  144.     private Line _yLine;
  145.     private Line _xLine;
  146.     private Point _originPoint;
  147.     private Point _yEndpoint;
  148.     private Point _xEndpoint;
  149.     private int _ySingleDistance;
  150.     private int _xSingleDistance;
  151.     private double _proportionSize;
  152.     //选择点实时数据位置变形
  153.     private Transform _transform_textBox;
  154.     //选择点位置校准变形
  155.     private Transform _transform_chekEllipse;
  156.     //拖动选择点位置变形
  157.     private Transform _transform_clickMoveEllipse;
  158.     //选择点显示值TextBox映射
  159.     private readonly Dictionary<Ellipse, TextBox> _selectEllipse_TextBoxMap = [];
  160.     //选择点与连接线链表
  161.     private readonly LinkedList<Shape> _selectEllipse_ConnectionLines = [];
  162.     private readonly Dictionary<double, Ellipse> _xValue_EllipseMap = [];
  163.     #endregion
  164.     public CoordinateSystemControl()
  165.     {
  166.         _canvas = new Canvas() { Margin = new Thickness(20) };
  167.         _canvas.SizeChanged += Canvas_SizeChanged;
  168.         _canvas.Loaded += Canvas_Loaded;
  169.         this.AddChild(_canvas);
  170.         _transform_textBox = new TranslateTransform() { X = -13, Y = -25 };
  171.         _transform_chekEllipse = new TranslateTransform() { X = -7, Y = -5 };
  172.         _transform_clickMoveEllipse = new TransformGroup
  173.         {
  174.             Children = { new TranslateTransform() { X = -3, Y = -5 }, new ScaleTransform(1.5, 1.5) }
  175.         };
  176.         //初始化依赖属性
  177.         CalloutLineColor = Brushes.DarkBlue;
  178.         CoordinateSystemColor = Brushes.Black;
  179.         SelectElementColor = Brushes.BlueViolet;
  180.         SelectedElementColor = Brushes.MediumVioletRed;
  181.     }
  182.     #region 事件与回调
  183.     private void Canvas_Loaded(object sender, RoutedEventArgs e)
  184.     {
  185.         Initialize();
  186.         _isFirstLoaded = true;
  187.     }
  188.     private static void CommandChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
  189.     {
  190.         if (e.NewValue is ICommand command)
  191.         {
  192.             var sender = d as CoordinateSystemControl;
  193.             command.CanExecuteChanged -= sender!.Command_CanExecuteChanged;
  194.             command.CanExecuteChanged += sender.Command_CanExecuteChanged;
  195.         }
  196.     }
  197.     private void Command_CanExecuteChanged(object? sender, EventArgs e)
  198.     {
  199.         IsEnabled = Command.CanExecute(null);
  200.     }
  201.     private static void YCollectionCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
  202.     {
  203.         if (e.OldValue is ObservableCollection<double> oldValue)
  204.         {
  205.             oldValue.CollectionChanged -= YCollectionChangedCallback;
  206.         }
  207.         if (e.NewValue is ObservableCollection<double> newValue)
  208.         {
  209.             newValue.CollectionChanged += YCollectionChangedCallback;
  210.         }
  211.         void YCollectionChangedCallback(object? sender, NotifyCollectionChangedEventArgs e)
  212.         {
  213.             if (d is CoordinateSystemControl control)
  214.             {
  215.                 if (control._isFirstLoaded)
  216.                 {
  217.                     control.Initialize();
  218.                 }
  219.             }
  220.         }
  221.     }
  222.     private static void XCollectionCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
  223.     {
  224.         if (e.OldValue is ObservableCollection<double> oldValue)
  225.         {
  226.             oldValue.CollectionChanged -= XCollectionChangedCallback;
  227.         }
  228.         if (e.NewValue is ObservableCollection<double> newValue)
  229.         {
  230.             newValue.CollectionChanged += XCollectionChangedCallback;
  231.         }
  232.         void XCollectionChangedCallback(object? sender, NotifyCollectionChangedEventArgs e)
  233.         {
  234.             if (d is CoordinateSystemControl control)
  235.             {
  236.                 if (control._isFirstLoaded)
  237.                 {
  238.                     control.Initialize();
  239.                 }
  240.             }
  241.         }
  242.     }
  243.     private static void X_YResultCollectionChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs de)
  244.     {
  245.         if (de.OldValue is ObservableDictionary<double, double> oldValue)
  246.         {
  247.             oldValue.CollectionChanged -= X_YResultCollectionChangedCallback;
  248.         }
  249.         if (de.NewValue is ObservableDictionary<double, double> newValue)
  250.         {
  251.             newValue.CollectionChanged += X_YResultCollectionChangedCallback;
  252.         }
  253.         void X_YResultCollectionChangedCallback(object? sender, NotifyCollectionChangedEventArgs e)
  254.         {
  255.             if (d is not CoordinateSystemControl control) return;
  256.             if (control.IsMouseCaptureWithin) return;
  257.             var oldValue = de.OldValue as ObservableDictionary<double, double>;
  258.             if (de.NewValue is not ObservableDictionary<double, double> newValue) return;
  259.             foreach (var item in newValue)
  260.             {
  261.                 double x = item.Key;
  262.                 var point = oldValue?.Where(n => n.Key == x).FirstOrDefault();
  263.                 if (point is null || point.Value.Value != item.Value)
  264.                 {
  265.                     if (!control._xValue_EllipseMap.TryGetValue(x, out var ellipse)) continue;
  266.                     double YUnit = control.CalculateYUnit(item.Value);
  267.                     control.SetSelectEllipsePosition(ellipse, control.GetActualCanvasTop(YUnit));
  268.                 }
  269.             }
  270.         }
  271.     }
  272.     private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
  273.     {
  274.         var textBox = (TextBox)sender;
  275.         if (!textBox.IsKeyboardFocused) return;
  276.         if (!double.TryParse(textBox.Text, out double value) || !ValidateNumber(value))
  277.         {
  278.             textBox.Background = Brushes.OrangeRed;
  279.             return;
  280.         }
  281.         textBox.Background = Brushes.Transparent;
  282.         double result = CalculateYUnit(value);
  283.         var selectEllipse = _selectEllipse_TextBoxMap.Single(n => n.Value.GetHashCode() == textBox.GetHashCode()).Key;
  284.         var canvasTop = GetActualCanvasTop(result);
  285.         SetSelectEllipsePosition(selectEllipse, canvasTop);
  286.         StoragePosition(selectEllipse);
  287.         bool ValidateNumber(double value)
  288.         {
  289.             return value >= 0 && value <= YCollection.Last();
  290.         }
  291.     }
  292.     private void SelectEllipse_Control_MouseUp(object sender, MouseButtonEventArgs e)
  293.     {
  294.         if (e.LeftButton == MouseButtonState.Released)
  295.         {
  296.             Mouse.Capture(null);
  297.             var element = (Ellipse)sender;
  298.             element.RenderTransform = _transform_chekEllipse;
  299.             element.Fill = SelectElementColor;
  300.             StoragePosition(element);
  301.             RaiseEventInvokeCommand();
  302.         }
  303.         e.Handled = true;
  304.     }
  305.     private void SelectEllipse_Control_MouseMove(object sender, MouseEventArgs e)
  306.     {
  307.         if (e.LeftButton == MouseButtonState.Pressed && Mouse.Captured == sender)
  308.         {
  309.             var ellipse = (Ellipse)sender;
  310.             var position = ValidataMouseYValue(e.GetPosition(_canvas).Y - 5);
  311.             SetSelectEllipsePosition(ellipse, position);
  312.         }
  313.         e.Handled = true;
  314.     }
  315.     private void SelectEllipse_MouseDown(object sender, MouseButtonEventArgs e)
  316.     {
  317.         if (e.LeftButton == MouseButtonState.Pressed)
  318.         {
  319.             Mouse.Capture((IInputElement)sender);
  320.             var element = (Ellipse)sender;
  321.             element.RenderTransform = _transform_clickMoveEllipse;
  322.             element.Fill = SelectedElementColor;
  323.         }
  324.         e.Handled = true;
  325.     }
  326.     #endregion
  327.     void Initialize()
  328.     {
  329.         YCollection ??= [1, 2, 3, 4, 5, 6, 7, 8, 9];
  330.         XCollection ??= [1, 2, 3, 4, 5, 6, 7, 8, 9];
  331.         //清理缓存数据
  332.         _selectEllipse_TextBoxMap.Clear();
  333.         _selectEllipse_ConnectionLines.Clear();
  334.         _xValue_EllipseMap.Clear();
  335.         if (_canvas.Children.Count > 0) _canvas.Children.RemoveRange(0, _canvas.Children.Count);
  336.         _originPoint = new Point(0, _canvas.ActualHeight);
  337.         _yEndpoint = new Point(_originPoint.X, _spacedPoints + 7);
  338.         _xEndpoint = new Point(_canvas.ActualWidth - _spacedPoints - X_AxisName.Length * 7, _originPoint.Y);
  339.         _ySingleDistance = (int)CalculateSingleDistance(Math.Abs(_originPoint.Y - _yEndpoint.Y), YCollection);
  340.         _xSingleDistance = (int)CalculateSingleDistance(Math.Abs(_originPoint.X - _xEndpoint.X), XCollection);
  341.         _proportionSize = _ySingleDistance / 7;
  342.         _yLine = new Line() { Stroke = CoordinateSystemColor, StrokeThickness = 2, X1 = _yEndpoint.X, Y1 = _yEndpoint.Y, X2 = _originPoint.X, Y2 = _originPoint.Y };
  343.         _xLine = new Line() { Stroke = CoordinateSystemColor, StrokeThickness = 2, X1 = _originPoint.X, Y1 = _originPoint.Y, X2 = _xEndpoint.X, Y2 = _xEndpoint.Y };
  344.         _canvas.Children.Add(_yLine);
  345.         _canvas.Children.Add(_xLine);
  346.         var y_AxisText = new TextBlock { Text = Y_AxisName };
  347.         var x_AxisText = new TextBlock { Text = X_AxisName };
  348.         _canvas.Children.Add(y_AxisText);
  349.         _canvas.Children.Add(x_AxisText);
  350.         Canvas.SetTop(x_AxisText, _yEndpoint.Y - _spacedPoints);
  351.         Canvas.SetLeft(x_AxisText, _yEndpoint.X + _spacedPoints);
  352.         Canvas.SetTop(y_AxisText, _xEndpoint.Y - _spacedPoints * 2);
  353.         Canvas.SetLeft(y_AxisText, _xEndpoint.X);
  354.         //加入坐标轴图标
  355.         AddAeeow();
  356.         //添加标注线和标注值和选择点
  357.         AddCalloutLine();
  358.     }
  359.     private void AddAeeow()
  360.     {
  361.         YAeeow ??= new Path()
  362.         {
  363.             Data = new PathGeometry()
  364.             {
  365.                 Figures = [new PathFigure(){
  366.                     IsClosed = true,
  367.                     StartPoint = new Point(0, 0),
  368.                     Segments = [new LineSegment(new Point(-_proportionSize, _proportionSize/2), true),
  369.                         new LineSegment(new Point(0, -_proportionSize), true),
  370.                         new LineSegment(new Point(_proportionSize, _proportionSize/2), true)]
  371.                     }]
  372.             },
  373.             Fill = CoordinateSystemColor
  374.         };
  375.         XAeeow ??= new Path()
  376.         {
  377.             Data = new PathGeometry()
  378.             {
  379.                 Figures = [new PathFigure() {
  380.                     IsClosed = true,
  381.                     StartPoint = new Point(0, 0),
  382.                     Segments = [new LineSegment(new Point(-_proportionSize/2, _proportionSize), true),
  383.                         new LineSegment(new Point(_proportionSize, 0), true),
  384.                         new LineSegment(new Point(-_proportionSize/2, -_proportionSize), true)]
  385.                     }]
  386.             },
  387.             Fill = CoordinateSystemColor
  388.         };
  389.         _canvas.Children.Add(YAeeow);
  390.         _canvas.Children.Add(XAeeow);
  391.         Canvas.SetTop(this.YAeeow, _yEndpoint.Y);
  392.         Canvas.SetLeft(this.YAeeow, _yEndpoint.X);
  393.         Canvas.SetTop(this.XAeeow, _xEndpoint.Y);
  394.         Canvas.SetLeft(this.XAeeow, _xEndpoint.X);
  395.     }
  396.     private void AddCalloutLine()
  397.     {
  398.         //添加Y轴标注线和标注值
  399.         AddYCalloutLines();
  400.         //添加X轴标注线和标注值和选择点
  401.         AddXCalloutLinesAndSelectEllipses();
  402.     }
  403.     private void AddYCalloutLines()
  404.     {
  405.         for (int i = 1; i < YCollection.Count + 1; i++)
  406.         {
  407.             var canvasTop = GetActualCanvasTop(i);
  408.             //添加标注线和标注值
  409.             var line = new Line() { Stroke = CalloutLineColor, StrokeThickness = 1, X1 = 0, Y1 = 0, X2 = _proportionSize, Y2 = 0 };
  410.             var text = new TextBlock() { Text = YCollection[i - 1].ToString(), RenderTransformOrigin = new(0.5, 0.5), RenderTransform = new TranslateTransform() { X = -12, Y = -8 } };
  411.             Canvas.SetLeft(line, _originPoint.X);
  412.             Canvas.SetTop(line, canvasTop);
  413.             Canvas.SetLeft(text, _originPoint.X);
  414.             Canvas.SetTop(text, canvasTop);
  415.             _canvas.Children.Add(line);
  416.             _canvas.Children.Add(text);
  417.         }
  418.     }
  419.     private void AddXCalloutLinesAndSelectEllipses()
  420.     {
  421.         for (int i = 1; i < XCollection.Count + 1; i++)
  422.         {
  423.             var currentValue = XCollection[i - 1];
  424.             var cancasLeft = GetActualCanvasLeft(i);
  425.             var line = new Line() { Stroke = CalloutLineColor, StrokeThickness = 1, X1 = 0, Y1 = 0, X2 = 0, Y2 = -_proportionSize };
  426.             var textBlock = new TextBlock() { Text = currentValue.ToString(), RenderTransformOrigin = new(0.5, 0.5), RenderTransform = new TranslateTransform() { X = -5, Y = 5 } };
  427.             Canvas.SetLeft(line, cancasLeft);
  428.             Canvas.SetTop(line, _originPoint.Y);
  429.             Canvas.SetLeft(textBlock, cancasLeft);
  430.             Canvas.SetTop(textBlock, _originPoint.Y);
  431.             _canvas.Children.Add(textBlock);
  432.             _canvas.Children.Add(line);
  433.             //添加选择点
  434.             var selectEllipse = new Ellipse
  435.             {
  436.                 Width = 14,
  437.                 Height = 14,
  438.                 Fill = SelectElementColor,
  439.                 RenderTransformOrigin = new Point(0.5, 0.5),
  440.                 RenderTransform = _transform_chekEllipse,
  441.             };
  442.             selectEllipse.AddHandler(MouseDownEvent, new MouseButtonEventHandler(SelectEllipse_MouseDown));
  443.             selectEllipse.AddHandler(MouseMoveEvent, new MouseEventHandler(SelectEllipse_Control_MouseMove));
  444.             selectEllipse.AddHandler(MouseUpEvent, new MouseButtonEventHandler(SelectEllipse_Control_MouseUp));
  445.             X_YResultCollection ??= new ObservableDictionary<double, double>();
  446.             bool excist = X_YResultCollection.Any(n => n.Key == currentValue);
  447.             string text = excist ? X_YResultCollection!.Single(n => n.Key == currentValue).Value.ToString("F2") : string.Empty;
  448.             var textBox = new TextBox() { Text = text, Background = Brushes.Transparent, BorderThickness = new(0), RenderTransform = _transform_textBox };
  449.             textBox.TextChanged += TextBox_TextChanged;
  450.             _canvas.Children.Add(selectEllipse);
  451.             _canvas.Children.Add(textBox);
  452.             _selectEllipse_TextBoxMap.Add(selectEllipse, textBox);
  453.             _xValue_EllipseMap.Add(currentValue, selectEllipse);
  454.             AddSelectEllipse(selectEllipse, i, currentValue);
  455.             AddConnectionLine(selectEllipse, i == XCollection.Count);
  456.         }
  457.     }
  458.     private void AddConnectionLine(Ellipse selectEllipse, bool isLast = false)
  459.     {
  460.         if (_selectEllipse_ConnectionLines.Count == 0)
  461.         {
  462.             _selectEllipse_ConnectionLines.AddFirst(selectEllipse);
  463.         }
  464.         else
  465.         {
  466.             var lastConnectionLine = (Line)_selectEllipse_ConnectionLines.Last!.Value;
  467.             lastConnectionLine.X2 = Canvas.GetLeft(selectEllipse);
  468.             lastConnectionLine.Y2 = Canvas.GetTop(selectEllipse);
  469.             _selectEllipse_ConnectionLines.AddLast(selectEllipse);
  470.         }
  471.         if (isLast) return;
  472.         var ConnectionLine = new Line() { StrokeThickness = 1, X1 = Canvas.GetLeft(selectEllipse), Y1 = Canvas.GetTop(selectEllipse), X2 = 0, Y2 = 0, Stroke = Brushes.LightGray };
  473.         _selectEllipse_ConnectionLines.AddLast(ConnectionLine);
  474.         _canvas.Children.Add(ConnectionLine);
  475.         //将选择点置于最上层
  476.         Panel.SetZIndex(selectEllipse, 1);
  477.     }
  478.     private void ChangeConnectionPosition(Ellipse ellipse, double canvasTop)
  479.     {
  480.         var linkedNode = _selectEllipse_ConnectionLines.Find(ellipse);
  481.         if (linkedNode?.Previous != null)
  482.         {
  483.             var previousLine = (Line)linkedNode.Previous.ValueRef;
  484.             previousLine.Y2 = canvasTop;
  485.         }
  486.         if (linkedNode?.Next != null)
  487.         {
  488.             var nextLine = (Line)linkedNode.Next.ValueRef;
  489.             nextLine.Y1 = canvasTop;
  490.         }
  491.     }
  492.     private double ValidataMouseYValue(double yPosition)
  493.     {
  494.         double MaxYValue = _originPoint.Y - _ySingleDistance * YCollection.Count;
  495.         if (yPosition < MaxYValue)
  496.         {
  497.             yPosition = MaxYValue;
  498.         }
  499.         if (yPosition > _originPoint.Y)
  500.         {
  501.             yPosition = _originPoint.Y;
  502.         }
  503.         return yPosition;
  504.     }
  505.     private void StoragePosition(Ellipse ellipse)
  506.     {
  507.         var xUnit = Math.Abs(_originPoint.X - Canvas.GetLeft(ellipse)) / _xSingleDistance;
  508.         var xValue = XCollection[(int)xUnit - 1];
  509.         var yUnit = (_originPoint.Y - Canvas.GetTop(ellipse)) / _ySingleDistance;
  510.         double yValue = CalculateYValue(yUnit);
  511.         X_YResultCollection[xValue] = Math.Round(yValue, 2);
  512.     }
  513.     private double CalculateYValue(double yunit)
  514.     {
  515.         var yArray = YCollection.ToArray();
  516.         bool minFlag = yunit >= 1;
  517.         if (yunit >= yArray.Length) return yArray.Last();
  518.         int lastValueIndex = (int)yunit - 1;
  519.         double lastValue = minFlag ? yArray[lastValueIndex] : 0;
  520.         double nextValue = yArray[lastValueIndex + 1];
  521.         return lastValue + (yunit - lastValueIndex - 1) * (nextValue - lastValue);
  522.     }
  523.     private double CalculateYUnit(double yValue)
  524.     {
  525.         double lastValue = 0;
  526.         int i = -1;
  527.         foreach (var item in YCollection)
  528.         {
  529.             i++;
  530.             if (item < yValue)
  531.             {
  532.                 lastValue = item;
  533.                 continue;
  534.             }
  535.             return i + (yValue - lastValue) / (item - lastValue);
  536.         }
  537.         return 0;
  538.     }
  539.     private void AddSelectEllipse(Ellipse ellipse, double xUnit, double xValue)
  540.     {
  541.         if (X_YResultCollection.Where(n => n.Key == xValue).Any())
  542.         {
  543.             var yValue = X_YResultCollection.Single(n => n.Key == xValue).Value;
  544.             double yUnit = CalculateYUnit(yValue);
  545.             SetSelectEllipsePosition(ellipse, GetActualCanvasTop(yUnit), GetActualCanvasLeft(xUnit));
  546.         }
  547.         else
  548.         {
  549.             X_YResultCollection.Add(xValue, 1);
  550.             AddSelectEllipse(ellipse, xUnit, xValue);
  551.         }
  552.     }
  553.     private void SetSelectEllipsePosition(Ellipse ellipse, double top, double? left = null)
  554.     {
  555.         Canvas.SetTop(ellipse, top);
  556.         var textBox = _selectEllipse_TextBoxMap[ellipse];
  557.         Canvas.SetTop(textBox, top);
  558.         //改变对应textblock值
  559.         var yUnit = (_originPoint.Y - top) / _ySingleDistance;
  560.         double yValue = CalculateYValue(yUnit);
  561.         textBox.Text = yValue.ToString("F2");
  562.         //改变连接线位置
  563.         ChangeConnectionPosition(ellipse, top);
  564.         if (left.HasValue)
  565.         {
  566.             Canvas.SetLeft(ellipse, (double)left);
  567.             Canvas.SetLeft(textBox, (double)left);
  568.         }
  569.     }
  570.     private double CalculateSingleDistance(double totalDistance, IEnumerable<double> values)
  571.     {
  572.         return (totalDistance - _spacedPoints) / values.Count();
  573.     }
  574.     private void Canvas_SizeChanged(object sender, SizeChangedEventArgs e)
  575.     {
  576.         if (e.PreviousSize.Width != 0 && e.PreviousSize.Height != 0)
  577.         {
  578.             Initialize();
  579.         }
  580.         e.Handled = true;
  581.     }
  582.     private double GetActualCanvasLeft(double unit) => _originPoint.X + _xSingleDistance * unit;
  583.     private double GetActualCanvasTop(double unit) => _originPoint.Y - _ySingleDistance * unit;
  584.     private void RaiseEventInvokeCommand()
  585.     {
  586.         ResultCollectionChangedEventArgs args = new(ResultCollectionChangedEvent, this, X_YResultCollection);
  587.         RaiseEvent(args);
  588.         if (Command?.CanExecute(null) ?? true)
  589.         {
  590.             Command?.Execute(X_YResultCollection);
  591.         }
  592.     }
  593.     public static double CalculateYValue(Dictionary<double, double> dic, double xValue)
  594.     {
  595.         double lastX = 0;
  596.         double lastY = 0;
  597.         foreach (var point in dic)
  598.         {
  599.             if (xValue > point.Key)
  600.             {
  601.                 lastX = point.Key;
  602.                 lastY = point.Value;
  603.                 continue;
  604.             }
  605.             else if (xValue == point.Key)
  606.             {
  607.                 return point.Value;
  608.             }
  609.             return (point.Value - lastY) / (point.Key - lastX) * (xValue - lastX) / (point.Key - lastX) + lastY;
  610.         }
  611.         return dic[dic.Max(n => n.Key)];
  612.     }
  613. }
复制代码
Demo

在拖动完选择点后会触发命令和抛出路由事件

  • View
  1. <Grid>
  2.     <Grid.RowDefinitions>
  3.         <RowDefinition Height="400" />
  4.         <RowDefinition Height="*" />
  5.     </Grid.RowDefinitions>
  6.     <local:CoordinateSystemControl
  7.         Command="{Binding TestCommand}"
  8.         X_AxisName="X"
  9.         XCollection="{Binding XCollection}"
  10.         YCollection="{Binding YCollection}"
  11.         X_YResultCollection="{Binding Datas}"
  12.         Y_AxisName="Y" />
  13.     <Button
  14.         Grid.Row="1"
  15.         Width="40"
  16.         Height="20"
  17.         Command="{Binding ClickCommand}"
  18.         Content="click me" />
  19. </Grid>
复制代码

  • ViewModel
  1.   public ObservableCollection<double> XCollection { get; set; } = [1, 3, 5, 7, 9];
  2.   public ObservableCollection<double> YCollection { get; set; } = [1, 3, 5, 7, 9];
  3.   public ObservableDictionary<double, double> Datas { get; set; } = [];
  4. //构造函数
  5.   public ViewModel()
  6.   {
  7.       Datas.Add(1, 1);
  8.       Datas.Add(3, 1);
  9.       Datas.Add(5, 1);
  10.       Datas.Add(7, 1);
  11.       Datas.Add(9, 1);
  12.   }
  13.   [RelayCommand]
  14.   public async Task Test(IDictionary<double, double> data)
  15.   {
  16.   }
  17.   [RelayCommand]
  18.   private void Click()
  19.   {
  20.       Datas[1.0] = 5;
  21.   }
复制代码
注意

ObservableDictionary可查看[https://www.cnblogs.com/yinyuessh/p/18205333]
希望能给你带来帮助 --来自.net菜鸡粉丝的祝愿

来源:https://www.cnblogs.com/yinyuessh/p/18205346
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

举报 回复 使用道具