Quantcast
Channel: C#タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9683

WindowsフォームのForm_Load()メソッドをWPFのMVVMで置き換える

$
0
0
はじめに WindowsフォームアプリからWPFアプリに移行すると MVVMの呪縛にかかり、コードビハインドに何も書けなくなる (※ 個人差があります) たとえば、フォーム(WPFではウィンドウ)を読み込んだ直後に何かの初期化処理をしたい場合など・・ フォームアプリでは、フォームをダブルクリックすると自動的にForm_Load()メソッドが作られるので、そこに処理を書けばよい 一方、WPFアプリでは・・・Windowをいくら高速クリックしてもメソッドは生成されない😣 これは困ったことなので、どうしたらいいかを書く 目次のようなもの << ゴール >> WPFアプリにおいて、ウィンドウが読み込まれた直後になんらかの初期化メソッドを呼ぶ (ただし、コードビハインドには何も追記しない) 流れとしては、まずフォームアプリで所望の動作をさせてみる。 そのあと、WPFアプリで同じことをする フォームアプリ おなじみのWindowsフォームアプリ プロジェクトを立ち上げ直後は、まっさらなフォームが表示される フォームの上でマウスを左ダブルクリックすると、メソッドが生成される // フォームをダブルクリックすると、このメソッドが生成される private void Form1_Load(object sender, EventArgs e) { // フォームをロードし終えたときの動作 } あとはこの関数の中に、初期化などの処理を書けばいい フォームアプリについては以上 WPFアプリ つづいてWPFアプリの2パターン コードビハインドを使う場合と、使わない場合 Case1: MVVMの呪縛に囚われていない場合 WindowタグにLoaded="Window_Loaded"を追加する XAMLファイル <Window x:Class="MyWpfApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:MyWpfApp" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800" + Loaded="Window_Loaded"> <Grid> </Grid> </Window> コードビハインドに同名のメソッドを追加し、中身を書けば終わり コードビハインド public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } + private void Window_Loaded(object sender, RoutedEventArgs e) + { + // ここに処理を書く + } } Tips) IDEの補助機能を有効につかう ViewでLoaded="まで入力すると、「新しいイベントハンドラー」ポップアップが表示される このポップアップをクリックすると、コードビハインドにメソッドが自動で追加される おまけ)ほかのイベントについて ウィンドウのプロパティを開いて右上の⚡を押せば、使えるであろうたくさんのイベントが表示される Case2: MVVMの呪縛でコードビハインドに何も書けない場合(本題) MVVMに従う場合、何はともあれViewModelのファイルを作成する 今回はMainWindowViewModel.csをプロジェクトに追加するものとする NuGetで必要なライブラリをインストールする Xaml.Behaviors.Wpfをインストールする これはViewのイベントとViewModelに定義したコマンドを紐づけるためにつかう 詳しくは・・・System.Windows.Interactivity.dll から Xaml.Behaviors.Wpf へ ReactivePropertyをインストールする これはコマンドを簡単に書くためのライブラリである View (XAML) を編集する さきほどのLoaded=...イベントは削除する。 代わりにiに関わるものを追加する。 新たに追加した行を見ると イベントトリガーという名前からして、なにかのイベントに連動して処理が行われそうだとわかる Loadedと書かれており、きっとウィンドウがロードされたイベントを指定していそうだとわかる Command="{Binding ...}"でViewModelのコマンドに連動しそうだとわかる MainWindow.xaml <Window x:Class="MyWpfApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:MyWpfApp" + xmlns:i="http://schemas.microsoft.com/xaml/behaviors" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800" - Loaded="Window_Loaded"> + <i:Interaction.Triggers> + <i:EventTrigger EventName="Loaded"> + <i:InvokeCommandAction Command="{Binding Loaded_Command}"/> + </i:EventTrigger> + </i:Interaction.Triggers> <Grid> </Grid> </Window> ViewModelを編集する ViewModelはINotifyPropertyChangedを実装する なぜ必要か? --->>> Q. あれ、ReactivePropertyだとINotifyPropertyChanged要らないんじゃなかったっけ? ReactiveCommandクラスのLoaded_CommandがViewにバインドされる .Subscribeの引数(ラムダ式)に書かれた処理が実行される MainWindowViewModel.cs using Reactive.Bindings; using System.ComponentModel; namespace MyWpfApp { public class MainWindowViewModel : INotifyPropertyChanged { // Viewにバインドするコマンド public ReactiveCommand Loaded_Command { get; } = new(); public MainWindowViewModel() // コンストラクター内で { // ボタンが押された時の動作を定義する Loaded_Command.Subscribe(()=> System.Diagnostics.Debug.WriteLine("Loaded !!")); } // これはひとまず気にしないでいい public event PropertyChangedEventHandler PropertyChanged; } } このまま実行しても"Loaded !!"とは表示されない 最後にもう1度、View (XAML) を編集する ViewとViewModelをつなぐために、Viewを編集する DataContextにMainWindowViewModelを指定する MainWindow.xaml <Window x:Class="MyWpfApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:MyWpfApp" xmlns:i="http://schemas.microsoft.com/xaml/behaviors" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> + <Window.DataContext> + <local:MainWindowViewModel/> + </Window.DataContext> <i:Interaction.Triggers> <i:EventTrigger EventName="Loaded"> <i:InvokeCommandAction Command="{Binding Loaded_Command}"/> </i:EventTrigger> </i:Interaction.Triggers> <Grid> </Grid> </Window> なお、MVVMの呪縛に囚われていない場合は コードビハインドでthis.DataContext = new MainViewModel();としてもよい。 まとめ ことLoadedイベントだけに関してみれば、WinFormのほうが何も考えずにできてよいかもしれない しかし、ひるがえってみれば、LoadedのためだけにNuGetを通してわざわざ2つのライブラリをインストールするWPFに対しては、なにかこの先大きなことをしてくれるのではないかと期待してしまう というまとめ。

Viewing all articles
Browse latest Browse all 9683

Trending Articles