TreeView の SelectedItemChanged に Command をバインドしたい。
遅ればせながら MVVM パターンの理解を深めるために WPF でエクスプローラーもどきを作っています。TreeView にディレクトリの木構造が表示され、任意のディレクトリを選択すると、その中のディレクトリとファイルの一覧が ListView に表示される、単純なやつです。
WPF の TreeView って初めて使ったんですけど、SelectedItem プロパティが読み取り専用になってて、Binding を設定したり出来ないんですよね。ViewModel 側で選択されたものを知りたいんだけど、なかなか一筋縄では行かない感じ。TreeView や TreeViewItem のイベントで設定すれば望みのことは出来るだろうけど、できれば View から ViewModel にアクセスせずに実現したい。
なんかいい方法ないかなーと思って調べたら、ちょうど TextBox で Enter キーを叩いたときに Command を実行するようなことをしてるのを見つけました。
添付ビヘイビアでTextBoxにCommandを実装してみた - SharpLab.
添付プロパティ内でイベントハンドラを設定したりするの、添付ビヘイビアって言うんですね。知らなかったです。手法自体は使ったことありましたが。
記事を参考にして、TreeView の SelectedItemChanged でコマンドを実行する添付ビヘイビアを定義してみました。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Input; namespace WpfExplorerApp { public class TreeViewBehaviors { public static ICommand GetOnSelectedItemChanged (DependencyObject d) { return (ICommand) d.GetValue (OnSelectedItemChangedProperty); } public static void SetOnSelectedItemChanged (DependencyObject d, ICommand value) { d.SetValue (OnSelectedItemChangedProperty, value); } public static readonly DependencyProperty OnSelectedItemChangedProperty = DependencyProperty.RegisterAttached ("OnSelectedItemChanged", typeof (ICommand), typeof (TreeViewBehaviors), new UIPropertyMetadata (null, OnSelectedItemChangedPropertyChanged)); static void OnSelectedItemChangedPropertyChanged (DependencyObject d, DependencyPropertyChangedEventArgs args) { var treeView = d as TreeView; if (treeView == null) return; if (args.NewValue is ICommand) treeView.SelectedItemChanged += new RoutedPropertyChangedEventHandler<object> (OnTreeViewSelectedItemChanged); else treeView.SelectedItemChanged -= new RoutedPropertyChangedEventHandler<object> (OnTreeViewSelectedItemChanged); } static void OnTreeViewSelectedItemChanged (object sender, RoutedPropertyChangedEventArgs<object> e) { var treeView = e.OriginalSource as TreeView; if (treeView == null) return; var command = GetOnSelectedItemChanged (treeView); if (command == null) return; command.Execute (treeView.SelectedItem); } } }
で、ViewModel 側に SetSelectedItemCommand って Command を定義。
namespace WpfExplorerApp.ViewModel { public class DirectoryTreeViewModel : ViewModelBase { DirectoryViewModel _selectedItem; RelayCommand _setSelectedItemCommand; public DirectoryViewModel SelectedItem { get { return _selectedItem; } set { if (_selectedItem == value) return; _selectedItem = value; OnPropertyChanged ("SelectedItem"); } } public ICommand SetSelectedItemCommand { get { if (_setSelectedItemCommand == null) _setSelectedItemCommand = new RelayCommand ( p => SetSelectedItem (p)); return _setSelectedItemCommand; } } private void SetSelectedItem (object parametor) { var directory = parametor as DirectoryViewModel; SelectedItem = directory; } } }
で、View 側で、この Command への Binding を記述します。
<TreeView local:TreeViewBehaviors.OnSelectedItemChanged="{Binding Path=SetSelectedItemCommand}"> </TreeView>
期待通り動いてるみたい。
トラックバック
- このエントリーにトラックバック:
- http://frog.raindrop.jp/cgi-bin/mt/mt-tb.cgi/2442
コメント