ドラッグ・アンド・ドロップ(D&D)
概要
D&Dとは,画面上のある要素をつかみ,移動させる方法のこと.本ページではウィンドウズから
WPFアプリケーション,WPFアプリケーションからウィンドウズへのD&Dについては解説せず,WPFアプリケーション内で完結するD&Dについて説明する.
D&Dは,ドラッグされる物とドロップされる物の2つを設定すれば実現可能.
ドラッグされる物
WPFではDragDropクラス(
MSDN
)の静的メソッドDoDragDropを呼び出すだけで良い.XAMLは後述.
+
|
ドラッグの開始 |
private void OnMouseDown(object s, System.Windows.Input.MouseButtonEventArgs e) { var sender = s as TextBlock; DragDrop.DoDragDrop( sender, // ドラッグされる物 sender.Text.Substring(sender.Text.Length-1), // 渡すデータ DragDropEffects.Copy); // D&Dで許可するオペレーション }
|
普通は右クリックとかではドラッグは開始しないのでマウスの状態をチェックする.
+
|
左クリックによるドラッグの開始 |
private void OnMouseDown(object s, System.Windows.Input.MouseButtonEventArgs e) { var sender = s as TextBlock; if( e.LeftButton == MouseButtonState.Pressed && e.RightButton == MouseButtonState.Released && e.MiddleButton == MouseButtonState.Released ) DragDrop.DoDragDrop( sender, // ドラッグされる物 sender.Text.Substring(sender.Text.Length-1), // 渡すデータ DragDropEffects.Copy); // D&Dで許可するオペレーション }
|
ドロップされる物
XAMLでドロップを許可すればよい.Dropイベントが発生する.XAMLは後述.
+
|
ドロップの受け入れ |
private void OnDrop(object s, System.Windows.DragEventArgs e) { MessageBox.Show(e.Data.GetData(typeof(string)) + " is dropped.", "Dropped!"); }
|
XAML
+
|
ドラッグ・アンド・ドロップのサンプル |
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="WpfApplication1.MainWindow" x:Name="Window" Title="MainWindow" Width="640" Height="480"> <Grid x:Name="LayoutRoot"> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" MouseDown="OnMouseDown">Grab me! 1</TextBlock> <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" MouseDown="OnMouseDown" Grid.Column="1">Grab me! 2</TextBlock> <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" AllowDrop="True" Grid.Column="2" Drop="OnDrop">Drop to me!</TextBlock> </Grid> </Window>
|
ユーザへのフィードバック
ドロップ先がドロップを受け入れることができるか,あるいは今ドロップした場合どの操作が行われるかをユーザに示すためにはDragEnterおよびDragOverイベントでEffectsを指定する.
+
|
ドロップエフェクトの指定サンプル |
// 関数名はDragOverだが,DragEnterもこれを呼び出しても動く private void OnDragOver(object sender, System.Windows.DragEventArgs e) { // ドロップされるデータがStringでなければ受け入れない if (!e.Data.GetDataPresent(typeof(System.String))) { e.Effects = DragDropEffects.None; return; } // ドロップされるデータ(String)がintに変換できなければ受け入れない var str = e.Data.GetData(typeof(string)) as string; int tmp; if (int.TryParse(str, out tmp)) e.Effects = DragDropEffects.Copy; else e.Effects = DragDropEffects.None; }
|
より適当なD&D
上記のコードでは,以下のように書き換えるとD&Dできなくなる.
+
|
ボタンのドラッグ・アンド・ドロップ |
<Button VerticalAlignment="Center" HorizontalAlignment="Center" MouseDown="OnMouseDown" Click="OnClick">Grab me! 1</Button> <Button VerticalAlignment="Center" HorizontalAlignment="Center" MouseDown="OnMouseDown" Click="OnClick" Grid.Column="1">Grab me! 2</Button> <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" AllowDrop="True" Grid.Column="2" Drop="OnDrop">Drop to me!</TextBlock>
private void OnClick(object s, System.Windows.RoutedEventArgs e) { var sender = s as Button; var text = sender.Content as string; MessageBox.Show(text.Substring(text.Length-1) + " is clicked.", "Clicked!"); }
|
少し調べるとOnMouseDown関数が呼び出されていないことが分かる(試しにMassageBox.ShowをOnMouseDownに追加してみると良い).これはButtonがClickイベントを実装しているため(
詳しくはここ
).このような場合はPreviewMouseDownを使う.
+
|
PreviewMouseDownによるドラッグアンドドロップ |
<Button VerticalAlignment="Center" HorizontalAlignment="Center" MouseDown="OnMouseDown" Click="OnClick">Grab me! 1</Button> <Button VerticalAlignment="Center" HorizontalAlignment="Center" PreviewMouseDown="OnMouseDown" Click="OnClick" Grid.Column="1">Grab me! 2</Button> <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" AllowDrop="True" Grid.Column="2" Drop="OnDrop">Drop to me!</TextBlock>
|
上のコードでは,1つめのボタンはドラッグできないが,2つめのボタンはできる.ただし,2つめのボタンはClickイベントが発生しない.原因は勿論PreviewMouseDownでD&Dを開始しているため.
これへの対応としては,クリックされたまま一定距離を移動したらドラッグ開始という風にすれば良い.
+
|
PreviewMouseDownによるドラッグアンドドロップ2 |
<Button VerticalAlignment="Center" HorizontalAlignment="Center" Grid.Column="1" PreviewMouseMove="OnMouseMove" PreviewMouseDown="OnMouseDown" Click="OnClick"> Grab me! 2 </Button>
private Point start; private Button downed; private void OnMouseDown(object s, System.Windows.Input.MouseButtonEventArgs e) { downed = s as Button; start = e.GetPosition(downed); } private void OnMouseMove(object s, System.Windows.Input.MouseEventArgs e) { var sender = (Button)s; if (downed != null && downed == sender && e.LeftButton == MouseButtonState.Pressed) { var current = e.GetPosition(sender); if (Math.Abs(current.X - start.X) > SystemParameters.MinimumHorizontalDragDistance || Math.Abs(current.Y - start.Y) > SystemParameters.MinimumVerticalDragDistance) { downed = null; DragDrop.DoDragDrop(sender, sender.Content, DragDropEffects.Copy); } } } private void OnDrop(object s, System.Windows.DragEventArgs e) { MessageBox.Show(e.Data.GetData(typeof(string)) + " is dropped.", "Dropped!"); }
|
これでクリックもドラッグもできるボタンができた.
パネルなどのドラッグ・アンド・ドロップ
パネルを丸ごとD&Dしたい場合には,背景にTransparent(もしくは何らかの色など)を指定しておくこと.指定しない場合は,パネルの子要素の位置でクリックした場合のみD&Dが開始され,それ以外の子要素がない場所でドラッグしようとしてもMouseDownイベントハンドラが呼び出されない.