使用实例一则 ScottPlot.WPF 5.1.58

  1 using System.Windows.Controls;
  2 using System.Xml.Linq;
  3 using ScottPlot;
  4 using ScottPlot.TickGenerators;
  5 using ScottPlot.WPF;
  6 using Color = ScottPlot.Color;
  7 using Colors = ScottPlot.Colors;
  8 
  9 namespace RootExudation.Views
 10 {
 11     public partial class ScottPlotControl : UserControl
 12     {
 13         // 存储每条曲线的数据(用 List 存储 X、Y)
 14         private Dictionary<int, List<double>> _xsData = new();
 15         private Dictionary<int, List<double>> _ysData = new();
 16         private Dictionary<int, Color> _colors = new();
 17         private Dictionary<int, string> _names = new();
 18         private Dictionary<int, bool> _visibleFlags = new();
 19         private ToolTip _hoverToolTip;
 20 
 21         public ScottPlotControl()
 22         {
 23             InitializeComponent();
 24 
 25             Loaded += (s, e) =>
 26             {
 27                 InitScott();
 28                 WpfPlot.Refresh();
 29             };
 30         }
 31 
 32         /// <summary>
 33         /// 初始化曲线(在添加第一个点前调用)
 34         /// </summary>
 35         public void InitializeTube(int tubeId, Color color, string name)
 36         {
 37             if (!_xsData.ContainsKey(tubeId))
 38             {
 39                 _xsData[tubeId] = new List<double>();
 40                 _ysData[tubeId] = new List<double>();
 41                 _colors[tubeId] = color;
 42                 _names[tubeId] = name;
 43                 _visibleFlags[tubeId] = true;
 44             }
 45         }
 46 
 47         /// <summary>
 48         /// 设置曲线显隐
 49         /// </summary>
 50         public void SetTubeVisibility(int tubeId, bool isVisible)
 51         {
 52             if (_visibleFlags.ContainsKey(tubeId))
 53             {
 54                 _visibleFlags[tubeId] = isVisible;
 55                 RedrawAllCurves();  // 重新绘制,只画可见的曲线
 56             }
 57         }
 58 
 59         /// <summary>
 60         /// 添加或更新曲线
 61         /// </summary>
 62         public void UpdatePlot(int tubeId, double yValue, DateTime time, string name, int maxCount)
 63         {
 64             var x = time.ToOADate();
 65 
 66             if (!_xsData.ContainsKey(tubeId))
 67             {
 68                 _xsData[tubeId] = new List<double>();
 69                 _ysData[tubeId] = new List<double>();
 70                 _colors[tubeId] = GetColorForTube(tubeId);
 71                 _names[tubeId] = name;
 72                 _visibleFlags[tubeId] = true;
 73             }
 74 
 75             _xsData[tubeId].Add(x);
 76             _ysData[tubeId].Add(yValue);
 77 
 78             // 限制点数(防止内存无限增长)
 79             if (_xsData[tubeId].Count > maxCount)
 80             {
 81                 _xsData[tubeId].RemoveAt(0);
 82                 _ysData[tubeId].RemoveAt(0);
 83             }
 84 
 85             // 重新绘制所有曲线(只画可见的)
 86             RedrawAllCurves();
 87         }
 88 
 89         /// <summary>
 90         /// 重新绘制所有曲线
 91         /// </summary>
 92         private void RedrawAllCurves()
 93         {
 94             WpfPlot.Plot.Clear();
 95             InitScott();
 96 
 97             foreach (var id in _xsData.Keys)
 98             {
 99                 var xs = _xsData[id].ToArray();
100                 var ys = _ysData[id].ToArray();
101                 if (xs.Length > 0 && ys.Length > 0)
102                 {
103                     var scatter = WpfPlot.Plot.Add.Scatter(xs, ys);
104                     scatter.Color = _colors[id];
105                     scatter.LineWidth = 2;
106                     scatter.MarkerSize = 3;
107                     scatter.MarkerShape = MarkerShape.OpenCircleWithDot;
108                     scatter.Smooth = true;
109                     scatter.IsVisible = _visibleFlags[id];
110                 }
111             }
112 
113             WpfPlot.Plot.Axes.AutoScale();
114             WpfPlot.Refresh();
115         }
116 
117         private Color GetColorForTube(int id)
118         {
119             var colors = new[]
120             {
121                 Colors.Red, Colors.Blue, Colors.Green, Colors.Orange,
122                 Colors.Purple, Colors.Teal, Colors.Brown, Colors.Pink
123             };
124             return id <= colors.Length ? colors[id - 1] : Colors.Gray;
125         }
126 
127         public void ClearPlots()
128         {
129             _xsData.Clear();
130             _ysData.Clear();
131             InitScott();
132             WpfPlot.Refresh();
133         }
134 
135         public void RefreshPlot()
136         {
137             WpfPlot.Refresh();
138         }
139 
140         private void InitScott()
141         {
142             WpfPlot.Plot.Axes.Left.Label.FontName = "Microsoft YaHei UI";
143             WpfPlot.Plot.Axes.Bottom.Label.FontName = "Microsoft YaHei UI";
144             WpfPlot.Plot.YLabel("Temperature (\u2103)");
145             WpfPlot.Plot.XLabel("Time");
146             //图例效果不好,外部自定义一个
147             //WpfPlot.Plot.ShowLegend();
148             //WpfPlot.Plot.Legend.IsVisible = true;
149             //WpfPlot.Plot.Legend.Alignment = Alignment.LowerRight;
150             //WpfPlot.Plot.Layout.Fixed(new PixelPadding(10, 10, 10, 10));
151             WpfPlot.Plot.Axes.DateTimeTicksBottom();
152             var bottomAxis = WpfPlot.Plot.Axes.Bottom;
153             bottomAxis.TickGenerator = new DateTimeAutomatic
154             {
155                 LabelFormatter = dt => dt.ToString("HH:mm:ss")
156             };
157             WpfPlot.Plot.Axes.Hairline(true);
158         }
159 
160         private void WpfPlot_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
161         {
162             var postion = e.GetPosition(WpfPlot);
163             var coordinate = WpfPlot.Plot.GetCoordinates((float)postion.X, (float)postion.Y);
164 
165             var nearestPoint = FindNearestPoint(coordinate.X, coordinate.Y);
166 
167             if (nearestPoint.HasValue)
168             {
169                 // 格式化 ToolTip 文本
170                 var tooltipText = FormatTooltip(nearestPoint.Value.curveId, nearestPoint.Value.x, nearestPoint.Value.y);
171 
172                 // 显示或更新 ToolTip
173                 if (_hoverToolTip == null)
174                 {
175                     _hoverToolTip = new ToolTip();
176                     ToolTipService.SetToolTip(WpfPlot, _hoverToolTip);
177                 }
178 
179                 _hoverToolTip.Content = tooltipText;
180                 _hoverToolTip.IsOpen = true;
181             }
182             else
183             {
184                 // 找不到点时,关闭 ToolTip
185                 if (_hoverToolTip != null)
186                 {
187                     _hoverToolTip.IsOpen = false;
188                 }
189             }
190         }
191 
192         /// <summary>
193         /// 格式化要显示的 ToolTip 内容
194         /// </summary>
195         private string FormatTooltip(int curveId, double x, double y)
196         {
197             // 将 X 轴的双精度值转换为时间
198             DateTime time = DateTime.FromOADate(x);
199             string timeStr = time.ToString("HH:mm:ss");
200             string curveName = _names.GetValueOrDefault(curveId, $"曲线 {curveId}");
201 
202             return $"{curveName}-{timeStr} : {y:F2} (℃)";
203         }
204 
205         /// <summary>
206         /// 在所有曲线中查找距离鼠标最近的散点
207         /// </summary>
208         private (int curveId, int pointIndex, double x, double y, double distance)? FindNearestPoint(double mouseX, double mouseY)
209         {
210             var nearest = new
211             {
212                 CurveId = -1,
213                 PointIndex = -1,
214                 X = 0.0,
215                 Y = 0.0,
216                 Distance = double.MaxValue
217             };
218 
219             bool found = false;
220 
221             // 确定选取的阈值(像素)
222             var coordination = WpfPlot.Plot.GetCoordinateRect(10f, 10f);
223             double xThreshold = coordination.Width / 2;
224             double yThreshold = coordination.Height / 2;
225 
226             foreach (var kv in _xsData)
227             {
228                 int curveId = kv.Key;
229                 var xs = kv.Value;
230                 var ys = _ysData[curveId];
231 
232                 // 只搜索可见曲线上的点
233                 if (!_visibleFlags.TryGetValue(curveId, out var isVis) || !isVis) continue;
234 
235                 for (int i = 0; i < xs.Count; i++)
236                 {
237                     double dx = xs[i] - mouseX;
238                     double dy = ys[i] - mouseY;
239 
240                     // 检查坐标差是否在阈值范围内
241                     if (Math.Abs(dx) <= xThreshold && Math.Abs(dy) <= yThreshold)
242                     {
243                         double distance = Math.Sqrt(dx * dx + dy * dy);
244                         if (distance < nearest.Distance)
245                         {
246                             nearest = new
247                             {
248                                 CurveId = curveId,
249                                 PointIndex = i,
250                                 X = xs[i],
251                                 Y = ys[i],
252                                 Distance = distance
253                             };
254                             found = true;
255                         }
256                     }
257                 }
258             }
259 
260             if (found)
261                 return (nearest.CurveId, nearest.PointIndex, nearest.X, nearest.Y, nearest.Distance);
262 
263             return null;
264         }
265     }
266 }

外部可拖拽可显隐控制图例控件

<!-- 可拖拽的面板容器 -->
<Border x:Name="DraggablePanel" CornerRadius="5" Grid.Row="1" Grid.ColumnSpan="3" Style="{StaticResource DraggableBorderStyle}" MouseLeftButtonDown="DraggablePanel_MouseLeftButtonDown" MouseLeftButtonUp="DraggablePanel_MouseLeftButtonUp" MouseMove="DraggablePanel_MouseMove" Width="Auto" Height="Auto" Visibility="Visible">
    <!--半透明背景,拖拽时更明显-->
    <Border.Background>
        <SolidColorBrush Color="{DynamicResource RegionColor}" Opacity="0.8"/>
    </Border.Background>
    <Border.Effect>
        <DropShadowEffect ShadowDepth="2" BlurRadius="5" Opacity="0.5"/>
    </Border.Effect>
    <ItemsControl ItemsSource="{Binding Tubes}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal" Margin="5">
                    <CheckBox IsChecked="{Binding Show, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Content="{Binding Name}" Foreground="{Binding Color}" FontSize="{StaticResource Size20}" VerticalAlignment="Center"/>
                    <TextBlock Style="{StaticResource TextBlockTitle}" Foreground="{Binding Color}" VerticalAlignment="Center">
                        : <Run Text="{Binding CurrentTemperature, StringFormat={}{0:F2}, UpdateSourceTrigger=PropertyChanged}"/></TextBlock>
                </StackPanel>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Border>

拖拽与自动吸附角落,后台

  1         private void SetPanelInitialPosition()
  2         {
  3             // 确保面板有实际大小
  4             DraggablePanel.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
  5             DraggablePanel.Arrange(new Rect(0, 0, DraggablePanel.DesiredSize.Width, DraggablePanel.DesiredSize.Height));
  6 
  7             var chartContainer = VisualTreeHelper.GetParent(ScottPlotControl) as FrameworkElement;
  8             if (chartContainer != null)
  9             {
 10                 double rightMargin = 20;
 11                 double bottomMargin = 20;
 12 
 13                 double rightPosition = chartContainer.ActualWidth - DraggablePanel.ActualWidth - rightMargin;
 14                 double bottomPosition = chartContainer.ActualHeight - DraggablePanel.ActualHeight - bottomMargin;
 15 
 16                 dragTransform.X = rightPosition;
 17                 dragTransform.Y = bottomPosition;
 18             }
 19 
 20             DraggablePanel.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
 21             DraggablePanel.VerticalAlignment = System.Windows.VerticalAlignment.Top;
 22             DraggablePanel.Margin = new Thickness(0);
 23         }
 24 
 25         private void DraggablePanel_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
 26         {
 27             isDragging = true;
 28             dragStartPoint = e.GetPosition(this);
 29             transformStartPoint = new Point(dragTransform.X, dragTransform.Y);
 30             DraggablePanel.CaptureMouse();
 31             e.Handled = true;
 32         }
 33 
 34         private void DraggablePanel_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
 35         {
 36             if (isDragging)
 37             {
 38                 isDragging = false;
 39                 DraggablePanel.ReleaseMouseCapture();
 40                 e.Handled = true;
 41                 SnapToCorner();
 42             }
 43         }
 44 
 45         private void SnapToCorner()
 46         {
 47             var chartContainer = VisualTreeHelper.GetParent(ScottPlotControl) as FrameworkElement;
 48             if (chartContainer == null) return;
 49 
 50             double margin = 20;
 51             double width = DraggablePanel.ActualWidth;
 52             double height = DraggablePanel.ActualHeight;
 53             double containerWidth = chartContainer.ActualWidth;
 54             double containerHeight = chartContainer.ActualHeight;
 55 
 56             double distanceToTopLeft = Math.Sqrt(Math.Pow(dragTransform.X, 2) + Math.Pow(dragTransform.Y, 2));
 57             double distanceToTopRight = Math.Sqrt(Math.Pow(containerWidth - dragTransform.X - width, 2) + Math.Pow(dragTransform.Y, 2));
 58             double distanceToBottomLeft = Math.Sqrt(Math.Pow(dragTransform.X, 2) + Math.Pow(containerHeight - dragTransform.Y - height, 2));
 59             double distanceToBottomRight = Math.Sqrt(Math.Pow(containerWidth - dragTransform.X - width, 2) + Math.Pow(containerHeight - dragTransform.Y - height, 2));
 60 
 61             var min = new[] { distanceToTopLeft, distanceToTopRight, distanceToBottomLeft, distanceToBottomRight }.Min();
 62 
 63             if (min < 100)
 64             {
 65                 if (min == distanceToTopLeft)
 66                 {
 67                     dragTransform.X = margin; dragTransform.Y = margin;
 68                 }
 69                 else if (min == distanceToTopRight)
 70                 {
 71                     dragTransform.X = containerWidth - width - margin; dragTransform.Y = margin;
 72                 }
 73                 else if (min == distanceToBottomLeft)
 74                 {
 75                     dragTransform.X = margin; dragTransform.Y = containerHeight - height - margin;
 76                 }
 77                 else
 78                 {
 79                     dragTransform.X = containerWidth - width - margin; dragTransform.Y = containerHeight - height - margin;
 80                 }
 81             }
 82         }
 83 
 84         private void DraggablePanel_MouseMove(object sender, MouseEventArgs e)
 85         {
 86             if (!isDragging || e.LeftButton != MouseButtonState.Pressed) return;
 87 
 88             var currentPoint = e.GetPosition(this);
 89             double deltaX = currentPoint.X - dragStartPoint.X;
 90             double deltaY = currentPoint.Y - dragStartPoint.Y;
 91 
 92             double newX = transformStartPoint.X + deltaX;
 93             double newY = transformStartPoint.Y + deltaY;
 94 
 95             var chartContainer = VisualTreeHelper.GetParent(ScottPlotControl) as FrameworkElement;
 96             if (chartContainer != null)
 97             {
 98                 if (newX < 0) newX = 0;
 99                 if (newY < 0) newY = 0;
100                 if (newX + DraggablePanel.ActualWidth > chartContainer.ActualWidth)
101                     newX = chartContainer.ActualWidth - DraggablePanel.ActualWidth;
102                 if (newY + DraggablePanel.ActualHeight > chartContainer.ActualHeight)
103                     newY = chartContainer.ActualHeight - DraggablePanel.ActualHeight;
104             }
105 
106             dragTransform.X = newX;
107             dragTransform.Y = newY;
108         }

 

posted @ 2026-05-12 16:38  dyfisgod  阅读(80)  评论(0)    收藏  举报