c#wpf重叠控件不接收鼠标事件 [英] c# wpf overlapping controls not receiving mouse events

查看:854
本文介绍了c#wpf重叠控件不接收鼠标事件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在构建一个画布控件.此根画布有几个重叠的子代(画布也是如此).这样做是为了让每个孩子都可以处理自己的画,然后我可以将任何孩子的组合组成最终结果,以获得所需的行为.

I am building a canvas control. This root canvas has several overlapping children (canvas as well). This is done so each child can handle its own drawing and I can then compose the final result with any combination of children to get the desired behavior.

就渲染而言,这非常有效.但是,这在鼠标事件中不能很好地工作.鼠标事件的工作方式如下(以Previewmousemove为例):

This is working very well as far as rendering is concerned. This does not work so well with mouse events however. The way mouse events works are as follow (using previewmousemove as an example):

1-如果根画布在鼠标下,则触发事件 2-检查所有孩子,如果其中一个在鼠标下,则开火并停止

1- If root canvas is under mouse, fire event 2- Check all children, if one is under mouse, fire event and stop

这样,只有我添加的第一个孩子会收到鼠标移动事件.该事件不会传播给所有子项,因为它们会重叠.

As such, only the first child I add will receive the mouse move event. The event is not propagated to all children because they overlap.

为了克服这个问题,我尝试了以下操作: 1-覆盖根画布中的鼠标事件 2-对于每个事件,请使用VisualTreeHelper.HitTest查找要处理该事件的所有子级 3-对于所有返回有效命中测试结果的孩子(即:在鼠标下并且愿意处理事件(IsHitTestVisible == true)),???

To overcome this, I attempted the following: 1- Override mouse events in the root canvas 2- For every event, find all children that want to handle the event using VisualTreeHelper.HitTest 3- For all children that returned a valid hit test result (ie: under mouse and willing to handle the event (IsHitTestVisible == true)), ???

这是我遇到的问题,我需要以某种方式将鼠标事件发送给所有孩子,并停止事件的正常进行,以确保第一个孩子不会两次接收到该事件(在事件中通过handle = true) ).

This is where I am stuck, I somehow need to send the mouse event to all children, and stop the normal flow of the event to make sure the first child doesn't receive it twice (via handled = true in the event).

通过将RaiseEvent与在子级上传递的相同事件一起使用,事情似乎可以正常工作,但又会以某种方式在父级(根画布)上引发该事件.为了绕过这个问题,我需要创建事件的副本并设置强制设置源,尽管它似乎更多地是hack,而不是解决方案.是否有正确的方法去做我想做的事情?代码示例如下.

By using RaiseEvent with the same event passed on the children, things seem to work but somehow it raises the event on the parent (root canvas) as well. To bypass this I needed to create a copy of the event and set force set the source though it appears to be more of a hack than a solution. Is there a proper way to do what I am trying to do? Code example follows.

    public class CustomCanvas : Canvas
    {
        private List<object> m_HitTestResults = new List<object>();

        public new event MouseEventHandler MouseMove;

        public CustomCanvas()
        {
            base.PreviewMouseMove += new MouseEventHandler(CustomCanvas_MouseMove);
        }

        private void CustomCanvas_MouseMove(object sender, MouseEventArgs e)
        {
// Hack here, why is the event raised on the parent as well???
            if (e.OriginalSource == this)
            {
                return;
            }

                Point pt = e.GetPosition((UIElement)sender);
                m_HitTestResults.Clear();

                VisualTreeHelper.HitTest(this,
                    new HitTestFilterCallback(OnHitTest),
                    new HitTestResultCallback(OnHitTest),
                    new PointHitTestParameters(pt));

                MouseEventArgs tmpe = new MouseEventArgs(e.MouseDevice, e.Timestamp, e.StylusDevice);
                tmpe.RoutedEvent = e.RoutedEvent;
                tmpe.Source = this;

                foreach (object hit in m_HitTestResults)
                {
                    UIElement element = hit as UIElement;
                    if (element != null)
                    {
 // This somehow raises the event on us as well as the element here, why???
                        element.RaiseEvent(tmpe);
                    }
                }


            var handlers = MouseMove;
            if (handlers != null)
            {
                handlers(sender, e);
            }

            e.Handled = true;
        }

        private HitTestFilterBehavior OnHitTest(DependencyObject o)
        {
            UIElement element = o as UIElement;
            if (element == this)
            {
                return HitTestFilterBehavior.ContinueSkipSelf;
            }
            else if (element != null && element.IsHitTestVisible && element != this)
            {
                return HitTestFilterBehavior.Continue;
            }
            return HitTestFilterBehavior.ContinueSkipSelfAndChildren;
        }

        private HitTestResultBehavior OnHitTest(HitTestResult result)
        {
            // Add the hit test result to the list that will be processed after the enumeration.
            m_HitTestResults.Add(result.VisualHit);
            // Set the behavior to return visuals at all z-order levels.
            return HitTestResultBehavior.Continue;
        }

推荐答案

我认为您应该使用预览事件,因为它们是RoutingStrategy.Tunnel,从Window到Z-Order的最高控件,正常事件是RoutingStrategy.Bubble.

I think you should use the preview events because those are RoutingStrategy.Tunnel from Window to the most high control in Z-Order, and normal events are RoutingStrategy.Bubble.

在此RoutedEvents中有一个属性Handle,如果为true,则系统将停止遍历可视化树,因为有人使用了此事件.

In this RoutedEvents there are a property Handle when it's true the system will stop to traverse the visual tree because someone used this event.

这篇关于c#wpf重叠控件不接收鼠标事件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆