C# - WPF - 鼠标在画布上移动会导致鼠标事件过载,因此无法触发点击事件。

5
我使用Shapes和Canvas,想制作类似于地图编辑器的东西。当鼠标在画布上移动时,我会在每次移动时将当前选定的对象绘制到鼠标位置的画布上,以便使用程序的人可以看到如果将该对象放置在那里,它将看起来像什么。
并且在鼠标单击时,我将当前对象/位置添加到一个列表中,该列表包含需要在每次更新时在画布上绘制的放置元素。
问题是,如果鼠标移动处理程序处于活动状态(绑定到画布),则不总是触发单击事件,我需要连续点击约十次才能放置元素。如果未绑定鼠标移动事件,则单击功能完美运行。
我制作了一个GIF来演示我的问题。
这是使用鼠标移动事件的情况
https://istack.dev59.com/qk3E6.gif
这是没有使用鼠标移动事件的情况
https://istack.dev59.com/QNrVF.gif
我认为这是因为移动事件过载了事件处理,并且没有资源来运行单击事件。
我如何同时使用这两个事件?
编辑
如建议所述,我附加了一些代码示例。
我有一个名为mapEditorModel的画布模型。对我们很重要的属性是mapEditorModel.MapObjects,它是包含需要绘制到画布上的元素的列表。
该列表包含一个包装器对象,其中包含有关元素的大量信息,对我们很重要的是,它包含用于绘制的预构建形状。
我有一个函数用于在画布上绘制元素:
private void DrawElementOnCanvas(MapElementContainer item)
    {
        Rectangle shape = item.Shape;

        CanvasElement.Children.Add(shape);
        Canvas.SetLeft(shape, item.Position.X);
        Canvas.SetTop(shape, item.Position.Y);    
    }

我有一个名为updateCanvas()的方法,代码如下:

private void updateCanvas()
    {
        CanvasElement.Children.RemoveRange(0, CanvasElement.Children.Count);

        foreach (MapElementContainer item in mapEditorModel.MapObjects)
        {
            DrawElementOnCanvas(item);   
        }
        //CollisionDetection();
    }

而两个事件方法是:

private void CanvasElement_MouseMove(object sender, MouseEventArgs e)
    {
        updateCanvas();

        MapElementContainer mapObject = new MapElementContainer();

        mapObject.Position = e.GetPosition((Canvas)sender);
        mapObject.MapElement = new ContainerMapObject();
        mapObject.CurrentRotateDegree = mapEditorModel.CurrentRotateDegree;
        mapObject.Shape = BuildShape(mapObject);

        DrawElementOnCanvas(mapObject);   
    }

private void CanvasElement_MouseDown(object sender, MouseButtonEventArgs e)
    {
        MapElementContainer mapObject = new MapElementContainer();

        mapObject.Position = e.GetPosition((Canvas)sender);
        mapObject.MapElement = new ContainerMapObject();
        mapObject.CurrentRotateDegree = mapEditorModel.CurrentRotateDegree;
        mapObject.Shape = BuildShape(mapObject);

        mapEditorModel.MapObjects.Add(mapObject);

        updateCanvas();
    }

编辑2

如果我在鼠标移动函数中注释掉所有代码,那么我仍然不能在第一次单击时将任何元素放置在画布上,这可能是设计问题吗?


我喜欢你的gif,但请展示一些move事件的代码,因为问题很可能在你的代码中,而不仅仅是绑定事件的问题。 - stijn
我按照你的建议修改了答案。 - NoNameProvided
我不太熟悉在画布上绘图,但是在移动事件中是否真的需要不断地删除并重新添加所有项目呢?难道你不能只更新位置吗? - stijn
是的,这个解决方案会消耗一些资源,它可能是问题的根源吗?在开始时,它将是第一个,但当鼠标离开画布时需要将其移除,之后它将被放置在最后,但我如何知道在移动时最后一个是已放置的(不应被覆盖)还是占位符?因此,管理它并不容易,因为我不知道它在列表中的位置,所以很难决定何时需要覆盖列表中的一个或放置一个新的。也许完全单独处理可以解决问题。 - NoNameProvided
2个回答

2
这里有一个最小化的代码示例,可以正常运行并且不会出现你所提到的问题:我可以快速地点击添加形状。我建议你检查你的代码与我的代码的差异以找出问题的原因——首先,我没有像你那样连续调用updateCanvas或类似的函数,因为这是不必要的。
<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" SizeToContent="WidthAndHeight">
  <Canvas x:Name="canvas" Background="AntiqueWhite" Width="1024" Height="768"
          MouseMove="Canvas_MouseMove" MouseDown="Canvas_MouseDown" />
</Window>

xaml.cs:

using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;

namespace WpfApplication1
{
  public class Item
  {
    private readonly Rectangle shape;

    public Item( Canvas canvas )
    {
      shape = new Rectangle { Width = 50, Height = 50, Fill = Brushes.Black };
      canvas.Children.Add( shape );
      SetPosition( 0.0, 0.0 );
    }

    public void SetPosition( double x, double y )
    {
      Canvas.SetLeft( shape, x );
      Canvas.SetTop( shape, y );
    }
  }

  public partial class MainWindow : Window
  {
    private readonly IList<Item> shapes;
    private Item currentMovingShape;

    public MainWindow()
    {
      InitializeComponent();
      shapes = new List<Item>();
      InitMovingShape();
    }

    private void InitMovingShape()
    {
      currentMovingShape = new Item( canvas );
    }

    private void SetMovingShapePosition( MouseEventArgs e )
    {
      var pos = e.GetPosition( canvas );
      currentMovingShape.SetPosition( pos.X, pos.Y );
    }

    private void Canvas_MouseMove( object sender, MouseEventArgs e )
    {
      SetMovingShapePosition( e );
    }

    private void Canvas_MouseDown( object sender, MouseButtonEventArgs e )
    {
      shapes.Add( currentMovingShape );
      InitMovingShape();
      SetMovingShapePosition( e );
    }
  }
}

谢谢,它完美地工作了,问题出在鼠标移动处理程序开始时调用updateCanvas()方法上。 - NoNameProvided

0
也许MouseDown事件在MapElementContainer类中被处理,从未到达CanvasElement?在您的第二个视频中,您总是点击空白区域。在您的第一个视频中,鼠标下方始终有MapElementContainer。

MapElementContainer并不存在作为GUI元素,鼠标下方的是我的画布,在那个位置实际上有一个元素被绘制。或者说,形状是从画布中单独处理的吗?无论如何,它是画布的子部分,因此事件冒泡会到达画布。 - NoNameProvided

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接