TreeViewのSelectedItemはsetterがないため、Bindingできません。
DataGridのSelectedItemsみたいなものですね。
これを解消するには大別して3つあります。
Behaviorで別の依存関係プロパティを設定し、SelectedItemChangedを購読して移し替えること。
TreeViewを継承し、SelectedItemをBindできる別のコントロールを作ってしまう。
EventToReactiveCommand等を使ってEventArgsを取り出す方法があります。
今回は EventToReactiveCommand を使ってSelectedItemを取り出したいと思います。
最小構成
ViewModel
ViewModel
public ReactiveCommand<RoutedPropertyChangedEventArgs<object>> SelectionChangedCommand { get; set; }
TreeViewのSelectedItemChangedイベントはRoutedPropertyChangedEventArgs<object>を使うのでこれをReactiveCommandの<T>に指定します。
RoutedPropertyChangedEventArgs のNewValueプロパティに新たに選択されたアイテムが格納されています。
Xaml
XAML
// xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:interactivity="clr-namespace:Reactive.Bindings.Interactivity;assembly=ReactiveProperty.NET46"
<TreeView ItemsSource="{Binding Directories}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectedItemChanged">
<interactivity:EventToReactiveCommand Command="{Binding SelectionChangedCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TreeView>
Interaction.TriggersからEventToReactiveCommandまでは定型文なのでこのまま覚えましょう。
EventNameだけは購読したいイベント名を書いてください。
CommandのBindingも至って普通です。
これでCommandのparameterとして、EventArgsを取得できます。
xmlnsについて
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
2010/Interactivityのnamespaceで解説されている記事が多かったですが、自分の環境(Prism&Rx)だと下記のエラーが出て動作しませんでした。
「型”EventToReactiveCommand”の値は、型”TriggerActionCollection”のコレクションまたは辞書に追加できません。」
「指定された値をコレクションに割り当てることができません。次の型の値を指定してください: “TriggerAction”」
試しに下記に置き換えてみたところ、無事に動作しました。
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
EventArgsをそのまま渡すのに抵抗がある場合
ReactiveConverterを使って値を成形できます。
Converter
public class TreeViewItemConverter : ReactiveConverter<RoutedPropertyChangedEventArgs<object>, FolderTreeViewItem>
{
protected override IObservable<FolderTreeViewItem> OnConvert(IObservable<RoutedPropertyChangedEventArgs<object>> source)
=> source.Select(x => (FolderTreeViewItem)x.NewValue);
}
ReactiveConverterを継承したConverterを作成し、OnConvertをoverrideします。
上記の例はEventArgsが長くて見にくいですが、
IObservable<T> OnConvert(EventArgs e)
のメソッドを定義してあげるだけです。
XAML
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectedItemChanged">
<interactivity:EventToReactiveCommand Command="{Binding SelectionChangedCommand}">
<vm:TreeViewItemConverter />
</interactivity:EventToReactiveCommand>
</i:EventTrigger>
</i:Interaction.Triggers>
XamlはEventToReactiveCommandの間にConverterを挟んだだけです。
これでCommandのparameterに変換後の値が入ります。
コメント