LoginSignup
6
7

More than 5 years have passed since last update.

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

Last updated at Posted at 2015-12-24

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

動作イメージ

  1. ドラッグ中は点線で表示
  2. ドロップ時に実線に変更

out.gif

環境

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

作戦

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

実装

マウスダウンイベントで点線を作る

Selectを使って作った線を続くイベントストリームに送ります。

.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;
})

マウスムーブイベントで線の位置を更新

.SelectMany(line => mouseMove.Select(moveEvent =>
{
    var curentPosition = moveEvent.GetPosition(this);
    line.X2 = curentPosition.X;
    line.Y2 = curentPosition.Y;

    return line;
}))

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

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

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();
        }
    }
}
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にもあげました。

感想とお願い

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

参考

Reactive Extensionsを使ったWPFのドラッグアンドドロップをまとめる

6
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
7