【NetOffice】【WPF】ドラッグアンドドロップでExcelを起動する。

  • 2012.12.19 Wednesday
  • 21:09
JUGEMテーマ:コンピュータ


Source and Project

WPFアプリケーションの所定の箇所にExcelファイルをドラッグアンドドロップすることで、Excelアプリケーションを起動し、ブックを開く方法を紹介します。

サンプルコード。

<Window x:Class="Art55.DropDwonExcelDemo20121219_001.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
       
        <Label Grid.Row="1" Grid.Column="1"
               Width="100" Height="100"
               Background="CornflowerBlue"
               AllowDrop="True"
               HorizontalContentAlignment="Center"
               VerticalContentAlignment="Center"
               Drop="OnDrop">Drop Excel</Label>
    </Grid>
</Window>

今回はラベルコントロールにExcelファイルをドロップできるようにします。まずは、Label.AllowDropの属性をtrueにすることで、ドロップできるようになります。また、ドロップ時のハンドリングを行うために、Dropイベントを設定します。

Dropイベントのハンドラーは以下の通り、

using System.Collections.Generic;
using System.Linq;
using System.Windows;
using Excel = NetOffice.ExcelApi;
namespace Art55.DropDwonExcelDemo20121219_001
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow
    {
        public MainWindow()
        {
            InitializeComponent();
        }
        private void OnDrop(object sender, DragEventArgs e)
        {
            using (var application = new Excel.Application { Visible = true })
            {
                ((e.Data.GetData(DataFormats.FileDrop) as string[]) ?? Enumerable.Empty<string>())
                    .Where(System.IO.File.Exists)
                    .Where(file => ExcelExtensionSet.Contains(System.IO.Path.GetExtension(file) ?? string.Empty))
                    .Distinct()
                    .ToList()
                    .ForEach(file => application.Workbooks.Add(file));
            }
        }
        private static readonly HashSet<string> ExcelExtensionSet = new HashSet<string>(new[] { ".xls", ".xlsx" });
    }
}

DragEventArgsからDataFormats.Filedropを指定してGetDataメソッドを呼び出すと、ドロップしたファイルのパスがstringの配列で取得できます。今回は、ファイルが存在する事と拡張子が.xlsまたはxlsxになっているかどうかで、そのファイルがExcelファイルであるかどうかの判定を行っています。Excelファイルであると判定されたファイルはExcelブックとして開きます。

今回は、ブックを開くだけにとどめていますが、シートのデータを読み取ったり、シートに何か書き込んだり、チェックしたり応用技は色々ありそうです。また、ドラッグアンドドロップだけではなく、起動中のExcelにアタッチして何かすると言うこともできます。

Source and Project

【WPF】TreeView内のTreeViewItemにぶら下がる要素からTreeViewItemの親のTreeViewItemを見つける方法

  • 2012.08.09 Thursday
  • 23:29
JUGEMテーマ:コンピュータ


        private TreeViewItem GetItemsControlFromItemContainer(TreeView treeView, DependencyObject buttomControl)
        {
            return ItemsControl.ItemsControlFromItemContainer(GetCetontainerFromElement(treeView, buttomControl)) as TreeViewItem;
        }

        private TreeViewItem GetCetontainerFromElement(TreeView treeView, DependencyObject buttomControl)
        {
            return GetGCetontainerFromElementList(treeView, buttomControl).LastOrDefault() as TreeViewItem;
        }

        private IEnumerable<ItemsControl> GetGCetontainerFromElementList(ItemsControl topItemsControl, DependencyObject buttomControl)
        {
            ItemsControl tmp = topItemsControl;
            while (tmp != null)
            {
                tmp = ItemsControl.ContainerFromElement(tmp, buttomControl) as ItemsControl;
                if (tmp != null)
                {
                    yield return tmp;
                }
            }
        }

たとえばクリックしたTreeViewItem内のTextBlockなどを指定して、トップにあるTreeViewとTextBlockを渡すと。TextBlockの親に存在するTreeViewItemを見つけ出し、さらにその親のTreeViewItemを見つけるという振る舞いをする。

---------------------
(追記)
GetGCetontainerFromElementListでとってきたシーケンスの後ろから2番目の奴をとってくれば良いだけだった・・・・

【WPF】TreeView内のTreeViewItemにぶら下がる要素からTreeViewItemを見つける方法

  • 2012.08.09 Thursday
  • 22:58
JUGEMテーマ:コンピュータ

 
        private TreeViewItem GetCetontainerFromElement(TreeView treeView, DependencyObject buttomControl)
        {
            return GetGCetontainerFromElementList(treeView, buttomControl).LastOrDefault() as TreeViewItem;
        }

        private IEnumerable<ItemsControl> GetGCetontainerFromElementList(ItemsControl topItemsControl, DependencyObject buttomControl)
        {
            ItemsControl tmp = topItemsControl;
            while (tmp != null)
            {
                tmp = ItemsControl.ContainerFromElement(tmp, buttomControl) as ItemsControl;
                if (tmp != null)
                {
                    yield return tmp;
                }
            }
        }

参考
http://pro.art55.jp/?eid=1033030

VisualTreeHelperを利用して探す方法は
http://pro.art55.jp/?eid=1303998
のサンプルコードに記述してます。

【WPF】TreeViewのアイテムをドラッグアンドドロップをするには(5)TreeView.ItemsSourceに代入するオブジェクトを考えなおす。

  • 2012.08.07 Tuesday
  • 21:09
JUGEMテーマ:コンピュータ
 
public class TreeViewNode<T> : ObservableCollection<TreeViewNode<T>>
{
    public T Value { get; set; }
}

こんなクラス書いてみたけど、よくよく考えてみると、親と子で同じクラスというのは、
意外にまれなケースで実際には違うことが多いのかなって思った。

T自体を自前で用意するなら、共通のインターフェイスや抽象クラスを用意することで問題は回避できるけど
.NET Frameworkにあるクラスやらそういうのがあると、ちと話がややこしくなってくるね。



基本的には"Value"というプロパティ名を保障しておけば、ダックタイピング的にはOKなはずなので、そういう方向で修正できないかちとかんがえてみよ。今日は眠いでおしまい。

【WPF】TreeViewのアイテムをドラッグアンドドロップをするには(5)TreeView.ItemsSourceに代入するオブジェクトを考える。

  • 2012.08.05 Sunday
  • 19:20


なかなかドラッグアンドドロップにたどり着けない訳ですが、今回もドラッグアンドドロップとは関係ありません。それ以前の問題です。前々回あたりに書いたとおり、私はさくっとサンプルコードがかけるレベルではなく、TreeViewには不慣れです。なので今回もTreeViewの基礎的な事を知ったかぶりさせていただきます。

ということで、今回は、ItemsControl系のコントロールでの定石であるItemsSource経由でのデータ表示について考えたいと思います。ただ、TreeViewは、ListBox、ListView、DataGridなどと違い階層に構造になっており、ItemsSourceに代入するオブジェクトも階層構造になっている必要がります。そのあたりがちょっぴり難易度があがってしまう原因になってしまいます。ポイントを簡単にまとめると下記のことを開発者はしなければなりません。

1. TreeView.ItemsSourceプロパティに代入するクラスを自作する。
2. TreeView.ItemTemplateプロパティに代入するDateTemplateを自作する。

まずは、ItemsSourceプロパティに代入するオブジェクトの元となるクラスを、さくっと一番(かどうかわかりませんが)シンプルなかたちで、クラスを作ってみました

    public class TreeViewNode<T>
    {
        public TreeViewNode() 
            : this(default(T), Enumerable.Empty<TreeViewNode<T>>().ToList()) 
        {}

        public TreeViewNode(T value) 
            : this(value, Enumerable.Empty<TreeViewNode<T>>().ToList()) 
        {}

        public TreeViewNode(T value, List<TreeViewNode<T>> children)
        {
            Value = value;
            Children = children;
        }

        public T Value { get; set; }
        public List<TreeViewNode<T>> Children { get; private set; }
    }

TreeViewNode<T>クラスはヘッダー情報と、それに付随するコンテンツのコレクションを持つクラスとなります。子供のコレクションが追加や削除が起こる場合は、変更通知をサポートするObservableCollection<TreeViewNode<T>>にしておくと良いでしょう。ドラッグアンドドロップをサポートする場合は、コレクションの変更通知は必須となるので、注意が必要です。または、コレクション自体を入れ替えて、INotifyPropertyChanged経由で変更通知するのもありかもしれませんが・・・Childrenがnullである状態を許すようなクラスは個人的にはお勧めしません(笑)

上記のクラスは、使ってみると分かると思いますが、非常に使いにくいです。一つはトップレベルのコレクションをどうするかという問題が発生します。また、Addがサポートされていないため、サンプルコードなどを記述したい場合、ツリーを構築するのが面倒になります。ということで、ちょっと改造してみます。

    public class TreeViewNode<T> : IEnumerable<TreeViewNode<T>>, IListSource
    {
        public TreeViewNode() : this(default(T), Enumerable.Empty<TreeViewNode<T>>().ToList()) { }
        public TreeViewNode(T value) : this(value, Enumerable.Empty<TreeViewNode<T>>().ToList()) {}
        public TreeViewNode(T value, List<TreeViewNode<T>> children)
        {
            Value = value;
            Children = children;
        }

        public T Value { get; set; }
        public List<TreeViewNode<T>> Children { get; private set; }

        public void Add(TreeViewNode<T> child)
        {
            Children.Add(child);
        }

        public IEnumerator<TreeViewNode<T>> GetEnumerator()
        {
            return Children.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public IList GetList()
        {
            return Children;
        }

        public bool ContainsListCollection
        {
            get { return Children != null; }
        }
    }

IListSourceを継承することにより、トップレベルからTreeViewNodeを利用できようにしてみました。WPFではIListSource継承することにより、コレクションクラスでないものに対してもBindingオブジェクトを通じて、それをコレクションと見なすような挙動をしてしてくれるようになります。もう一つがIEnumerable<TreeViewNode<T>>を継承させる事です。IEnumerable<TreeViewNode<T>>を継承させて、Addメソッドをサポートしたいので、継承させてみました。(追記:IListSourceを継承せず、IEnumerable<TreeViewNode<T>>のみを継承させた場合、コレクションがIEnumerable<TreeViewNode<T>>で実現できることしか実現できなくなってしまいます。たとえばObservableCollection<TreeViewNode<T>>がコレクションの本体であった場合でもコレクションの変更通知がサポートされなくなってしまいます。)

実際に使ってみると、サンプルデータを作るにはとっても便利になります(笑)
IListSourceをサポートしているためトップのコレクションもTreeViewNodeで記述できます。またAddメソッドがサポートされているため、外延表現でリストの初期化が可能となります。

var source = new TreeViewNode<string>
             {
                 new TreeViewNode<string>("大分類A")
                     {
                        new TreeViewNode<string>("小分類A")
                            {
                                new TreeViewNode<string>("項目1"),
                                new TreeViewNode<string>("項目2"),
                                new TreeViewNode<string>("項目3"),
                            },
                        new TreeViewNode<string>("小分類B")
                            {
                                new TreeViewNode<string>("項目1"),
                                new TreeViewNode<string>("項目2"),
                                new TreeViewNode<string>("項目3"),
                            }
                     },
                     new TreeViewNode<string>("大分類B")
                     {
                        new TreeViewNode<string>("小分類A")
                            {
                                new TreeViewNode<string>("項目1"),
                                new TreeViewNode<string>("項目2"),
                            },
                        new TreeViewNode<string>("小分類C")
                            {
                                new TreeViewNode<string>("項目1"),
                            }
                     }
             };
treeView.DataContext = source;

さらにサンプルデータを記述するという観点だけで話を広げると、上記のTreeViewNode<T>クラスをXAMLで記述しインスタンスを作成する事もできます。たとえば下記のように記述できます。

public class TreeViewNode : TreeViewNode<string>
{}

<Window x:Class="Art55.TreeViewDemo20120805_003.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:TreeViewDemo20120805_003="clr-namespace:Art55.TreeViewDemo20120805_003" Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <TreeViewDemo20120805_003:TreeViewNode x:Key="sampleData">
            <TreeViewDemo20120805_003:TreeViewNode.Children>
                <TreeViewDemo20120805_003:TreeViewNode Value="大分類1">
                    <TreeViewDemo20120805_003:TreeViewNode.Children>
                        <TreeViewDemo20120805_003:TreeViewNode Value="小分類1">
                            <TreeViewDemo20120805_003:TreeViewNode.Children>
                                <TreeViewDemo20120805_003:TreeViewNode Value="項目A" />
                                <TreeViewDemo20120805_003:TreeViewNode Value="項目B" />
                                <TreeViewDemo20120805_003:TreeViewNode Value="項目C" />
                            </TreeViewDemo20120805_003:TreeViewNode.Children>
                        </TreeViewDemo20120805_003:TreeViewNode>
                    </TreeViewDemo20120805_003:TreeViewNode.Children>
                </TreeViewDemo20120805_003:TreeViewNode>
                <TreeViewDemo20120805_003:TreeViewNode Value="大分類2">
                    <TreeViewDemo20120805_003:TreeViewNode.Children>
                        <TreeViewDemo20120805_003:TreeViewNode Value="小分類1">
                            <TreeViewDemo20120805_003:TreeViewNode.Children>
                                <TreeViewDemo20120805_003:TreeViewNode Value="項目A" />
                            </TreeViewDemo20120805_003:TreeViewNode.Children>
                        </TreeViewDemo20120805_003:TreeViewNode>
                        <TreeViewDemo20120805_003:TreeViewNode Value="小分類1">
                            <TreeViewDemo20120805_003:TreeViewNode.Children>
                                <TreeViewDemo20120805_003:TreeViewNode Value="項目A" />
                            </TreeViewDemo20120805_003:TreeViewNode.Children>
                        </TreeViewDemo20120805_003:TreeViewNode>
                    </TreeViewDemo20120805_003:TreeViewNode.Children>
                </TreeViewDemo20120805_003:TreeViewNode>
                <TreeViewDemo20120805_003:TreeViewNode Value="大分類3">
                    <TreeViewDemo20120805_003:TreeViewNode.Children>
                        <TreeViewDemo20120805_003:TreeViewNode Value="小分類1">
                            <TreeViewDemo20120805_003:TreeViewNode.Children>
                                <TreeViewDemo20120805_003:TreeViewNode Value="項目A" />
                                <TreeViewDemo20120805_003:TreeViewNode Value="項目B" />
                            </TreeViewDemo20120805_003:TreeViewNode.Children>
                        </TreeViewDemo20120805_003:TreeViewNode>
                    </TreeViewDemo20120805_003:TreeViewNode.Children>
                </TreeViewDemo20120805_003:TreeViewNode>
            </TreeViewDemo20120805_003:TreeViewNode.Children>
        </TreeViewDemo20120805_003:TreeViewNode>
    </Window.Resources>
    <Grid>
        <TreeView x:Name="treeView" DataContext="{StaticResource sampleData}" ItemsSource="{Binding .}">
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding .}" >
                    <TextBlock Text="{Binding Value}" />
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>
    </Grid>
</Window>

上記をみると分かると思いますが、ListViewNode<T>をTreeViewコントロールに適用する場合、TreeViewコントロール自身は、どのオブジェクトがコレクションになたるのか、どのオブジェクトがヘッダーになるのか分からないため、ItemTemplateを自作する必要があります。

<HierarchicalDataTemplate ItemsSource="{Binding .}" >
    <TextBlock Text="{Binding Value}" />
</HierarchicalDataTemplate>

今回用意したサンプルデータだと上記のように記述することでツリー表示が可能となります。このあたりがListBoxやDataGridとはかなり違うところで私自身ふなれなところです。

TreeViewNodeの話に戻します。今回、用意したTreeViewNodeクラスはヘッダーとコレクションの情報を保持するクラスでした。少し考え方を変えて、ヘッダー情報付きコレクションと考えてみてはどうでしょうか。

書き換えるとこうなります。

    public class TreeViewNode<T> : List<TreeViewNode<T>>
    {
        public T Value { get; set; }
    }

継承よりコンポジットに方が世の中うまく回ることが多いですが、あえてこのようなクラスにしてしまうというのもありかもしれません。この場合は「ヘッダー情報付きコレクションクラス」です。

こちらの方がXAMLで記述した場合すっきりします。

<TreeViewDemo20120805_003:TreeViewNode x:Key="sampleData">
        <TreeViewDemo20120805_003:TreeViewNode Value="大分類1">
                <TreeViewDemo20120805_003:TreeViewNode Value="小分類1">
                        <TreeViewDemo20120805_003:TreeViewNode Value="項目A" />
                        <TreeViewDemo20120805_003:TreeViewNode Value="項目B" />
                        <TreeViewDemo20120805_003:TreeViewNode Value="項目C" />
                </TreeViewDemo20120805_003:TreeViewNode>
        </TreeViewDemo20120805_003:TreeViewNode>
        ...
</TreeViewDemo20120805_003:TreeViewNode>

可変なコレクションに対して、コレクションの変更通知をサポートしたいのであれば

    public class TreeViewNode<T> : ObservableCollection<TreeViewNode<T>>
    {
        public T Value { get; set; }
    }

「ヘッダーと子供のコレクションを管理するノードクラス」か「ヘッダー付きコレクションクラス」と見るか、どちらが良いかのか私には判断できるほどの経験がないので結論を出せませんが、まあ色々試してみると面白いと思います(笑)

ちなみにValueに対して変更通知をサポートしたいのであれば、INotifyPropertyChangedを継承する必要があります。ObservableCollection<TreeViewNode<T>>を継承したかたちでは、ObservableCollection<TreeViewNode<T>>自身がINotifyPropertyChangedから派生しているので、OnPropertyChangedメソッドを継承元から呼び出すことができます。ただ単にコーディング量が減るというメリットが得られるだけです・・・一応書いておきました。

【WPF】TreeViewのアイテムをドラッグアンドドロップをするには(4)TreeViewの基本

  • 2012.08.05 Sunday
  • 05:10



TreeViewコントロールを利用することでツリー構造をもつ対象をヴィジュアル的にツリー表示することが可能となります。TreeViewは内部的にはTreeViewItemのコレクションを包含しています。System.Windows.Controls.TreeViewItemは、System.Windows.Controls.HeaderedItemsControlから派生している事から、TreeViewItemはヘッダー付きItemsControlといえます。TreeViewItemは、一つのヘッダーに対して、子供としてのコレクションが存在し、その子供のコレクションの要素もまたTreeViewItemとなり、TreeViewItemの親子な関係が構築できます。

TreeViewコントロールをXAMLで宣言的に記述すると下記のようにかけます。

<TreeView>
    <TreeViewItem Header="陸の生き物">
        <TreeViewItem Header="鳥">
            <TreeViewItem Header="鶏" />
            <TreeViewItem Header="やんばるくいな" />
        </TreeViewItem>
        <TreeViewItem Header="昆虫">
            <TreeViewItem Header="ダンゴムシ" />
        </TreeViewItem>
    </TreeViewItem>
    <TreeViewItem Header="空の生き物">
        <TreeViewItem Header="鳥">
            <TreeViewItem Header="ツバメ" />
            <TreeViewItem Header="フクロウ" />
        </TreeViewItem>
        <TreeViewItem Header="昆虫">
            <TreeViewItem Header="蜂" />
        </TreeViewItem>
    </TreeViewItem>
    <TreeViewItem Header="海の生き物">
        <TreeViewItem Header="鳥">
            <TreeViewItem Header="カモメ" />
        </TreeViewItem>
        <TreeViewItem Header="魚">
            <TreeViewItem Header="サメ" />
        </TreeViewItem>
        <TreeViewItem Header="おおきいやつ">
            <TreeViewItem Header="鯨" />
        </TreeViewItem>
    </TreeViewItem>
</TreeView>

上記を見れば分かると思いますが、TreeViewの内部に複数のTreeViewItemが存在し、TreeViewItemの内部にも、複数のTreeViewItemが構築できます。


【WPF】TreeViewのアイテムをドラッグアンドドロップをするには(3)本当にTreeViewを使うんですか?使うべきなんですか?

  • 2012.08.05 Sunday
  • 04:07



この連日の投稿以外では、当ブログでTreeViewコントロールの話題を扱ったことがありません。個人的にはTreeViewコントロールというのは嫌いです。理由の一つはアホなSEがTreeViewを使えば問題解決できると思ってるからです。それに関してはこれ以上触れませんが(笑)ほかの理由としては、TreeViewコントロールは、アプリケーション提供者が恣意的に分類した構造をユーザに無理強いしてしまう恐れがあるからです。

たとえばCドライブ直下にあるディレクリ構造。これはユーザにそう見せたくて、あのような構造になっているでしょうか?もちろんそんな訳がなく、一般ユーザから見れば、自分の目当てのファイルを探しにくい構造になっています。Windowsのディレクトリ構造はバージョンによって多少異なりますが、同じバージョンであれば変更なく、ユーザが十分に学習していく時間があるので、ツリー表示しても、そのうち問題にならなくなる可能性があります。

また、もう一つ例をあげるとURLに使われているDNS(www.google.jpなど)もし仮にエッチなサイトを検索したと思い、誰かに頼んでエッチなサイト検索アプリケーションを作ってもらったとします。このアプリケーションはDNSをドット区切りで右からツリー表示して、表示していたとします。DNSの分類からエッチなサイトを見つけようとしても、最終的にそのWebサイトを見なければ目的のサイトかどうか分かりません。これは完全に分類方法とユーザが何をしたかったのかという事に対する解がミスマッチです。ここまでひどい例はないと思いますが、案外よくあることです。

ツリー表示というのは、多角的な知見が存在するものに対して恣意的に分類を選択されてしまうというのは否めません。目的に対してベストチョイスするというのは案外難しいと思います。または、そもそも分類してしまうということが、自体がデメリットになってしまうこともあるでしょう。

ということで自分が作成しているアプリケーションにメニュー表示以外でツリー表示したことがありません。そもそもアホな人が分類方法も提供せずにTreeViewを使って!って言ってくるので、全力で反対せざるをえません。ちなみに一階層のグループ化は時々使っています。ユーザが必要な時にグループ化できるというのはユーザビリティ的に優れていると思います。

【WPF】TreeViewのアイテムをドラッグアンドドロップをするには(2)System.Windows.DragDropを利用したドラッグアンドドロップの基本

  • 2012.08.04 Saturday
  • 14:07




今回は「System.Windows.DragDropクラス」を利用したドラックアンドドロップの基本を解説します。ポイントは下記の通りです。

1.ユーザが行ったジェスチャーをドラッグと判定する。
2.DragDrop.DoDragDropメソッドでドラッグが開始したことをコンピュータに通知する。
3.ドロップが可能なコントロールにAllowDropをTrueに設定しておく。
4.ドロップされた後の挙動をハンドリングする。

サンプルコードで上記のポイントの実装部分を解説します。

1.ユーザが行ったジェスチャーをドラッグと判定する。
今回のサンプルコードでは、青色のLabelコントロールでマウスの左ボタンが押されたら、一律ドラッグが開始されたと判定することにしました。

<Label Grid.Column="0" 
   ...
   MouseLeftButtonDown="OnMouseLeftButtonDown"
   ...>あ</Label>

private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    var label = (Label)sender;
    DragDrop.DoDragDrop(label, label.Content, DragDropEffects.Move);
}

今回はマウスの左ボタンダウンが、ドラッグ開始のジェスチャーであるとしたので、ラベル上でマウスダウン以外の判定は行っていませんが、必要であればマウスがダウンされてマウスをダウンしたまま何ピクセル移動すればドラッグだとか、もっと細かく判定することも可能です。また、バブリングやトンネリングのイベントの伝搬を利用すれば、複数コントロールを一括して同じイベントハンドラで処理することもできます。その時はドラッグ対象のコントロールかどうかをしっかり判定する必要があります。

2.DragDrop.DoDragDropメソッドでドラッグが開始したことをコンピュータに通知する。

ドラッグは、WPFの場合、マウスをクリックした状態で移動させてもドラッグしているとコンピュータは見なしません。マウスをクリックした状態で移動させるというジェスチャーが行われたと判定されたら、DragDrop.DoDragDropメソッドでドラッグ開始された事を通知する必要があります。こうすることではじめてドラッグ関連のイベントは、発砲する事になります。ちなみにドラッグアンドドロップ関連のイベントは各コントロールに存在し、また、DragDropクラスの添付イベントとして存在しているので確認することをお勧めします。

private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    var label = (Label)sender;
    DragDrop.DoDragDrop(label, label.Content, DragDropEffects.Move);
}

サンプルコードでは、マウスの左ボタンをクリックしたタイミングでDragDrop.DoDragDropメソッドを呼び出しています。注目すべきは第二引数です。第二引数はドラッグするデータを格納するデータ オブジェクトです。このデータ オブジェクトはドロップ時に取得することが可能です。なので渡したい情報を設定することをお勧めします。

3.ドロップが可能なコントロールにAllowDropをTrueに設定しておく。

ドロップが可能な領域にAllowDropをTrueに設定する必要があります。どのタイミング設定するかという事は意外に重要です。常にTrueにしておきたいのであればXAMLで設定すると実現できますが、常にTrueである場合、特定のドラッグ元からしかドロップを受け付けたくない場合などの要求があった場合には問題となります。その場合は、ドラッグが開始されるタイミングの直前までに何らかのかたちでAllowDropを制御する必要があります。

今回のサンプルコードはXAMLで常にTrueを設定しています。

<Label ...
   AllowDrop="True"
   Drop="OnDrop"
   ...></Label>

試しにこのアプリケーションを実行し、デスクトップにあるアイコンをドラッグさせて、赤いラベルにドラッグしてみてください。まるでドロップできるような見た目になると思います。

4.ドロップされた後の挙動をハンドリングする。
ドロップされた後の挙動をハンドリングするには、ドロップしたい対象のDropイベントをハンドリングする必要があります。ドラッグ開始時と同様、バブリング/トンネリングで複数コントロールを一括してハンドリングする事もできます。もちろん、複数コントロールに同じハンドラを設定する事でも実現できます。

今回のサンプルコードではドロップ可能なラベルコントロールにDropイベントをハンドリングしています。

private void OnDrop(object sender, DragEventArgs e)
{
    var value = (string) e.Data.GetData(typeof (string));
    var targetLabel = (Label) sender;
    targetLabel.Content = value;
}

DragEventArgs.Data.GetDataメソッドを利用すると、DragDrop.DoDragDropメソッドの第二引数で指定しデータ オブジェクトを取り出すことができます。また、いくつかのデータ オブジェクトが組み込まれており、たとえばデスクトップからドラッグアンドドロップさせた場合、ドラッグさせたファイルパスを受け取ったりすることもできます。

今回は、受け取った文字列をドロップしたラベルコントロールに設定しています。

以上が、ドラッグアンドドロップの簡単な実装です。

応用力をつけたいのであれば、
DragDrop.DoDragDropメソッドやDragEventArgsの詳細を調べたり
DragDropの各添付イベントの調べたりすると良いでしょう。

【WPF】TreeViewのアイテムをドラッグアンドドロップをするには(1)

  • 2012.08.04 Saturday
  • 04:34
JUGEMテーマ:コンピュータ

WPFでドラッグアンドドロップしたいという質問をBlogで何度も質問を受けているので、ちゃんと説明しようと思い立ちました。TreeView内のアイテムをドラッグアンドドロップさせるにはどうすれば良いのかというところを最終的に理解できる内容にしたいと思います。今回は、そもそもドラッグアンドドロップを実現するにはどういう方法があるのかという事を解説したいと思います。

WPFでドラッグアンドドロップでするにはいくつかの方法が考えられます。

1.System.Windows.DragDropクラスを利用する。
2.System.Windows.Controls.Primitives.Thumbコントロールを利用する。
3.Blend SDKのMouseDragElementBehaviorを利用しコントロールの座標を変更する。


2と3はドロップというアクションが明示的なものではなく、ドラッグさせるという事が主な目的になります。それに対して1はドラッグとドロップのアクションがイベントとして連動するので、比較的簡単にドラッグアンドドロップが実現できると思います。ThumbやBlend SDKを利用したことがない方は一度自分でコードを書いてみて、動かしてみるとイメージがつくと思います。そして、自分が実現したいことが、単にコントロールを動かしたいだけでドラッグアンドドロップじゃなかったなど、勘違いに気づけるかもしれません。
Thumbを利用したサンプルコード
http://pro.art55.jp/?eid=1181623
http://pro.art55.jp/?eid=1181572


MouseDragElementBehaviorを利用したサンプルコード
http://pro.art55.jp/?eid=1303847


System.Windows.DragDropクラスに関しても過去に何度も投稿していますが
あえて新しく投稿してみようと思います。

【WPF】TreeViewコントロール上でドラッグアンドドロップ

  • 2012.08.04 Saturday
  • 03:26
 JUGEMテーマ:コンピュータ


TreeViewコントロール上でドラッグアンドドロップを実現したサンプルコードをアップしてきます。
要素技術が広範囲にわたるため、今回の投稿では解説はしません。
ある程度の粒度でわかりやすく解説できる自信とこころに余裕があれば実現したいと思います。

calendar

S M T W T F S
 123456
78910111213
14151617181920
21222324252627
28293031   
<< July 2019 >>

あわせて読みたい

あわせて読みたいブログパーツ

selected entries

categories

archives

recent comment

  • 【キーボード】6年前のRealForceを復活させることはできる!?その3
    art55 (05/22)
  • 【キーボード】6年前のRealForceを復活させることはできる!?その3
    分解大好き (05/18)
  • 【.NET Framework 4.5】 IListがIReadOnlyListを継承してない理由。
    art55 (02/04)
  • 【.NET Framework 4.5】 IListがIReadOnlyListを継承してない理由。
    Gen (02/04)
  • 【キーボード】RealForce が壊れて帰ってきた。
    art55 (04/29)
  • 【.NET Framework 4.5】 IListがIReadOnlyListを継承してない理由。
    art55 (02/23)
  • 【.NET Framework 4.5】 IListがIReadOnlyListを継承してない理由。
    かるあ (02/22)
  • 【C#】Dictionaryの実装・データ構造・アルゴリズムを観察する。
    art55 (01/16)
  • 【C#】Dictionaryの実装・データ構造・アルゴリズムを観察する。
    karuakun (01/16)
  • 【NetOffice】【Excel】死なないExcelプロセスをKillする。
    art55 (12/05)

recent trackback

recommend

recommend

recommend

C#プログラマのための.NETアプリケーション最適化技法 (Programmer's SELECTION)
C#プログラマのための.NETアプリケーション最適化技法 (Programmer's SELECTION) (JUGEMレビュー »)
Sasha Goldshtein,Dima Zurbalev,Ido Flatow,サシャ・ゴルドシュタイン,ディマ・ズルバレフ,イド・フラトー

recommend

ろんりと集合
ろんりと集合 (JUGEMレビュー »)
中内 伸光
とてもわかりやすいです。

recommend

recommend

シャノン・ノイマン・ディジタル世界
シャノン・ノイマン・ディジタル世界 (JUGEMレビュー »)
市川 忠男
4章がリレーショナルデータベースな内容になってます。ページ数があまりありませんが、ポイントがものすごく的確にまとまっていて、感動します。

recommend

recommend

東プレ Realforce91UBK-S 静音キーボード 静電容量無接点方式 変荷重 ブラック NG01BS
東プレ Realforce91UBK-S 静音キーボード 静電容量無接点方式 変荷重 ブラック NG01BS (JUGEMレビュー »)

テンキーレス、静音のRealForce91UBK-S。スコスコ感がたまらなく気持ちいいです。家と会社で2台持ってます。

recommend

recommend

プログラミング.NET Framework 第4版 (プログラミングシリーズ)
プログラミング.NET Framework 第4版 (プログラミングシリーズ) (JUGEMレビュー »)
Jeffrey Richter
発売予定美 2013年10月10日。.NET Frameworkとお付き合いする人のバイブルですね。

recommend

recommend

キャット・シッターの君に。
キャット・シッターの君に。 (JUGEMレビュー »)
喜多嶋 隆
私のイラストレータデビュー本です。

recommend

Essential .NET ― 共通言語ランタイムの本質
Essential .NET ― 共通言語ランタイムの本質 (JUGEMレビュー »)
ドン・ボックス,クリス・セルズ,Don Box,Chris Sells,吉松 史彰

links

profile

search this site.

others

mobile

qrcode

powered

無料ブログ作成サービス JUGEM