o0虾米0o 2025-11-06 13:51 采纳率: 50%
浏览 5

C# WPF scottplot 游标问题

在我的WPF项目中有个scotplot控件,控件版本V5.1.17,现在出现鼠标放置在游标线上不能变成移动光标,而是需要离开游标线才能变成移动光标

img

img



        // 创建游标
        private void CreateCursors(ScottPlot.Plot plt)
        {
            if (plt == null) return;

            // 移除已存在的游标
            RemoveCursors(plt);

            // 获取X轴范围来设置初始位置
            var xRange = plt.Axes.Bottom.Range;
            double xMin = xRange.Min;
            double xMax = xRange.Max;
            double xSpan = xMax - xMin;

            // 确保有有效的X轴范围
            if (xSpan <= 0)
            {
                xMin = 0;
                xMax = 1;
                xSpan = 1;
            }

            // 设置初始位置:A在30%位置,B在70%位置,确保最小间距
            double minSpacing = 0.1 * xSpan; // 最小间距为X轴范围的10%
            double posA = xMin + 0.3 * xSpan;
            double posB = xMin + 0.7 * xSpan;

            // 确保游标之间有足够的间距
            if (posB - posA < minSpacing)
            {
                posA = xMin + 0.4 * xSpan;
                posB = xMin + 0.6 * xSpan;
            }

            // 创建游标A(红色)
            _cursorA = plt.Add.VerticalLine(posA);
            _cursorA.IsDraggable = true;
            _cursorA.Color = new ScottPlot.Color(255, 0, 0); // 红色
            _cursorA.LineWidth = 2;
            _cursorA.Label.Text = "A";
            _cursorA.Label.BackgroundColor = new ScottPlot.Color(255, 0, 0);
            _cursorA.Label.ForeColor = ScottPlot.Colors.White;
            _cursorA.Label.FontSize = 12;
            _cursorA.Label.Padding = 8;
            _cursorA.Label.BorderColor = ScottPlot.Colors.White;
            _cursorA.Label.BorderWidth = 1;
            _cursorA.Label.OffsetX = 0;
            _cursorA.Label.OffsetY = -30f; // 使用 float 类型
           
            // 创建游标B(蓝色)
            _cursorB = plt.Add.VerticalLine(posB);
            _cursorB.IsDraggable = true;
            _cursorB.Color = new ScottPlot.Color(0, 0, 255); // 蓝色
            _cursorB.LineWidth = 2;
            _cursorB.Text = "B";
            _cursorB.Label.BackgroundColor = new ScottPlot.Color(0, 0, 255);
            _cursorB.Label.ForeColor = ScottPlot.Colors.White;
            _cursorB.Label.FontSize = 12;
            _cursorB.Label.Padding = 8;
            _cursorB.Label.BorderColor = ScottPlot.Colors.White;
            _cursorB.Label.BorderWidth = 1;
            _cursorB.Label.OffsetX = 0;
            _cursorB.Label.OffsetY = -30f; // 使用 float 类型

            UpdateCursorPositions();

            // 设置鼠标事件
             SetupCursorMouseEvents(plt);
        }
        // 更新游标标签位置,使其始终保持在图表上边沿
        private void UpdateCursorLabelPositions(ScottPlot.Plot plt)
        {
            if (_cursorA == null || _cursorB == null) return;

            try
            {
                // 获取图表的数据区域
                var dataArea = plt.RenderManager.LastRender.DataRect;

                // 计算标签应该放置的位置(在图表顶部)
                // 使用固定像素偏移,确保标签始终在图表上边沿
                float labelOffsetY = -35f; // 使用 float 类型

                // 更新游标A的标签位置
                _cursorA.Label.OffsetY = labelOffsetY;

                // 更新游标B的标签位置
                _cursorB.Label.OffsetY = labelOffsetY;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"Error updating cursor label positions: {ex.Message}");
            }
        }
        // 移除游标
        private void RemoveCursors(ScottPlot.Plot plt)
        {
            if (plt == null) return;

            // 移除鼠标事件处理
            RemoveCursorMouseEvents();

            if (_cursorA != null)
            {
                plt.Remove(_cursorA);
                _cursorA = null;
            }

            if (_cursorB != null)
            {
                plt.Remove(_cursorB);
                _cursorB = null;
            }
        }
        // 更新游标的外观,反映选中状态
        private void UpdateCursorAppearance()
        {
            if (_cursorA == null || _cursorB == null) return;

            // 保持游标A的默认外观
            _cursorA.LineWidth = 2f; // 使用 float 类型
            _cursorA.Label.Text = "A";
            _cursorA.Label.BackgroundColor = new ScottPlot.Color(255, 0, 0);

            // 保持游标B的默认外观
            _cursorB.LineWidth = 2f; // 使用 float 类型
            _cursorB.Label.Text = "B";
            _cursorB.Label.BackgroundColor = new ScottPlot.Color(0, 0, 255);

            // 刷新显示
            if (this.FindName("WpfPlot") is ScottPlot.WPF.WpfPlot sp)
            {
                sp.Refresh();
            }
        }
        // 设置游标鼠标事件
        private void SetupCursorMouseEvents(ScottPlot.Plot plt)
        {
           

            
            if (plt == null) return;

            // 为WPF控件添加鼠标事件处理
            // 使用FindName方法获取WpfPlot控件
            var wpfPlot = this.FindName("WpfPlot") as ScottPlot.WPF.WpfPlot;
            if (wpfPlot == null) return;

            wpfPlot.MouseDown += OnWpfPlotMouseDown;
            wpfPlot.MouseMove += OnWpfPlotMouseMove;
            wpfPlot.MouseUp += OnWpfPlotMouseUp;
            wpfPlot.MouseLeave += OnWpfPlotMouseLeave;
            
        }

        // 移除游标鼠标事件
        private void RemoveCursorMouseEvents()
        {
            // 使用FindName方法获取WpfPlot控件
            var wpfPlot = this.FindName("WpfPlot") as ScottPlot.WPF.WpfPlot;
            if (wpfPlot == null) return;

            wpfPlot.MouseDown -= OnWpfPlotMouseDown;
            wpfPlot.MouseMove -= OnWpfPlotMouseMove;
            wpfPlot.MouseUp -= OnWpfPlotMouseUp;
            wpfPlot.MouseLeave -= OnWpfPlotMouseLeave;
        }

        // 鼠标按下事件处理
        private void OnWpfPlotMouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {

            if (_cursorA == null || _cursorB == null) return;

            var wpfPlot = sender as ScottPlot.WPF.WpfPlot;
            if (wpfPlot == null) return;

            // 获取鼠标在控件内的像素坐标
            var mousePoint = e.GetPosition(wpfPlot);
            var mousePixel = new ScottPlot.Pixel(mousePoint.X, mousePoint.Y);

            try
            {
                // 使用更精确的坐标转换方法
                var mouseData = wpfPlot.Plot.GetCoordinates(mousePixel, wpfPlot.Plot.Axes.Bottom, wpfPlot.Plot.Axes.Left);

                // 获取图表渲染信息以更精确计算
                var renderInfo = wpfPlot.Plot.RenderManager.LastRender;

                // 检查渲染信息是否有效(不使用 == null,因为它是结构体)
                if (renderInfo.DataRect.Width <= 0 || renderInfo.DataRect.Height <= 0)
                    return;

                // 计算数据坐标到像素坐标的转换比例
                double dataWidth = wpfPlot.Plot.Axes.Bottom.Range.Span;
                double pixelWidth = renderInfo.DataRect.Width;
                double pixelsPerDataUnit = pixelWidth / dataWidth;

                // 使用更小的像素容差(4像素),但提高检测精度
                double pixelTolerance = 4.0;

                // 将游标位置转换为像素坐标进行比较
                double cursorAPixelX = renderInfo.DataRect.Left + (_cursorA.Position - wpfPlot.Plot.Axes.Bottom.Range.Min) * pixelsPerDataUnit;
                double cursorBPixelX = renderInfo.DataRect.Left + (_cursorB.Position - wpfPlot.Plot.Axes.Bottom.Range.Min) * pixelsPerDataUnit;

                double mouseX = mousePixel.X;

                // 检查是否点击了游标A(使用像素坐标比较)
                if (Math.Abs(mouseX - cursorAPixelX) <= pixelTolerance)
                {
                    _isDraggingCursorA = true;
                    _cursorDragStartX = mouseData.X;
                    wpfPlot.CaptureMouse();
                    System.Diagnostics.Debug.WriteLine($"Started dragging Cursor A - Pixel distance: {Math.Abs(mouseX - cursorAPixelX)}");
                    return;
                }

                // 检查是否点击了游标B(使用像素坐标比较)
                if (Math.Abs(mouseX - cursorBPixelX) <= pixelTolerance)
                {
                    _isDraggingCursorB = true;
                    _cursorDragStartX = mouseData.X;
                    wpfPlot.CaptureMouse();
                    System.Diagnostics.Debug.WriteLine($"Started dragging Cursor B - Pixel distance: {Math.Abs(mouseX - cursorBPixelX)}");
                    return;
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"Error in mouse down: {ex.Message}");
            }

            // 如果点击了其他地方,释放鼠标捕获
            wpfPlot.ReleaseMouseCapture();
        }


        // 鼠标移动事件处理
        private void OnWpfPlotMouseMove(object sender, System.Windows.Input.MouseEventArgs e)
        {
            if (_cursorA == null || _cursorB == null) return;

            var wpfPlot = sender as ScottPlot.WPF.WpfPlot;
            if (wpfPlot == null) return;

            var mousePoint = e.GetPosition(wpfPlot);
            var mousePixel = new ScottPlot.Pixel(mousePoint.X, mousePoint.Y);

            try
            {
                // 获取图表渲染信息
                var renderInfo = wpfPlot.Plot.RenderManager.LastRender;

                // 检查渲染信息是否有效
                if (renderInfo.DataRect.Width <= 0 || renderInfo.DataRect.Height <= 0)
                    return;

                // 计算数据坐标到像素坐标的转换比例
                double dataWidth = wpfPlot.Plot.Axes.Bottom.Range.Span;
                double pixelWidth = renderInfo.DataRect.Width;
                double pixelsPerDataUnit = pixelWidth / dataWidth;

                // 使用像素坐标进行更精确的悬停检测
                double pixelTolerance = 6.0; // 稍微增大容差以改善用户体验

                // 将游标位置转换为像素坐标
                double cursorAPixelX = renderInfo.DataRect.Left + (_cursorA.Position - wpfPlot.Plot.Axes.Bottom.Range.Min) * pixelsPerDataUnit;
                double cursorBPixelX = renderInfo.DataRect.Left + (_cursorB.Position - wpfPlot.Plot.Axes.Bottom.Range.Min) * pixelsPerDataUnit;

                double mouseX = mousePixel.X;

                bool isNearCursorA = Math.Abs(mouseX - cursorAPixelX) <= pixelTolerance;
                bool isNearCursorB = Math.Abs(mouseX - cursorBPixelX) <= pixelTolerance;

                // 设置鼠标光标样式
                if (isNearCursorA || isNearCursorB)
                {
                    wpfPlot.Cursor = System.Windows.Input.Cursors.SizeWE;
                    System.Diagnostics.Debug.WriteLine($"Cursor near line - A: {isNearCursorA}, B: {isNearCursorB}, Distance A: {Math.Abs(mouseX - cursorAPixelX)}, Distance B: {Math.Abs(mouseX - cursorBPixelX)}");
                }
                else
                {
                    wpfPlot.Cursor = System.Windows.Input.Cursors.Arrow;
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"Error in mouse move: {ex.Message}");
            }

            
            try
            {
                var mouseData = wpfPlot.Plot.GetCoordinates(mousePixel, wpfPlot.Plot.Axes.Bottom, wpfPlot.Plot.Axes.Left);

                // 获取X轴范围用于边界检查和间距计算
                var xRange = wpfPlot.Plot.Axes.Bottom.Range;
                double xSpan = xRange.Span;

                // 固定间距模式:最小间距为X轴范围的1%
                double minSpacing = 0.01 * xSpan;

                // 拖动游标A
                if (_isDraggingCursorA)
                {
                    double newPosition = mouseData.X;

                    // 边界检查:限制在X轴范围内
                    newPosition = Math.Max(xRange.Min, Math.Min(xRange.Max, newPosition));

                    // 固定间距模式:防止游标A和B重叠
                    if (_cursorB != null)
                    {
                        // 如果A试图越过B,保持最小间距
                        if (newPosition > _cursorB.Position - minSpacing)
                        {
                            newPosition = _cursorB.Position - minSpacing;
                        }

                        // 确保A在B的左侧
                        if (newPosition >= _cursorB.Position)
                        {
                            newPosition = _cursorB.Position - minSpacing;
                        }
                    }

                    _cursorA.Position = newPosition;
                    UpdateCursorPositions();

                    // 更新标签位置
                    UpdateCursorLabelPositions(wpfPlot.Plot);

                    wpfPlot.Refresh();
                    return;
                }

                // 拖动游标B
                if (_isDraggingCursorB)
                {
                    double newPosition = mouseData.X;

                    // 边界检查:限制在X轴范围内
                    newPosition = Math.Max(xRange.Min, Math.Min(xRange.Max, newPosition));

                    // 固定间距模式:防止游标A和B重叠
                    if (_cursorA != null)
                    {
                        // 如果B试图越过A,保持最小间距
                        if (newPosition < _cursorA.Position + minSpacing)
                        {
                            newPosition = _cursorA.Position + minSpacing;
                        }

                        // 确保B在A的右侧
                        if (newPosition <= _cursorA.Position)
                        {
                            newPosition = _cursorA.Position + minSpacing;
                        }
                    }

                    _cursorB.Position = newPosition;
                    UpdateCursorPositions();

                    // 更新标签位置
                    UpdateCursorLabelPositions(wpfPlot.Plot);

                    wpfPlot.Refresh();
                    return;
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"Error in drag handling: {ex.Message}");
            }
        }

        


        // 鼠标释放事件处理
        private void OnWpfPlotMouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            var wpfPlot = sender as ScottPlot.WPF.WpfPlot;
            if (wpfPlot != null)
            {
                // 释放鼠标捕获
                wpfPlot.ReleaseMouseCapture();
            }

            _isDraggingCursorA = false;
            _isDraggingCursorB = false;

            System.Diagnostics.Debug.WriteLine("Stopped dragging cursors");
        }

        // 鼠标离开事件处理
        private void OnWpfPlotMouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
        {
            var wpfPlot = sender as ScottPlot.WPF.WpfPlot;
            if (wpfPlot != null)
            {
                // 释放鼠标捕获
                wpfPlot.ReleaseMouseCapture();
            }

            _isDraggingCursorA = false;
            _isDraggingCursorB = false;
        }

        

        // 更新游标位置到ViewModel
        private void UpdateCursorPositions()
        {
            if (_cursorA == null || _cursorB == null) return;

            var vm = this.DataContext as ViewModels.OscilloscopeViewModel;
            if (vm != null)
            {
                vm.UpdateCursorPositions(_cursorA.Position, _cursorB.Position);
            }

            // 更新游标外观
            //UpdateCursorAppearance();
        }

      


  • 写回答

4条回答 默认 最新

  • 阿里嘎多学长 2025-11-06 14:03
    关注

    阿里嘎多学长整理AIGC生成,因移动端显示问题导致当前答案未能完全显示,请使用PC端查看更加详细的解答过程

    解决方案

    根据你的描述,似乎是scotplot控件的鼠标事件处理有问题。尝试以下解决方案:

    1. 检查scotplot控件的版本是否有更新,尝试升级到最新版本。
    2. 检查鼠标事件处理代码是否正确,确保鼠标事件被正确处理。
    3. 尝试使用MouseLeftButtonDown事件代替MouseMove事件,来实现鼠标移动光标的功能。

    核心代码

    private void Plot_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        // 鼠标左键按下事件
        // 在这里处理鼠标移动光标的逻辑
    }
    

    如果以上解决方案不起作用,请提供更多的代码信息,我将继续帮助你解决问题。

    评论

报告相同问题?

问题事件

  • 创建了问题 11月6日