现在的位置: 首页 > 综合 > 正文

Reactive Extensions (Rx) 拖拽实例

2013年08月21日 ⁄ 综合 ⁄ 共 2399字 ⁄ 字号 评论关闭
Rx 最大的特点就是可以对事件进行组合形成一个新的事件。比如下面的这个拖拽。我们对于拖拽的定义是:当鼠标按下时开始DragDrop,当鼠标移动时移动图形,最后当鼠标放开时停止DragDrop。

我定义了一些图形放在ToolBar里,点击ToolBar里的图形将在Canvas里创建新的图形,在Canvas里可以拖拽它们移动。

using System;
using System.Reactive.Linq;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;

namespace RxDragDownSample
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void btnCircular_Click_1(object sender, RoutedEventArgs e)
        {
            AddShape<Shapes.Circular>();
        }

        private void btnSquare_Click_1(object sender, RoutedEventArgs e)
        {
            AddShape<Shapes.Square>();
        }

        private void btnTriangle_Click_1(object sender, RoutedEventArgs e)
        {
            AddShape<Shapes.Triangle>();
        }

        private void AddShape<T>() where T : new()
        {
            var shape = (new T()) as UserControl;
            myCanvas.Children.Add(shape);
            Canvas.SetLeft(shape, 10);
            Canvas.SetTop(shape, 10);

            var minX = 0;
            var maxX = myCanvas.ActualWidth - 30;
            var minY = 0;
            var maxY = myCanvas.ActualHeight - 30;

            // 鼠标在Shape上按下,开始DragDrop
            var mouseDown = from evt in Observable.FromEventPattern<MouseButtonEventArgs>(shape, "MouseLeftButtonDown")
                            select evt.EventArgs.GetPosition(this);
            // 鼠标移动,取得坐标
            var mouseMove = from evt in Observable.FromEventPattern<MouseEventArgs>(this, "MouseMove")
                            select evt.EventArgs.GetPosition(this);
            // 鼠标放开,终止DragDrop
            var mouseUp = from evt in Observable.FromEventPattern<MouseButtonEventArgs>(this, "MouseLeftButtonUp")
                          select evt.EventArgs.GetPosition(this);     
            // 当鼠标移出Window,终止DragDrop
            var mouseLeave = from evt in Observable.FromEventPattern<MouseEventArgs>(this, "MouseLeave")
                             select evt;
            var mouseMoves = mouseMove.Skip(1).Zip(mouseMove, (prev, cur) =>
                               new { X = prev.X - cur.X, Y = prev.Y - cur.Y });
            var dragDrop = mouseDown.SelectMany(mouseMoves.TakeUntil(mouseUp).TakeUntil(mouseLeave));

            dragDrop.ObserveOn(SynchronizationContext.Current).Subscribe(p =>
            {
                var x = Math.Min(Math.Max(Canvas.GetLeft(shape) + p.X, minX), maxX);
                var y = Math.Min(Math.Max(Canvas.GetTop(shape) + p.Y, minY), maxY);
                Canvas.SetLeft(shape, x);
                Canvas.SetTop(shape, y);
                this.lblPosition.Content = "{x:" + x.ToString() + ",y:" + y.ToString() + "}";
            });
        }
    }
}

利用 Observable.FromEventPattern<T> 我们将"MouseLeftButtonDown", "MouseMove", "MouseLeftButtonUp", "MouseLeave" 事件先转化成 IObervable<T> 的Rx对象。
mouseMoves则是对连续的 mouseMove 进行了Zip 获得鼠标位移(offset)

var mouseMoves = mouseMove.Skip(1).Zip(mouseMove, (prev, cur) =>
                               new { X = prev.X - cur.X, Y = prev.Y - cur.Y });

如图:

最后 DragDrop 事件:

var dragDrop = mouseDown.SelectMany(mouseMoves.TakeUntil(mouseUp).TakeUntil(mouseLeave));

【源码下载】

抱歉!评论已关闭.