ドラッグアンドドロップでドラッグ中の状態を表現します。
動作イメージ
- ドラッグ中は点線で表示
- ドロップ時に実線に変更
環境
- 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にもあげました。
感想とお願い
もっと華麗な書き方ができそうな気がします。
ご意見お待ちしております。