はじめに
DragDropManager for WPFのC1DragDropManagerコンポーネントは、ウィンドウ上のコントロールにドラッグ&ドロップ操作を簡単に追加できるコントロールです。このコンポーネントを使うと、アプリケーション実行時にユーザーがウィンドウ内のレイアウトを自由に操作できるようになります。
そこで今回は、このC1DragDropManagerコンポーネントを使い、シフト勤務のアサイン表をドラッグ操作で簡単に編集できるアプリケーションを作ってみました。
対象読者
Visual Basic 2010/2012、またはVisual C# 2010/2012を使ってプログラムを作ったことがある人。
必要な環境
Visual Basic 2010/2012、Visual C# 2010/2012、Visual Studio 2010/2012、SQL Server Expressでプログラムが作れる環境。
なお、本プログラムは次の環境で開発・動作確認を行っています。
- OS:Windows 7
- 開発Tool:Visual Studio 2010、.NET Framework 4
プログラム実行時の注意事項
本稿の実行ファイル(バイナリファイル)を動かすには、zipファイルに同梱してある以下のファイルが必要になります(.NET Framework 4でのみご使用いただけます)。
ファイル名 | 説明 |
---|---|
C1.WPF.4 | 本体アセンブリ |
このファイルを、実行プログラムと同じフォルダに格納します。
コンポーネントのインストール
トライアル版は、グレープシティのWebページから申し込みできます。
トライアル申込フォームが表示されますので、必要情報を入力して申し込むとトライアル版のダウンロード手順を記載したE-Mailが送られてきます。その手順にそってダウンロードを行ってください。また、ダウンロードファイルは圧縮ファイルになっていますので、解凍してインストーラを起動します。
制限事項などの詳細については、インストーラに同梱されているリリースノートを参照ください。
参照の追加
ComponentOne Studio 2013Jをインストールしたら、プロジェクトに参照を追加します。追加する参照は「C1.WPF.4」です。.NET Framework 4が必要です。
ファイル | 内容 |
---|---|
C1.WPF.4 | 本体アセンブリ |
C1DragDropManagerコンポーネントの概要
前述したとおり、DragDropManager for WPFのC1DragDropManagerコンポーネントは、WPFアプリケーションにドラッグ&ドロップ操作機能を簡単に追加できるコンポーネントです。
ドラッグ&ドロップサービスを提供するクラスなので、ウィンドウに表示されず機能の提供だけを行います。
C1DragDropManagerクラスは、Windowsフォームのドラッグ&ドロップ実装のパターンに従い、ドラッグ&ドロップ操作を開始するために呼び出されるDoDragDropメソッドを提供し、プロセス全体をカスタマイズするためのイベント(DragStart、DragEnter、DragOver、DragLeave、DragDrop)を発生させます。
このイベントのパラメータには、ドラッグされる項目(e.DragSource)やドロップされる場所(e.DropTarget)に関する情報が格納されます。
C1DragDropManagerクラスには、プロセス全体を簡略化する2つのヘルパーメソッドがあります。それは、RegisterDragSourceとRegisterDropTargetです。これらのメソッドは、ほとんどの一般的なドラッグ&ドロップのシナリオを少ないコードで記述できる機能を提供します。これらのメソッドで要素をソースやターゲットとして登録すると、C1DragDropManagerは、マウスを監視し、ドラッグ&ドロップ操作を管理します。
そのため、プログラマに必要な作業は移動またはコピーの操作を実行するためにDragDropイベントを処理するだけです。
C1DragDropManagerコンポーネントの特徴
このC1DragDropManagerコンポーネントは、次のような特徴を持っています。
ドラッグ&ドロップ動作の制御
C1DragDropManagerコンポーネントには、ドラッグ&ドロップ処理全体を制御できるさまざまなメソッドやイベントが用意されています。いくつかの要素をドラッグ元およびドロップ先として登録してから、要素を新しい場所に移動またはコピーするためにDragDropイベントを処理するだけで、アプリケーションにドラッグ&ドロップ機能を実装できます。
ドラッグマーカーのカスタマイズ
C1DragDropManagerコンポーネントは、ドラッグ元とドロップ先のドラッグマーカーの外観をカスタマイズするためのプロパティを公開します。これにより、操作全体が見栄えよく、ユーザーフレンドリーになります。
スクロールのサポート
スクロール可能なターゲットの端の近くにオブジェクトをドラッグすると、ターゲットが自動的にスクロールされるので、エンドユーザーは、要素を1回の操作で目的の場所にドロップできます。
C1DragDropManagerクラスのメンバ
C1DragDropManagerコンポーネントを使ってドラッグ&ドロップ機能を実装するには、C1DragDropManagerクラスの以下の主要なメンバとイベントを使用します。
プロパティ | 説明 |
---|---|
AutoScroll |
C1DragDropManagerコンポーネントがドロップ先を含むScrollViewerを 自動的にスクロールするかどうかを取得または設定 |
AutoScrollDelay | 自動スクロールステップの時間間隔をミリ秒単位で取得または設定 |
AutoScrollEdge |
マウスがドラッグ先要素の端にどの距離まで近付いたら 自動スクロール処理を開始するかを取得または設定 |
AutoScrollStep | 自動スクロールステップごとにスクロールするピクセル数を取得または設定 |
Canvas | ドラッグ&ドロップ処理の表示に使用されるキャンバスへの参照を取得 |
DragThreshold |
マウスがどれだけ移動したらドラッグ操作を開始するかを ピクセル単位の距離で取得または設定 |
SourceMarker | ドラッグ元の強調表示に使用される境界線への参照を取得 |
TargetMarker | ドロップ場所を示すために使用される境界線を取得 |
メソッド | 説明 |
---|---|
ClearSources | 登録されているすべてのドラッグ元を削除 |
ClearTargets | 登録されているすべてのドロップ先を削除 |
DoDragDrop |
UIElementをドラッグ元として使用するドラッグ&ドロップ操作を開始し、 指定されたDragDropEffectをサポート |
RegisterDragSource | ドラッグ元として機能するUIElementを登録 |
RegisterDropTarget | 要素をドロップ先として登録(または登録解除) |
イベント | 説明 |
---|---|
DragAutoScroll | ドロップ場所をビュー内に留めておくためにC1DragDropManagerコンポーネントがScrollViewerを自動的にスクロールした後で発生 |
DragDrop | ドラッグ&ドロップ処理の最後に、ユーザーが登録されたドロップ先上でマウスボタンを放したときに発生 |
DragEnter | ドラッグ&ドロップ処理中に、ポインタが登録されたドロップ先に入ったときに発生 |
DragLeave | ドラッグ&ドロップ処理中に、ポインタが登録されたドロップ先から出たときに発生 |
DragOver | ドラッグ&ドロップ処理中に、ポインタが登録されたドロップ先上に移動したときに発生 |
DragStart | ドラッグ&ドロップ処理が開始されたときに発生 |
GUIの作成
では、さっそくアプリケーションを作成していきます。
今回作成するのは、グリッドに配置したテキストブロックをマウスでドラッグして移動できるようにしたアプリケーションです。この機能を実現するために、C1DragDropManagerコンポーネントを使用します。
ウィンドウには8列6行のグリッドを作成し、そこにテキストブロックを6つ配置します。機能の実行はビハインドコードで作成します。
グリッドの作成
では、GUIを作成していきます。
① グリッドを8列6行にします。そして、最初の列の列幅と1行目の高さを次のように指定します。また、「Grid1」という名前を付けます。
<Grid x:Name="Grid1" Background="#FFCDE6FA" ShowGridLines="True" Width="400" Height="300" > <Grid.RowDefinitions> <RowDefinition Height="30"/> <RowDefinition/> <RowDefinition/> <RowDefinition/> <RowDefinition/> <RowDefinition/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="57*" /> <ColumnDefinition Width="57*" /> <ColumnDefinition Width="57*" /> <ColumnDefinition Width="57*" /> <ColumnDefinition Width="57*" /> <ColumnDefinition Width="57*" /> <ColumnDefinition Width="57*" /> <ColumnDefinition Width="57*" /> </Grid.ColumnDefinitions>
② テキストブロックを使って、曜日名と時間を設定します。
<!-- 曜日 --> <TextBlock Height="23" HorizontalAlignment="Left" Margin="23,12,0,0" Text="月" VerticalAlignment="Top" Grid.Column="1" Grid.RowSpan="2" /> <TextBlock Height="23" HorizontalAlignment="Left" Margin="22,12,0,0" Text="火" VerticalAlignment="Top" Grid.Column="2" Grid.RowSpan="2" /> <TextBlock Height="23" HorizontalAlignment="Left" Margin="24,12,0,0" Text="水" VerticalAlignment="Top" Grid.Column="3" Grid.RowSpan="2" /> <TextBlock Height="23" HorizontalAlignment="Left" Margin="27,12,0,0" Text="木" VerticalAlignment="Top" Grid.Column="4" Grid.RowSpan="2" /> <TextBlock Height="23" HorizontalAlignment="Left" Margin="25,12,0,0" Text="金" VerticalAlignment="Top" Grid.Column="5" Grid.RowSpan="2" /> <TextBlock Height="23" HorizontalAlignment="Left" Margin="24,12,0,0" Text="土" VerticalAlignment="Top" Grid.Column="6" Grid.RowSpan="2" /> <TextBlock Height="23" HorizontalAlignment="Left" Margin="23,12,0,0" Text="日" VerticalAlignment="Top" Grid.Column="7" Grid.RowSpan="2" /> <!-- 時間 --> <TextBlock Height="23" HorizontalAlignment="Left" Margin="12,17,0,0" Text="09:00" VerticalAlignment="Top" Grid.Row="1" /> <TextBlock Height="23" HorizontalAlignment="Left" Margin="12,16,0,0" Text="11:00" VerticalAlignment="Top" Grid.Row="2" /> <TextBlock Height="23" HorizontalAlignment="Left" Margin="12,16,0,0" Text="13:00" VerticalAlignment="Top" Grid.Row="3" /> <TextBlock Height="23" HorizontalAlignment="Left" Margin="12,16,0,0" Text="15:00" VerticalAlignment="Top" Grid.Row="4" /> <TextBlock Height="23" HorizontalAlignment="Left" Margin="12,16,0,0" Text="17:00" VerticalAlignment="Top" Grid.Row="5" />
③ さらにテキストブロックを配置し、名前と背景色を設定します。
<TextBlock Text="加藤" FontSize="14" Grid.Row="1" Grid.Column="2" Background="Aqua"/> <TextBlock Text="清水" FontSize="14" Grid.Row="3" Grid.Column="1" Background="PaleGreen"/> <TextBlock Text="山本" FontSize="14" Grid.Row="4" Grid.Column="6" Background="LightCoral"/> <TextBlock Text="阿部" FontSize="14" Grid.Row="6" Grid.Column="2" Background="#FFE5AA64" /> <TextBlock Text="木下" FontSize="14" Grid.Row="3" Grid.Column="3" Background="LightYellow" /> <TextBlock Text="長谷川" FontSize="14" Grid.Row="2" Grid.Column="5" Background="#FF98A7FB" /> <TextBlock Grid.ColumnSpan="4" Height="21" HorizontalAlignment="Left" Margin="-3,-31,0,0" Name="TextBlock1" Text="今週のシフト設定" VerticalAlignment="Top" Width="181" /> </Grid>
④ 最後にラベルで表題を付けて出来上がりです。
ドラッグ処理の作成
実際にテキストブロックをドラッグできるようにする処理は、ビハインドコードで作成します。
テキストブロックのドラッグ処理
テキストブロックのドラッグ処理は、C1DragDropManagerクラスのRegisterDropTargetメソッドを呼び出して、グリッドがドロップ先になるように指定します。
次に、RegisterDragSourceメソッドを使用してドラッグ元にテキストブロックを指定します。
そして、マウス座標をグリッドの座標に変換するイベントハンドラを作成し、DragDropイベントに連結します。
これで、テキストブロックをドラッグすると、グリッドの新しい位置に移動できるようになります。
① 最初に、C1DragDropManagerクラスのインスタンスを作成します。
そして、RegisterDropTargetメソッドの引数にGrid要素を指定し、グリッドをドロップ先になるように指定します。
RegisterDropTargetメソッドは2つの引数を持ち、一つはドロップ先として機能するUIElementオブジェクト、もう一つは要素を登録する場合はtrue、登録解除する場合はfalseの論理値を指定します。
Imports C1.WPF Class MainWindow Public Sub New() ' この呼び出しはデザイナーで必要です。 InitializeComponent() ' InitializeComponent() 呼び出しの後で初期化を追加します。 'ドラッグドロップ マネージャの作成 Dim ddmgr As New C1DragDropManager() ddmgr.RegisterDropTarget(Grid1, True)
using C1.WPF; namespace DragDropMNG_WPF_cs { /// <summary> /// MainWindow.xaml の相互作用ロジック /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); C1DragDropManager ddmng = new C1DragDropManager(); ddmng.RegisterDropTarget(Grid1, true);
② 続いて、RegisterDragSourceメソッドを使用してテキストブロックをドラッグ対象のオブジェクトとして指定します。グリッド上にいくつテキストブロックがあってもドラッグ対象に設定できるように、グリッド内のオブジェクトを一つ一つサーチしドラッグ対象として設定していきます。
RegisterDragSourceメソッドは3つの引数を持ち、1つはドラッグ対象のUIElementオブジェクト、もう一つはドラッグ元をドロップ先要素にドロップしたときに実行されるアクションが、移動なのかコピーなのかを表すをDragDropEffect列挙体のメンバで指定します。
3つ目の引数は、どの修飾子キー(Shiftキー、CtrlキーおよびAltキー)が押された状態であるかを示す値を格納しているModifierKeysプロパティ(Controlクラスのメンバ値はKeys列挙体メンバ)を指定しますが、このメソッドではデフォルトの「None」を指定します。
メンバ名 | 説明 |
---|---|
None | アクションなし |
Move | ドラッグ元要素をドラッグ先要素に移動します |
Copy | ドラッグ元要素をドラッグ先要素にコピーします |
For Each e As UIElement In Grid1.Children ddmgr.RegisterDragSource(e, DragDropEffect.Move, ModifierKeys.None) Next
foreach (UIElement e in Grid1.Children) { ddmng.RegisterDragSource(e, DragDropEffect.Move, ModifierKeys.None); }
③ そして、C1DragDropManagerクラスのDragDropイベントに、マウス座標をグリッドの座標に変換するイベントハンドラ「Do_DragDrop」を連結します。
AddHandler ddmgr.DragDrop, AddressOf Do_DragDrop End Sub
ddmng.DragDrop += Do_DragDrop; }
座標変換用イベントハンドラ「Do_DragDrop」の作成
C1DragDropManagerコンポーネントの設定が終わったら、マウス座標をグリッドの座標に変換するイベントハンドラ「Do_DragDrop」を作成します。
DragDropイベントは、ドラッグ&ドロップ処理の最後に、ユーザーが登録されたドロップ先上でマウスボタンを放したときに発生します。
そこで、グリッドのどの位置にドロップされたのかをマウスの位置で判断し、そこにテキストブロックを移動させます。
① まずマウスボタンがクリックされた位置を取得します。そして、その値からグリッドの行列番号を導き出します。
これは、グリッドの行列の高さと幅を計算して割り出します。
Private Sub Do_DragDrop(source As Object, e As DragDropEventArgs) ' マウスの位置を取得 Dim MousePt As Point = e.GetPosition(Grid1) ' グリッド行/列座標に変換 Dim row As Integer, col As Integer Dim GridPt As New Point(0, 0) For row = 0 To Grid1.RowDefinitions.Count - 1 GridPt.Y += Grid1.RowDefinitions(row).ActualHeight If GridPt.Y > MousePt.Y Then Exit For End If Next For col = 0 To Grid1.ColumnDefinitions.Count - 1 GridPt.X += Grid1.ColumnDefinitions(col).ActualWidth If GridPt.X > MousePt.X Then Exit For End If Next
private void Do_DragDrop(object source, DragDropEventArgs e) { // マウスの位置を取得 Point MousePt = e.GetPosition(Grid1); // グリッド行/列座標に変換 int row, col; Point GridPt = new Point(0, 0); for (row = 0; row < Grid1.RowDefinitions.Count; row++) { GridPt.Y += Grid1.RowDefinitions[row].ActualHeight; if (GridPt.Y > MousePt.Y) break; } for (col = 0; col < Grid1.ColumnDefinitions.Count; col++) { GridPt.X += Grid1.ColumnDefinitions[col].ActualWidth; if (GridPt.X > MousePt.X) break; }
② ドロップ先のグリッドの位置が決まったら、テキストブロックを移動させます。
まず、イベントハンドラの引数「DragDropEventArgs」クラスのDragSourceプロパティでドラッグ元のUIElementオブジェクトを取得します。この場合はテキストブロックなので、TextBlockクラスのSetValueメソッドを使用して移動させます。
SetValueメソッドは依存関係プロパティ識別子を指定して、依存関係プロパティのローカル値を設定するメソッドです。この場合、TextBlock要素はGrid要素の子要素として依存関係にあるので、グリッドの行列番号を指定することでその位置のグリッドにテキストブロックが移動するようになります。
' 要素を新しい位置に移動 e.DragSource.SetValue(Grid.RowProperty, row) e.DragSource.SetValue(Grid.ColumnProperty, col)
// 要素を新しい位置に移動 e.DragSource.SetValue(Grid.RowProperty, row); e.DragSource.SetValue(Grid.ColumnProperty, col);
以上で出来上がりです。
まとめ
ドラッグ&ドロップ機能は、使い方によってはユーザーの操作性を高めてくれる便利な機能です。特にWPFアプリケーションでは、タブレット端末も視野に入れてアプリケーションを開発することもあります。アプリケーションの機能の中にドラッグ&ドロップを組み込むのであれば、C1DragDropManagerコンポーネントはきっと役に立つでしょう。