Shoeisha Technology Media

CodeZine(コードジン)

特集ページ一覧

ドラッグ&ドロップで操作できるシフト表作成WPFアプリケーションを作る

ComponentOne Studioを活用したWindowsフォームアプリケーションの作成

  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加
2013/12/25 14:00

 「ComponentOne Studio 2013J」に含まれるDragDropManager for WPFのC1DragDropManagerコンポーネントを使って、ドラッグ操作が使えるシフト勤務の管理アプリケーションを作ってみます。

はじめに

 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 本体アセンブリ
「C1.WPF.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の論理値を指定します。

Visual Basic
Imports C1.WPF
Class MainWindow
    Public Sub New()

        ' この呼び出しはデザイナーで必要です。
        InitializeComponent()

        ' InitializeComponent() 呼び出しの後で初期化を追加します。

        'ドラッグドロップ マネージャの作成
        Dim ddmgr As New C1DragDropManager()
        ddmgr.RegisterDropTarget(Grid1, True)
C#
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」を指定します。

DragDropEffect列挙体のメンバ
メンバ名 説明
None アクションなし
Move ドラッグ元要素をドラッグ先要素に移動します
Copy ドラッグ元要素をドラッグ先要素にコピーします
Visual Basic
For Each e As UIElement In Grid1.Children
    ddmgr.RegisterDragSource(e, DragDropEffect.Move, ModifierKeys.None)
Next
C#
foreach (UIElement e in Grid1.Children)
{
    ddmng.RegisterDragSource(e, DragDropEffect.Move, ModifierKeys.None);
}

 ③ そして、C1DragDropManagerクラスのDragDropイベントに、マウス座標をグリッドの座標に変換するイベントハンドラ「Do_DragDrop」を連結します。

Visual Basic
    AddHandler ddmgr.DragDrop, AddressOf Do_DragDrop
End Sub
C#
    ddmng.DragDrop += Do_DragDrop;
}

座標変換用イベントハンドラ「Do_DragDrop」の作成

 C1DragDropManagerコンポーネントの設定が終わったら、マウス座標をグリッドの座標に変換するイベントハンドラ「Do_DragDrop」を作成します。

 DragDropイベントは、ドラッグ&ドロップ処理の最後に、ユーザーが登録されたドロップ先上でマウスボタンを放したときに発生します。

 そこで、グリッドのどの位置にドロップされたのかをマウスの位置で判断し、そこにテキストブロックを移動させます。

 ① まずマウスボタンがクリックされた位置を取得します。そして、その値からグリッドの行列番号を導き出します。

 これは、グリッドの行列の高さと幅を計算して割り出します。

Visual Basic
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
C#
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要素の子要素として依存関係にあるので、グリッドの行列番号を指定することでその位置のグリッドにテキストブロックが移動するようになります。

Visual Basic
' 要素を新しい位置に移動
e.DragSource.SetValue(Grid.RowProperty, row)
e.DragSource.SetValue(Grid.ColumnProperty, col)
C#
// 要素を新しい位置に移動
e.DragSource.SetValue(Grid.RowProperty, row);
e.DragSource.SetValue(Grid.ColumnProperty, col);

 以上で出来上がりです。

まとめ

 ドラッグ&ドロップ機能は、使い方によってはユーザーの操作性を高めてくれる便利な機能です。特にWPFアプリケーションでは、タブレット端末も視野に入れてアプリケーションを開発することもあります。アプリケーションの機能の中にドラッグ&ドロップを組み込むのであれば、C1DragDropManagerコンポーネントはきっと役に立つでしょう。

  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

  • 瀬戸 遥(セト ハルカ)

    8ビットコンピュータの時代からBASICを使い、C言語を独習で学びWindows 3.1のフリーソフトを作成、NiftyServeのフォーラムなどで配布。Excel VBAとVisual Basic関連の解説書を中心に現在まで40冊以上の書籍を出版。近著に、「ExcelユーザーのためのAccess再...

All contents copyright © 2005-2019 Shoeisha Co., Ltd. All rights reserved. ver.1.5