首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >防止事件起泡: WPF & Rx

防止事件起泡: WPF & Rx
EN

Stack Overflow用户
提问于 2010-11-26 23:57:01
回答 2查看 1.6K关注 0票数 0

我有一个小的WPF应用程序,它在画布上画一些点,然后在mouseDown发生在任何点之后跟踪鼠标移动,直到画布上有一个mouseUp。我的问题是如何防止点内的mouseDowns冒泡到画布上。我希望画布mouseDown观察者只在mouseDowns发生在圆点之外时才开火,而对于点mouseDown观察者来说,只有当mouseDowns发生在圆点内部时,才能触发。

下面是我构建可观测链的技术,表明如果EventArgs.Handled的第三个参数为真,则在触发mouseDown事件中设置DownInAndTrackWrtObservable属性:

代码语言:javascript
复制
private IObservable<BasedVector> DownInAndTrackWrtObservable(
    UIElement hitTestElement, 
    UIElement trackWrtElement,
    bool handleItP = false)
{
    var hitObs = hitTestElement.GetLeftMouseDownObservable();

    var trackObs = from mDown in hitObs
                   let mDownP = mDown.EventArgs.GetPosition(hitTestElement)
                   from mMove in trackWrtElement.GetMouseMoveObservable().
                       TakeUntil(trackWrtElement.GetLeftMouseUpObservable())
                   select new {D = mDown, P = mDownP, M = mMove};

    if (handleItP)
        trackObs.Subscribe(e =>
            e.D.EventArgs.Handled = true);

    return from obs in trackObs 
           select new BasedVector
           (
               tail: obs.P,
               head: obs.M.EventArgs.GetPosition(trackWrtElement)
           );
}

然后,我在两个地方设置了处理程序,一个是跟踪画布中的点击、点和拖拽。

代码语言:javascript
复制
DownInAndTrackWrtObservable(ellipse, canvas1, handleItP:true).Subscribe(bv =>
{
    PointMouseDownTextBox.Text =
        String.Format("Point {2} MouseDown({0:G}, {1:G})", bv.Tail.X, bv.Tail.Y, ellipse.Uid);
    CanvasMouseMoveTextBox.Text =
        String.Format("Canvas MouseMove({0:G}, {1:G})", bv.Head.X, bv.Head.Y);
});

代码语言:javascript
复制
DownInAndTrackWrtObservable(canvas1, canvas1).Subscribe(bv =>
{
    CanvasMouseDownTextBox.Text =
        String.Format("Canvas MouseDown({0:G}, {1:G})", bv.Tail.X, bv.Tail.Y);
    CanvasMouseMoveTextBox.Text =
        String.Format("Canvas MouseMove({0:G}, {1:G})", bv.Head.X, bv.Head.Y);
});

问题是,当点中发生mouseDown时,仍然会检测到画布mouseDown,如第二个处理程序所示。但我不认为这是因为设置了Event.Handled属性。顺便说一句,如果我把Event.Handled设置在hitObs上而不是trackObs上,那么点击率就不会在点内全部检测到。这是一个完整的应用程序,带有它的XAML (请“添加引用”到System.coreex、System.Reactive和System.Interactive)

代码语言:javascript
复制
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Shapes;

namespace WpfTest
{
    public static partial class UIElementExtensions
    {
        public static IObservable<IEvent<MouseButtonEventArgs>>
            GetLeftMouseDownObservable(this UIElement uiElement)
        {
            return Observable.FromEvent<MouseButtonEventHandler, MouseButtonEventArgs>(
               h => new MouseButtonEventHandler(h),
               h => uiElement.MouseLeftButtonDown += h,
               h => uiElement.MouseLeftButtonDown -= h);
        }

        public static IObservable<IEvent<MouseButtonEventArgs>>
            GetLeftMouseUpObservable(this UIElement uiElement)
        {
            return Observable.FromEvent<MouseButtonEventHandler, MouseButtonEventArgs>(
               h => new MouseButtonEventHandler(h),
               h => uiElement.MouseLeftButtonUp += h,
               h => uiElement.MouseLeftButtonUp -= h);
        }

        public static IObservable<IEvent<MouseEventArgs>>
            GetMouseMoveObservable(this UIElement uiElement)
        {
            return Observable.FromEvent<MouseEventHandler, MouseEventArgs>(
               h => new MouseEventHandler(h),
               h => uiElement.MouseMove += h,
               h => uiElement.MouseMove -= h);
        }
    }

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            Draw();
        }

        private static double scaleCanvasPerWorld = 150D;
        private static Point offsetInCanvas = new Point(15D, 15D);
        private static Point worldOrigin = new Point();
        private static Point WorldToCanvas(Point worldPoint)
        {
            // Difference of two points gives a System.Drawing.Vector, which has operator overloads.
            var result = ((worldPoint - worldOrigin) * scaleCanvasPerWorld + offsetInCanvas);
            return result;
        }
        private static Point CanvasToWorld(Point canvasPoint)
        {
            var result = ((canvasPoint - offsetInCanvas) / scaleCanvasPerWorld);
            return (Point)result;
        }

        private void DrawPoint(Point worldPoint)
        {
            var ellipse = new Ellipse();
            ellipse.Stroke = System.Windows.Media.Brushes.Black;
            ellipse.Fill = System.Windows.Media.Brushes.Black;
            ellipse.HorizontalAlignment = HorizontalAlignment.Center;
            ellipse.VerticalAlignment = VerticalAlignment.Center;
            ellipse.Width = 12;
            ellipse.Height = 12;

            var canvasPoint = WorldToCanvas(worldPoint);
            ellipse.SetValue(Canvas.LeftProperty, canvasPoint.X);
            ellipse.SetValue(Canvas.TopProperty, canvasPoint.Y);

            DownInAndTrackWrtObservable(ellipse, canvas1, handleItP: true).Subscribe(bv =>
            {
                PointMouseDownTextBox.Text =
                    String.Format("Point {2} MouseDown({0:G}, {1:G})", bv.Tail.X, bv.Tail.Y, ellipse.Uid);
                CanvasMouseMoveTextBox.Text =
                    String.Format("Canvas MouseMove({0:G}, {1:G})", bv.Head.X, bv.Head.Y);
            });

            canvas1.Children.Add(ellipse);
        }

        private class BasedVector
        {
            public Point Tail { get; set; }
            public Point Head { get; set; }
            public Vector Vector { get; set; }
            public BasedVector(Point tail, Point head)
            {
                Debug.Assert(tail != null);
                Debug.Assert(head != null);

                this.Tail = tail;
                this.Head = head;

                this.Vector = head - tail;
            }
        }

        private IObservable<BasedVector> DownInAndTrackWrtObservable(
            UIElement hitTestElement,
            UIElement trackWrtElement,
            bool handleItP = false)
        {
            var hitObs = hitTestElement.GetLeftMouseDownObservable();

            var trackObs = from mDown in hitObs
                           let mDownP = mDown.EventArgs.GetPosition(hitTestElement)
                           from mMove in trackWrtElement.GetMouseMoveObservable().
                               TakeUntil(trackWrtElement.GetLeftMouseUpObservable())
                           select new { D = mDown, P = mDownP, M = mMove };

            if (handleItP)
                trackObs.Subscribe(e =>
                    e.D.EventArgs.Handled = true);

            return from obs in trackObs
                   select new BasedVector
                   (
                       tail: obs.P,
                       head: obs.M.EventArgs.GetPosition(trackWrtElement)
                   );
        }

        private void Draw()
        {
            var controlPoints = new Point[]
            {
                new Point(0D, 0D),
                new Point(0D, 1D),
                new Point(0.5D, 1D),
                new Point(1.5D, 0D),
                new Point(2D, 0D),
                new Point(2D, 1D),
            };

            controlPoints.Run(DrawPoint);

            DownInAndTrackWrtObservable(canvas1, canvas1).Subscribe(bv =>
            {
                CanvasMouseDownTextBox.Text =
                    String.Format("Canvas MouseDown({0:G}, {1:G})", bv.Tail.X, bv.Tail.Y);
                CanvasMouseMoveTextBox.Text =
                    String.Format("Canvas MouseMove({0:G}, {1:G})", bv.Head.X, bv.Head.Y);
            });
        }
    }
}

和XAML

代码语言:javascript
复制
<Window x:Class="WpfTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" mc:Ignorable="d" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" Height="350" Width="522">
    <Grid Height="313" Name="grid1" Width="500" >
        <Canvas Height="237" HorizontalAlignment="Left" Margin="0,75,0,0" Name="canvas1" VerticalAlignment="Top" Width="500" Background="#00000000" />
        <StackPanel Height="52" HorizontalAlignment="Left" Margin="12,12,0,0" Name="stackPanel1" VerticalAlignment="Top" Width="200">
            <TextBox Height="23" Name="CanvasMouseDownTextBox" Width="186" BorderBrush="Blue" />
            <TextBox Height="23" Name="PointMouseDownTextBox" Width="186" BorderBrush="Blue" />
        </StackPanel>
        <StackPanel Height="52" HorizontalAlignment="Left" Margin="211,12,0,0" Name="stackPanel2" VerticalAlignment="Top" Width="200">
            <TextBox Height="23" Name="CanvasMouseMoveTextBox" Width="186" BorderBrush="Blue" />
        </StackPanel>
    </Grid>
</Window>

如有任何建议,将不胜感激。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2010-11-27 11:44:24

尝试使用它来处理hitObs上的事件,将其作为相同可观察链中的一个副作用:

代码语言:javascript
复制
var hitObs = hitTestElement.GetLeftMouseDownObservable().Do(e => e.EventArgs.Handled = handleItP);

还移除if (handleItP),这将取代它。

您尝试的另外两种方法是使用单独的订阅,它可以在其他处理程序已经接收到事件之后抢占您的主订阅或在链中处理得太晚的设置。

票数 1
EN

Stack Overflow用户

发布于 2010-11-27 22:01:15

与其说这是Rx问题,不如说是事件冒泡问题。

如果我正确理解您的问题,我建议基于OriginalSource属性过滤事件,以确保最初引发事件的元素是有问题的元素(而不是子元素)。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/4289315

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档