Canvas上未检测到绘制元素的MouseMove事件。

huangapple go评论71阅读模式
英文:

Canvas MouseMove event not detected on drawn elements

问题

我需要你的帮助。我正在开发一个带有鼠标管理功能的绘图程序,用于移动和缩放绘图。

一切都运行得相当顺利。

然而,我遇到了一个问题。当光标位于一条线上时,我的MouseWheel和MouseMove事件不再起作用。

这可能是因为程序将其视为我的画布的子元素而不是我的画布。

我不知道如何解决这个问题?

在画布上的鼠标:移动和缩放有效。

鼠标在画布上
Canvas上未检测到绘制元素的MouseMove事件。

在线上的鼠标:移动和缩放不起作用。

在线上的鼠标
Canvas上未检测到绘制元素的MouseMove事件。

XAML

<Window x:Class="CanvasCad.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        
        xmlns:local="clr-namespace:CanvasCad"
        
        mc:Ignorable="d"
        
        Title="MainWindow" 
        Height="450" Width="800"
        
        Loaded="Window_Loaded"
        >
    
    <Grid Margin="5">

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="200"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="200"/>
        </Grid.ColumnDefinitions>

        <StackPanel Grid.Column="0" Margin="5">

            <Label Content="Model origin in canvas"/>
            <Label Name="lblOriginInCanvasX" Content="X="/>
            <Label Name="lblOriginInCanvasY" Content="Y="/>
            <Separator Height="1" />

            <Label Content="Model scale in canvas"/>
            <Label Name="lblScaleModelInCanvas" Content="z="/>
            <Separator Height="1" />

            <Label Content="Mouse in canvas"/>
            <Label Name="lblMouseInCanvasX" Content="X="/>
            <Label Name="lblMouseInCanvasY" Content="Y="/>
            <Separator Height="1" />

            <Label Content="Mouse in model"/>
            <Label Name="lblMouseInModelX" Content="X="/>
            <Label Name="lblMouseInModelY" Content="Y="/>
            <Separator Height="1" />

        </StackPanel>

        <Canvas
            Name="CanvasCad"
            Grid.Column="1"
            >
        </Canvas>

        <StackPanel
            Grid.Column="2">
            <Button Name="btTest">Test</Button>
        </StackPanel>

    </Grid>
    
</Window>

C#

// 这里是你的C#代码,不需要翻译
// 这里是你的C#代码,不需要翻译
// 这里是你的C#代码,不需要翻译
// 这里是你的C#代码,不需要翻译

<details>
<summary>英文:</summary>

I need your help. I&#39;m working on a drawing program with mouse management of moving and zooming the drawing.

It all works quite well.

However, I am having a problem. My MouseWheel and MouseMove events no longer work when the cursor is on a line.

It&#39;s probably because the program considers it to be the child element of my canvas and not my canvas.

I don&#39;t see how to work around the problem?

the mouse on the canvas : moving and zooming works.

mouse on the canvas  
[![enter image description here][1]][1]

the mouse on the line : moving and zooming not works.

the mouse on the line  
[![enter image description here][2]][2]

**xaml**

<Window x:Class="CanvasCad.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

    xmlns:local=&quot;clr-namespace:CanvasCad&quot;
    
    mc:Ignorable=&quot;d&quot;
    
    Title=&quot;MainWindow&quot; 
    Height=&quot;450&quot; Width=&quot;800&quot;
    
    Loaded=&quot;Window_Loaded&quot;
    &gt;

&lt;Grid Margin=&quot;5&quot;&gt;

    &lt;Grid.ColumnDefinitions&gt;
        &lt;ColumnDefinition Width=&quot;200&quot;/&gt;
        &lt;ColumnDefinition Width=&quot;*&quot;/&gt;
        &lt;ColumnDefinition Width=&quot;200&quot;/&gt;
    &lt;/Grid.ColumnDefinitions&gt;

    &lt;StackPanel Grid.Column=&quot;0&quot; Margin=&quot;5&quot;&gt;

        &lt;Label Content=&quot;Model origin in canvas&quot;/&gt;
        &lt;Label Name=&quot;lblOriginInCanvasX&quot; Content=&quot;X=&quot;/&gt;
        &lt;Label Name=&quot;lblOriginInCanvasY&quot; Content=&quot;Y=&quot;/&gt;
        &lt;Separator Height=&quot;1&quot; /&gt;

        &lt;Label Content=&quot;Model scale in canvas&quot;/&gt;
        &lt;Label Name=&quot;lblScaleModelInCanvas&quot; Content=&quot;z=&quot;/&gt;
        &lt;Separator Height=&quot;1&quot; /&gt;

        &lt;Label Content=&quot;Mouse in canvas&quot;/&gt;
        &lt;Label Name=&quot;lblMouseInCanvasX&quot; Content=&quot;X=&quot;/&gt;
        &lt;Label Name=&quot;lblMouseInCanvasY&quot; Content=&quot;Y=&quot;/&gt;
        &lt;Separator Height=&quot;1&quot; /&gt;

        &lt;Label Content=&quot;Mouse in model&quot;/&gt;
        &lt;Label Name=&quot;lblMouseInModelX&quot; Content=&quot;X=&quot;/&gt;
        &lt;Label Name=&quot;lblMouseInModelY&quot; Content=&quot;Y=&quot;/&gt;
        &lt;Separator Height=&quot;1&quot; /&gt;

    &lt;/StackPanel&gt;

    &lt;Canvas
        Name=&quot;CanvasCad&quot;
        Grid.Column=&quot;1&quot;
        &gt;
    &lt;/Canvas&gt;

    &lt;StackPanel
        Grid.Column=&quot;2&quot;&gt;
        &lt;Button Name=&quot;btTest&quot;&gt;Test&lt;/Button&gt;
    &lt;/StackPanel&gt;

&lt;/Grid&gt;

</Window>

c#


**c#**

using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Xml.Linq;

namespace CanvasCad
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{

    private Point _originModelInCanvas;
    private double _scaleModelInCanvas;
    private double _zoomFactor;

    private bool _IsModelMove;
    private Point _vectorMouseToOriginInCanvas;

    private List&lt;Entity&gt; _entities;

    public MainWindow()
    {
        InitializeComponent();

        //Rendu du canvas
        CanvasCad.Background = new SolidColorBrush(Colors.Black);
        CanvasCad.ClipToBounds = true;

        //Gestion des &#233;v&#232;nement
        CanvasCad.MouseMove += CanvasCad_MouseMove;
        CanvasCad.MouseDown += CanvasCad_MouseDown;
        CanvasCad.MouseUp += CanvasCad_MouseUp;
        CanvasCad.MouseWheel += CanvasCad_MouseWheel;

        //Balise
        _IsModelMove = false;


        //Definition de l&#39;origine
        _originModelInCanvas = new Point(0, 0);
        _scaleModelInCanvas = 1;

        //Facteur de zoom
        _zoomFactor = 0.1;

        //Liste des entit&#233;es de dessin
        _entities = new List&lt;Entity&gt;();
        Entity e;
        //axe x
        e = new Entity(new Point(-500, 0), new Point(500, 0), new SolidColorBrush(Colors.Blue), 2);
        _entities.Add(e);
        //axe y
        e = new Entity(new Point(0, -500), new Point(0, 500), new SolidColorBrush(Colors.Blue), 2);
        _entities.Add(e);
        //ligne1
        e = new Entity(new Point(50, 70), new Point(100, 30), new SolidColorBrush(Colors.Red), 4);
        _entities.Add(e);
        //ligne2
        e = new Entity(new Point(200, -83), new Point(-96, 300), new SolidColorBrush(Colors.Orange), 4);
        _entities.Add(e);

    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {

        //Definition de l&#39;origine
        _originModelInCanvas = new Point(75, 130);
        _scaleModelInCanvas = 1;

        //Affichage
        lblOriginInCanvasX.Content = $&quot;X={_originModelInCanvas.X.ToString()}&quot;;
        lblOriginInCanvasY.Content = $&quot;Y={_originModelInCanvas.Y.ToString()}&quot;;
        lblScaleModelInCanvas.Content = $&quot;Z={_scaleModelInCanvas.ToString()}&quot;;

        //Redessiner
        Redraw();

    }

    private void CanvasCad_MouseMove(object sender, MouseEventArgs e)
    {

        //Position de la souris dans le canvas (coordonn&#233;es wpf)
        Point ptMouseInCanvas = e.GetPosition(CanvasCad);

        //Position de la souris dans le World
        Point ptMouseInModel = ConvertPoint.MouseCanvasToModel (ptMouseInCanvas, _originModelInCanvas, _scaleModelInCanvas);

        //Gestion du d&#233;placement du Model par la souris
        if (_IsModelMove)
            //Bouton de la roulette appuy&#233;
        {
            _originModelInCanvas = new Point(ptMouseInCanvas.X-_vectorMouseToOriginInCanvas.X, ptMouseInCanvas.Y - _vectorMouseToOriginInCanvas.Y);
        }

        //Affichage
        lblMouseInCanvasX.Content = $&quot;X={ptMouseInCanvas.X.ToString()}&quot;;
        lblMouseInCanvasY.Content = $&quot;Y={ptMouseInCanvas.Y.ToString()}&quot;;
        lblMouseInModelX.Content = $&quot;X={ptMouseInModel.X.ToString()}&quot;;
        lblMouseInModelY.Content = $&quot;Y={ptMouseInModel.Y.ToString()}&quot;;

        //Redessiner
        Redraw();

    }

    private void CanvasCad_MouseWheel(object sender, MouseWheelEventArgs e)
    {

        //Position de la souris dans le canvas (coordonn&#233;es wpf)
        Point ptMouseInCanvas = e.GetPosition(CanvasCad);

        if (!_IsModelMove)
        {
            //D&#233;tection de la roulette
            double zoom = 0;
            if (e.Delta &gt; 0) { zoom = 1 + _zoomFactor; }
            if (e.Delta &lt; 0) { zoom = 1 - _zoomFactor; }

            //Vecteur entre la souris et l&#39;origine
            _vectorMouseToOriginInCanvas = new Point(ptMouseInCanvas.X - _originModelInCanvas.X, ptMouseInCanvas.Y - _originModelInCanvas.Y);

            _originModelInCanvas = new Point(ptMouseInCanvas.X - _vectorMouseToOriginInCanvas.X * zoom, ptMouseInCanvas.Y - _vectorMouseToOriginInCanvas.Y * zoom);
            _scaleModelInCanvas = _scaleModelInCanvas * zoom;

            //Redessiner
            Redraw();
        }
        
    }

    private void CanvasCad_MouseDown(object sender, MouseButtonEventArgs e)
    {

        //Position de la souris dans le canvas
        Point ptMouseInCanvas = new Point(e.GetPosition(CanvasCad).X, e.GetPosition(CanvasCad).Y);

        //Bouton de la souris
        switch (e.ChangedButton)
        {
            //
            case MouseButton.Left:
                break;
            
            //
            case MouseButton.Right:
                break;
            
            //Gestion du d&#233;placement du Model
            case MouseButton.Middle:
                //Bouton de la roulette appuy&#233;
                _IsModelMove = true;
                //Vecteur entre la souris et l&#39;origine
                _vectorMouseToOriginInCanvas = new Point(ptMouseInCanvas.X-_originModelInCanvas.X, ptMouseInCanvas.Y - _originModelInCanvas.Y);
                break;

        }

    }

    private void CanvasCad_MouseUp(object sender, MouseButtonEventArgs e)
    {

        //Position de la souris dans le canvas
        Point ptMouseCanvas = new Point(e.GetPosition(this).X, e.GetPosition(this).Y);

        //Bouton de la souris
        switch (e.ChangedButton)
        {
            
            case MouseButton.Left:
                break;
            
            case MouseButton.Right:
                break;
            
            case MouseButton.Middle:
                //Bouton de la roulette relach&#233;e
                _IsModelMove = false;
                break;
        }

    }

    private void Redraw()
    {
        
        //Canvas nettoy&#233;
        CanvasCad.Children.Clear();

        //Ajout des entit&#233;es au canvas
        foreach (Entity e in _entities)
        {
            //CanvasCad.Children.Add(e.ToModel());
            CanvasCad.Children.Add(e.ToModel(_originModelInCanvas, _scaleModelInCanvas));
        }
    }

}

}


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;

namespace CanvasCad
{
internal class Entity
{

    private Point _pointStart;
    public Point pointStart
    {
        get { return _pointStart; }
        set { _pointStart = value; }
    }


    private Point _pointEnd;
    public Point pointEnd
    {
        get { return _pointEnd; }
        set { _pointEnd = value; }
    }

    private Brush _brush;
    public Brush brush
    {
        get { return _brush; }
        set { _brush = value; }
    }

    private int _thickness;
    public int thickness
    {
        get { return _thickness; }
        set { _thickness = value; }
    }

    public Entity()
    {
        _pointStart = new Point(0, 0);
        _pointEnd = new Point(0, 0);
        //_SolidColorBrush = new SolidColorBrush(Color.FromRgb(0, 0, 255));
        _brush = Brushes.Black;
        _thickness = 1;
    }

    public Entity(Point pointStart, Point pointEnd, Brush brush, int thickness)
    {
        _pointStart = pointStart;
        _pointEnd = pointEnd;
        _brush = brush;
        _thickness = thickness;
    }

    public Path ToCanvas()
    {

        Path p = new Path();
        p.Stroke = _brush;
        p.StrokeThickness = _thickness;

        LineGeometry lg = new LineGeometry(_pointStart, _pointEnd);

        GeometryGroup gg = new GeometryGroup();
        gg.Children.Add(lg);

        p.Data = gg;

        return p;

    }

    public Path ToModel(Point origin, double scale)
    {

        Path p = new Path();
        p.Stroke = _brush;
        p.StrokeThickness = _thickness;

        LineGeometry lg = new LineGeometry(ConvertPoint.CanvasToModel(_pointStart, origin, scale), ConvertPoint.CanvasToModel(_pointEnd, origin, scale));

        //Matrix translateMatrix = new Matrix();
        //translateMatrix.Translate(100, 0);
        //p.Transform(translateMatrix);

        GeometryGroup gg = new GeometryGroup();
        gg.Children.Add(lg);

        p.Data = gg;

        return p;

    }

}

}


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;

namespace CanvasCad
{
internal class ConvertPoint
{

    public static Point CanvasToModel(Point point, Point origine, double scale)
    {
        
        //point de r&#233;f&#233;rence
        Point p = new Point(point.X, point.Y);

        //sym&#233;trie par rapport &#224; y
         p = new Point(p.X, -p.Y);

        //mise &#224; l&#39;echelle par rapport &#224; l&#39;origine
        p = new Point(p.X*scale, p.Y*scale);

        //d&#233;placement de l&#39;origine
        p = new Point(p.X + origine.X, p.Y + origine.Y);

        return p;
    }

    public static Point MouseCanvasToModel(Point mouse, Point origine, double scale)
    {

        //point de r&#233;f&#233;rence
        Point p = new Point(mouse.X, mouse.Y);

        //d&#233;placement de l&#39;origine
        p = new Point(p.X - origine.X, p.Y - origine.Y);

        //mise &#224; l&#39;echelle par rapport &#224; l&#39;origine
        p = new Point(p.X / scale, p.Y / scale);

        //sym&#233;trie par rapport &#224; y
        p = new Point(p.X, -p.Y);

        return p;
    }

}

}



  [1]: https://i.stack.imgur.com/iQTWd.png
  [2]: https://i.stack.imgur.com/DOgWH.png

</details>


# 答案1
**得分**: 1

有几种方法可以解决这个问题。

问题的原因是因为您在画布上处理事件,而路径位于其中。事件在路径上触发而不是画布上,因为它首先被“命中”。

解决这个问题的最简单方法是使用预览事件。
例如,

PreviewMouseMove。
https://learn.microsoft.com/en-us/dotnet/api/system.windows.uielement.previewmousemove?view=windowsdesktop-6.0

和

PreviewMouseWheel
https://learn.microsoft.com/en-us/dotnet/api/system.windows.uielement.previewmousewheel?view=windowsdesktop-6.0

之所以这些事件会起作用,而mousemove和mousewheel不会,是因为在WPF中路由事件的工作方式不同。

https://learn.microsoft.com/en-us/dotnet/desktop/wpf/events/routed-events-overview?view=netdesktop-6.0

如果您不与它们交互,还可以考虑将路径的IsHitTestVisible属性设置为false。

<details>
<summary>英文:</summary>

There are several ways to work round this.

The problem is caused because you handle events on the canvas and your paths are in the way. The event fires on the path rather than the canvas because it&#39;s &quot;hit&quot; first.   

The simplest approach to working round that is to use preview events.
eg

PreviewMouseMove.
https://learn.microsoft.com/en-us/dotnet/api/system.windows.uielement.previewmousemove?view=windowsdesktop-6.0

and

PreviewMouseWheel
https://learn.microsoft.com/en-us/dotnet/api/system.windows.uielement.previewmousewheel?view=windowsdesktop-6.0

The reason these will work whilst mousemove and mousewheel will not is because of the way routed events work in wpf.

https://learn.microsoft.com/en-us/dotnet/desktop/wpf/events/routed-events-overview?view=netdesktop-6.0

If you do not interact with them, you could also consider making the paths IsHitTestVisible false.



</details>



# 答案2
**得分**: 1

在@Andy的回答中添加。首先,一些常规信息。RoutedEvent 主要有以下特点:
- 冒泡 - 从最终元素上升到窗口的事件;
- 隧道 - 从窗口浸入最终元素的事件。
在任何级别上,事件都可以标记为已处理,然后带有此标志继续沿着路由移动。

CLR 包装事件(例如,UIElement.MouseMove)是访问静态 RoutedEvent 方法的代理方法(参见[源代码][1]):
```cs
public event MouseEventHandler MouseMove
{
    add { AddHandler(Mouse.MouseMoveEvent, value, false); }
    remove { RemoveHandler(Mouse.MouseMoveEvent, value); }
}

AddHandler 方法中的 false 参数表示不应调用带有执行标志的事件处理程序。
因此,如果子元素已将冒泡事件标记为已处理,则不会调用使用事件的 CLR 包装的父元素处理程序。
这正是在你的情况中发生的事情。

实际上是一种可能的解决方案。
您可以直接使用 RoutedEvent 的静态方法连接处理程序。在这种情况下,您可以指定它即使对于已处理的事件也要调用。

AddHandler(Mouse.MouseMoveEvent, new MouseEventHandler(CanvasCad_MouseMove), true);
英文:

Adding to @Andy's answer.
First, some general information. RoutedEvent are mostly:

  • bubbling - those that rise from the final element up to the Window;
  • tunneling - those that are immersed from the Window to the final element.
    At any level, an event can be marked as processed and it will move further along the route with this flag.

The CLR wrapper-event (for example, UIElement.MouseMove) is a proxy methods that accesses the static RoutedEvent methods (see Source Code):

        public event MouseEventHandler MouseMove
        {
            add { AddHandler(Mouse.MouseMoveEvent, value, false); }
            remove { RemoveHandler(Mouse.MouseMoveEvent, value); }
        }

The false parameter in the AddHandler method indicates that the handler should not be called for events marked with the execution flag.
For this reason, if a child element has marked the bubble event as handled, then the parent element's handler wired with the event's CLR wrapper will not be invoked.
This is exactly what happened in your case.

Actually a possible solution.
You can connect the handler directly using the static methods of RoutedEvent. In this case, you can specify that it be called even for processed events.

    AddHandler(Mouse.MouseMoveEvent, new MouseEventHandler(CanvasCad_MouseMove), true);

huangapple
  • 本文由 发表于 2023年1月9日 06:26:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/75051702.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定