※この記事に書かれている方法は、MVVMの原則を壊す恐れがあるため、推奨される方法ではありません。それをご理解の上無理矢理実装したい方向けの記事になります。
最近WPFを書き始めたのですが、MVVMでコードを書いてる時にViewModelでViewのインスタンスが欲しいなーって思うことが時々あったので、取得する方法を忘却録として書いておきます。
個人的にはReactiveProperty無しでC#+WPFでMVVMなコードを書く気にはならないです。
環境: .NET FrameWork4.8,Visual Studio Community 2019
(.NET Coreでも.NET5でもVisual Studio2017 Expressなんかでも動くと思います)
準備
まず前提条件として、View.XamlのDataContextでViewModelを参照します。
PrismなどのMVVMフレームワークを使用しているなら自動的に登録されていたりしますが、そうでないならWindowやUserControlの下にこういった感じで指定します。
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
①プロジェクト→Nugetパッケージの管理より、ReactiveProperty.WPFを追加します。
②View.xamlに以下の参照を追加します。
xmlns:bh="http://schemas.microsoft.com/xaml/behaviors"
xmlns:rp="clr-namespace:Reactive.Bindings.Interactivity;assembly=ReactiveProperty.WPF"
③ViewModel.csに以下の参照を追加します。
using System.Windows;
using Reactive.Bindings;
取得方法
View.xamlにてEventToReactiveCommandを使い、Loadedイベントをコマンドに変換してViewModelに渡します。
<!--参照の定義の下、ボタンなどの要素の上に書きます。-->
<bh:Interaction.Triggers>
<bh:EventTrigger EventName="Loaded">
<rp:EventToReactiveCommand Command="{Binding LoadCommand}" />
</bh:EventTrigger>
</bh:Interaction.Triggers>
受け取ったコマンド内のRoutedEventArgs.SourceからWindowのインスタンスを取得します。
他で使えるように、適当な変数にでも入れておきましょう。
public class MainWindowViewModel{
public ReactiveCommand<RoutedEventArgs> LoadCommand { get; }
private MainWindow _mainWindow;
public MainWindowViewModel() {
//必要に応じて.addToやClassTerminateなどでオブジェクトの解放を定義しておきます。
LoadCommand =
new ReactiveCommand<RoutedEventArgs>()
.WithSubscribe(e => _mainWindow = (MainWindow)e.Source);
//例えばこんな感じのコマンドを作ってウィンドウを閉じることが出来ます。
//このコードはメンバ定義もバインディングもしてないのでこのままでは動きません。
hogehogeCommand=
new ReactiveCommand()
.WithSubscribe(() => _mainWindow.Close());
}
}
所感
WindowやWindow内に読み込んだUserControl自体の状態をどこで管理したら良いかよくわからない。
(呼び出したWindowのViewModelに責務があるのか、DIに登録したModelと繋いだほうがいいのか・・・とか)