カテゴリー別アーカイブ: C#

【WPF】MVVMパターンのウィンドウを「閉じる」を考える

WPFの便利な機能 ApplicationCommands

WPFでは標準的なコマンドを定義を定義したクラスApplicationCommandsがあります。

このクラスには終了を表すプロパティCloseが定義されており、以下のように利用することができます。

MainWindow.xaml
<Window x:Class="WpfApplication01.MainWindow" ...>
    <Window.CommandBindings>
        <CommandBinding Command="Close" Executed="CommandBinding_Executed" CanExecute="CommandBinding_CanExecute" />
    </Window.CommandBindings>
    <Grid>
        <Button Content="おしまい" Command="Close" />
    </Grid>
</Window>
MainWindow.xaml.cs
 private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
 {
    Close();    // ウィンドウを閉じる
 }

private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    e.CanExecute = true; // 無条件で実行可能
}

これだけで十分ですが、MVVMにこだわりたいのでコードビハインドにハンドラを書かずにBehaviorに役割を渡します。

ApplicationCommands.CloseをBehaviorで実現する

先ほどのイベントハンドラで実装した内容をそのままBehaviorで実現します。

ApplicationCommandBehavior.cs
    public class ApplicationCommandBehavior : Behavior<Window>
    {
        protected override void OnAttached()
        {
            base.OnAttached();
            CommandBinding CloseCommandBinding = new CommandBinding(
                ApplicationCommands.Close,
                CloseCommandExecuted,
                CloseCommandCanExecute);
            AssociatedObject.CommandBindings.Add(CloseCommandBinding);
        }

        private void CloseCommandExecuted(object target, ExecutedRoutedEventArgs e)
        {
            AssociatedObject.Close();
            e.Handled = true;
        }

        private void CloseCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
            e.Handled = true;
        }
    }

名前はApplicationCommandBehaviorですがCloseしか書いてません。
このBehaviorをMainWindow.xamlに記述します。

MainWindow.xaml
<Window x:Class="WpfApplication01.MainWindow" ...>
    <i:Interaction.Behaviors>
        <local:ApplicationCommandBehavior />
    </i:Interaction.Behaviors>
    <Grid>
        <Button Content="おしまい" Command="Close" />
    </Grid>
</Window>

こうするとコードビハインドにイベントハンドラがなくなりすっきりしました。

ViewModelにWindowが閉じることを知らせる

次にWindowが閉じることをViewModelに知らせる方法を実装します。
これは、Closingイベントハンドラにコマンドを割り当てるのが一般的だと思います。

MainWindow.xaml
<Window x:Class="WpfApplication01.MainWindow" ...>
    <Window.DataContext>
        <local:MainWindowViewModel />
    </Window.DataContext>
    <i:Interaction.Behaviors>
        <local:ApplicationCommandBehavior />
    </i:Interaction.Behaviors>
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Closing">
            <i:InvokeCommandAction Command="{Binding Path=ClosingCommand}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
    <Grid>
        <Button Content="おしまい" Command="Close" />
    </Grid>
</Window>