Reactive Extensionsを使ったドラッグアンドドロップで途中状態を表示する


ドラッグアンドドロップでドラッグ中の状態を表現します。

# 動作イメージ
1. ドラッグ中は点線で表示
1. ドロップ時に実線に変更

![out.gif](https://qiita-image-store.s3.amazonaws.com/0/15964/62b382c7-061c-c922-9bd9-9bdb2b2fc81a.gif "out.gif")

# 環境

- GUIフレームワークはWPF
- VisualStudio 2015 Community Update1

# 作戦
- マウスダウンイベントで点線を作る
- マウスムーブイベントで線の位置を更新
- マウスアップイベントで実線にする

# 実装

## マウスダウンイベントで点線を作る
Selectを使って作った線を続くイベントストリームに送ります。

```cs
.Select(e =>
{
    var startPosition = e.GetPosition(this);
    var line = new Line();
    line.X1 = startPosition.X;
    line.Y1 = startPosition.Y;
    line.X2 = startPosition.X;
    line.Y2 = startPosition.Y;
    line.Stroke = Brushes.Black;
    line.StrokeDashArray = new DoubleCollection { 2, 2 };
    Canvas.Children.Add(line);

    return line;
})
```

## マウスムーブイベントで線の位置を更新
```cs
.SelectMany(line => mouseMove.Select(moveEvent =>
{
    var curentPosition = moveEvent.GetPosition(this);
    line.X2 = curentPosition.X;
    line.Y2 = curentPosition.Y;

    return line;
}))
```

SelectManyを使うのは、後続のTakeUntilで更新を止めるためです。


## マウスアップイベントで実線にする

```cs
mouseUp
    .Take(1)
    .Do(_ => line.StrokeDashArray = null)
    .Subscribe();
```

TakeUntilはイベントが受け取れません。
Doで本流のイベントストリームに追加しています。
この書き方はイマイチ感があります。

と言うかイベントハンドラーがリークするような、しないような気が・・・

# ソースコード全文

```MainWindow.xaml.cs
using System;
using System.Linq;
using System.Reactive.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;

namespace WpfApplication2
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            var mouseDown = Observable.FromEvent<MouseButtonEventHandler, MouseButtonEventArgs>(
                h => (s, e) => h(e),
                h => MouseDown += h,
                h => MouseDown -= h);

            var mouseMove = Observable.FromEvent<MouseEventHandler, MouseEventArgs>(
                h => (s, e) => h(e),
                h => MouseMove += h,
                h => MouseMove -= h);

            var mouseUp = Observable.FromEvent<MouseButtonEventHandler, MouseButtonEventArgs>(
                h => (s, e) => h(e),
                h => MouseUp += h,
                h => MouseUp -= h);

            var drag = mouseDown
                .Do(e => CaptureMouse())
                .Select(e =>
                {
                    var startPosition = e.GetPosition(this);
                    var line = new Line();
                    line.X1 = startPosition.X;
                    line.Y1 = startPosition.Y;
                    line.X2 = startPosition.X;
                    line.Y2 = startPosition.Y;
                    line.Stroke = Brushes.Black;
                    line.StrokeDashArray = new DoubleCollection { 2, 2 };
                    Canvas.Children.Add(line);

                    return line;
                })
                .Do(line =>
                {
                    mouseUp
                        .Take(1)
                        .Do(_ => line.StrokeDashArray = null)
                        .Subscribe();
                })
                .SelectMany(line => mouseMove.Select(moveEvent =>
                {
                    var curentPosition = moveEvent.GetPosition(this);
                    line.X2 = curentPosition.X;
                    line.Y2 = curentPosition.Y;

                    return line;
                }))
                .TakeUntil(mouseUp.Do(e => ReleaseMouseCapture()))
                .Repeat()
                .Subscribe();
        }
    }
}
```

```cs:MainWindow.xaml
<Window x:Class="WpfApplication2.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:WpfApplication2"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Canvas x:Name="Canvas"/>
</Window>
```

[github](
https://github.com/ledsun/SandBox/tree/master/VisualStudio/WPF_Rx_DandDSample)にもあげました。

# 感想とお願い
もっと華麗な書き方ができそうな気がします。
ご意見お待ちしております。

# 参考
[Reactive Extensionsを使ったWPFのドラッグアンドドロップをまとめる](http://qiita.com/ledsun/items/1ab669ecbea103972e7d)