WPF、ComboBoxのGotFocusイベント MVVM版
WPF、ComboBoxのGotFocusイベントを、MVVMにしてみましょう。
準備
NuGetで次をインストールします。
- Microsoft.Xaml.Behaviors.Wpf 1.1.31
- ReactiveProperty 7.8.3
方法1 Behaviorsを利用する
- BehaviorsのInvokeCommandActionで、PassEventArgsToCommand="True" とすることで、EventArgsを渡すことが出来ます。
- ICommandを利用しても良いですが、ReactiveCommandにはICommandが含まれているのでそれを利用すると簡潔なコードになります。
View
xmlns:bh="http://schemas.microsoft.com/xaml/behaviors"
<ComboBox Width="120" Height="30">
<bh:Interaction.Triggers>
<bh:EventTrigger EventName="GotFocus">
<bh:InvokeCommandAction Command="{Binding ComboBoxFocus}" PassEventArgsToCommand="True" />
</bh:EventTrigger>
</bh:Interaction.Triggers>
<ComboBoxItem>Test Item1</ComboBoxItem>
<ComboBoxItem>Test Item2</ComboBoxItem>
<ComboBoxItem>Test Item3</ComboBoxItem>
</ComboBox>
- ModelになるMainManagerのコードは、ここでは利用しませんので、省略しています。
- ComboBoxにフォーカスがあたった時の処理を、Viewで行うか、ViewModelで行うか、Modelで行うかは、その処理の内容がどの領分にあたるのかによって、決めれば良いです。
ViewModel
public class MainViewModel : INotifyPropertyChanged, IDisposable
{
private MainManager Model { get; }
public ReactiveCommand<RoutedEventArgs> ComboBoxFocus { get; } = new();
public MainViewModel(MainManager model)
{
Model = model;
ComboBoxFocus.Subscribe(e => ComboBox_GotFocus(e)).AddTo(Disposable);
}
private void ComboBox_GotFocus(RoutedEventArgs e)
{
var type = e.OriginalSource.GetType();
if (type == typeof(ComboBox))
{
Debug.WriteLine("ComboBox Foucus");
}
else if (type == typeof(ComboBoxItem))
{
Debug.WriteLine("ComboBoxItem Foucus");
Debug.WriteLine(((ComboBoxItem)e.OriginalSource).Content);
}
}
// INotifyPropertyChanged を実装
// Dispose を実装
方法2 Behaviorsを利用しない
Behaviorsを利用しないなら、Viewのコードビハインドにイベントを定義します。Viewから、VMの処理を呼び出せばOKです。
VMの、ComboBox_GotFocusは、Publicにします。
View
public partial class MainView : Window
{
MainViewModel vm;
public MainView(MainViewModel viewModel)
{
InitializeComponent();
this.vm = viewModel;
this.DataContext = vm;
}
private void ComboBox_GotFocus(object sender, RoutedEventArgs e)
{
vm.ComboBox_GotFocus(e);
}
}
環境
.NET5, WPF
MVVMについて
MVVMにしようとすると、Behaviorsを利用する方法では、XAMLに5行も書かないといけません。長すぎます。従って、コードの保守も難しくなります。ReactiveCommandを利用しないと、さらにコードは長くなります。
WPF、ComboBoxのGotFocusイベントなら、簡潔ですよね。けれども、コードビハインドでなにもかもするのは、複雑なプログラムでは破綻しやすいと思います。すると、MVVMを利用せざる得ないわけです。
MVVMは、やたらとコードが長くなるのが、デメリット極まりないです。これは、Prismを使うと解消するのでしょうか? なんどかPrismにトライしようと調べるのですが、とてもそうは思えないです。また、破壊的変更もままあり、Prismのバージョンアップに対応しきれないと判断し、利用しないことにしました。
MVVMに足りていないことの1つは、それを実現するためのフレームワークや、.NETの機能でしょう。例えば、次のコードはエラーになりますが、このようにComboBoxのイベントにバインディングできれば、ぐっと簡潔になると思います。
<ComboBox GotFocus="{Binding ComboBox_GotFocus}" />
こうした実装は議論はされたのだとは思いますが、未だなっていないということは、デメリットがあるのか、実装が大変すぎるのか・・・
いずれにしろ、現状の.NET環境で、MVVMに即してコードを書くのは、未だ課題が多いということです。
Discussion