You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF Canvas拖拽矩形时出现闪烁跳动问题求助

问题解决:WPF Canvas拖拽矩形闪烁/坐标跳变

问题根源

拖拽时鼠标坐标在当前位置和旧位置之间跳变,是因为鼠标事件的e.Source发生了变化:当鼠标移到绘制的Rectangle上时,e.Source从Canvas变成了Rectangle,此时GetPosition(e.Source)获取的是相对于Rectangle的局部坐标,而非Canvas的全局坐标,导致矩形位置计算错误,UI反复更新引发闪烁。

解决方案

步骤1:统一获取Canvas相对坐标

在视图后台代码中,直接计算鼠标相对于Canvas的坐标,再传递给ViewModel,避免ViewModel依赖UI事件参数中的动态Source:

修改CanvasWindow.xaml.cs中的鼠标事件处理:

private void Canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    var viewModel = DataContext as CanvasWindowViewModel;
    if (viewModel != null)
    {
        // 获取相对于Canvas的起始点
        var startPoint = e.GetPosition(viewCanvas);
        viewModel.StartDrawingCommand.Execute(startPoint);
    }
}

private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
    var viewModel = DataContext as CanvasWindowViewModel;
    if (viewModel != null)
    {
        // 获取相对于Canvas的当前点
        var currentPoint = e.GetPosition(viewCanvas);
        viewModel.UpdateDrawingCommand.Execute(currentPoint);
    }
}

private void Canvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
    var viewModel = DataContext as CanvasWindowViewModel;
    if (viewModel != null)
    {
        viewModel.EndDrawingCommand.Execute(null);
    }
}

步骤2:修改ViewModel命令参数

更新ViewModel中的命令,接收Point类型参数而非鼠标事件参数,简化坐标计算逻辑:

public class CanvasWindowViewModel : INotifyPropertyChanged
{
    public ICommand StartDrawingCommand { get; }
    public ICommand UpdateDrawingCommand { get; }
    public ICommand EndDrawingCommand { get; }

    public CanvasWindowViewModel()
    {
        // 修改命令参数类型为Point/object
        StartDrawingCommand = new DelegateCommand<Point>(StartDrawing);
        UpdateDrawingCommand = new DelegateCommand<Point>(UpdateDrawing);
        EndDrawingCommand = new DelegateCommand<object>(EndDrawing);
    }

    // ... 其他属性保持不变 ...

    private void StartDrawing(Point startPoint)
    {
        _startPoint = startPoint;
        SelectionRectangle = new Rect(_startPoint, new Size(0, 0));
        IsDrawing = true;
        Debug.WriteLine($"Start X: {_startPoint.X}, Start Y: {_startPoint.Y}");
    }

    private void UpdateDrawing(Point currentPoint)
    {
        if (IsDrawing)
        {
            var x = Math.Min(currentPoint.X, _startPoint.X);
            var y = Math.Min(currentPoint.Y, _startPoint.Y);
            var width = Math.Abs(currentPoint.X - _startPoint.X);
            var height = Math.Abs(currentPoint.Y - _startPoint.Y);
            SelectionRectangle = new Rect(x, y, width, height);
            Debug.WriteLine($"Update X: {currentPoint.X}, Update Y: {currentPoint.Y}");
        }
    }

    private void EndDrawing(object obj)
    {
        IsDrawing = false;
        Debug.WriteLine("Drawing Ended");
    }

    // ... 其他方法保持不变 ...
}

步骤3:优化绑定模式(可选)

由于矩形的位置和尺寸仅由ViewModel驱动,无需UI回传值,给绑定添加Mode=OneWay减少不必要的绑定更新:

<Rectangle Stroke="Red" StrokeThickness="2"
           Visibility="{Binding IsDrawing, Converter={StaticResource BooleanToVisibilityConverter}}"
           Canvas.Left="{Binding SelectionRectangle.X, Mode=OneWay}"
           Canvas.Top="{Binding SelectionRectangle.Y, Mode=OneWay}"
           Width="{Binding SelectionRectangle.Width, Mode=OneWay}"
           Height="{Binding SelectionRectangle.Height, Mode=OneWay}"/>

效果验证

修改完成后,拖拽矩形时鼠标坐标将始终基于Canvas计算,不会因鼠标移到Rectangle上而跳变,闪烁问题即可解决。

内容的提问来源于stack exchange,提问作者JianYA

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 13:37:31