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
相关产品推荐
相关产品推荐

