LoginSignup
12
15

More than 5 years have passed since last update.

ReactivePropertyとMaterial Design In XAML Toolkitを組み合わせて応答を待つダイアログを作る

Last updated at Posted at 2018-08-26

概要

MVVMでつまずくポイントとしてよく挙げられるのが、ダイアログ表示です。
単純にダイアログを表示して、OKを押されたら閉じる、という内容を以前に書きました。
WPFでいけてるダイアログを出す方法 - Qiita
今回はダイアログが閉じられるのを待って結果を受け取る方法を
Material Design In XAML ToolkitとReactivePropertyを使用して説明したいと思います。
なお今回の内容は現時点(2018/08/26)で最新のReactiveProperty 5.2.0が必要です。

説明

ダイアログはMaterial Design In XAML ToolkitのDialogHostを使用して表示します。
DialogHostのDialogContentプロパティにダイアログのViewを設定します。
ダイアログのDataContextには対応するダイアログVMを設定します。

MainWindow.xaml(部分)
<material:DialogHost.DialogContent>
    <v:DialogWindow DataContext="{Binding DialogVM}" />
</material:DialogHost.DialogContent>

DialogHostのIsOpenプロパティの変更でダイアログの表示/非表示を切り替えます。
そしてそのIsOpenプロパティをダイアログVMのIsOpen(ReactiveProperty)にBindします。

MainWindow.xaml(部分)
<material:DialogHost IsOpen="{Binding DialogVM.IsOpen.Value}">

MainWindowVMからダイアログを開始する際は、そのBindされたIsOpenにtrueを設定します。
そしてすぐにIsOpenの変更(=ダイアログの終了)を待機します。

MainWindowViewModel.cs(部分)
DialogVM.IsOpen.Value = true;
await DialogVM.IsOpen;

ダイアログを閉じる際は
ダイアログVM内でIsOpenにfalseを設定します。
ダイアログの結果のわたし方は色々ありますが、今回はダイアログVM内のReactivePropertyとして表現しました。

DialogWindowViewModel.cs(部分)
IsOK.Value = true;
IsOpen.Value = false;

ダイアログが閉じられたあとはMainWindowVMでダイアログVMのダイアログ結果を参照します。

MainWindowViewModel.cs(部分)
DialogResultText.Value = DialogVM.IsOK.Value == true ? "おーけー" : "きゃんせる";

デモプログラム実行結果

今回のデモプログラムの実行結果です。
中央のボタンを押すと、ダイアログが表示されます。
スクリーンショット 2018-08-26 22.30.20.png
ダイアログ内にはちょっと怖めの確認文とOKとCancelの2つのボタンがあります。
どちらを押してもダイアログは閉じられます。
スクリーンショット 2018-08-26 22.30.24.png
OKとCancelのどちらを押したかでMainWindowの文字が変わります。
スクリーンショット 2018-08-26 22.30.29.png

全体コード

デモプログラムの全体コードです。

View

MainWindow.xaml
<Window
    x:Class="ResponseDialogTest.Views.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:material="http://materialdesigninxaml.net/winfx/xaml/themes"
    xmlns:v="clr-namespace:ResponseDialogTest.Views"
    xmlns:vm="clr-namespace:ResponseDialogTest.ViewModels"
    Width="400"
    Height="350">
    <Window.DataContext>
        <vm:MainWindowViewModel />
    </Window.DataContext>

    <!--  DialogHostのIsOpenプロパティをDialogVMのIsOpenプロパティにBindしている  -->
    <material:DialogHost IsOpen="{Binding DialogVM.IsOpen.Value}">
        <material:DialogHost.DialogContent>
            <v:DialogWindow DataContext="{Binding DialogVM}" />
        </material:DialogHost.DialogContent>

        <StackPanel>
            <StackPanel Orientation="Horizontal">
                <TextBlock FontSize="18" Text="ダイアログ結果:" />
                <TextBlock FontSize="18" Text="{Binding DialogResultText.Value}" />
            </StackPanel>
            <Button Command="{Binding StartDialogCommand}" Content="ダイアログ開始" />
        </StackPanel>
    </material:DialogHost>
</Window>
DialogWindow.xaml
<UserControl
    x:Class="ResponseDialogTest.Views.DialogWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <UserControl.Resources>
        <Style BasedOn="{StaticResource MaterialDesignFlatButton}" TargetType="{x:Type Button}">
            <Setter Property="Margin" Value="10" />
            <Setter Property="Width" Value="150" />
        </Style>
    </UserControl.Resources>
    <StackPanel>
        <TextBlock
            Margin="15"
            HorizontalAlignment="Center"
            FontSize="24"
            Text="ほんとうに実行しますか?" />
        <StackPanel Orientation="Horizontal">
            <Button Command="{Binding OkCommand}" Content="OK" />
            <Button Command="{Binding CancelCommand}" Content="Cancel" />
        </StackPanel>
    </StackPanel>
</UserControl>

ViewModel

MainWindowViewModel.cs
public class MainWindowViewModel : ViewModel
{
    /// <summary>
    /// ダイアログ処理実行コマンド
    /// </summary>
    public AsyncReactiveCommand StartDialogCommand { get; } = new AsyncReactiveCommand();

    /// <summary>
    /// ダイアログ内容ViewModel
    /// </summary>
    public DialogWindowViewModel DialogVM { get; } = new DialogWindowViewModel();

    /// <summary>
    /// ダイアログ結果文字列
    /// </summary>
    public ReactiveProperty<string> DialogResultText { get; } = new ReactiveProperty<string>();

    public MainWindowViewModel()
    {
        StartDialogCommand.Subscribe(StartDialog);
    }

    private async Task StartDialog()
    {
        //ダイアログ内容VMのOpenプロパティを変更することで、ダイアログのOpen・Closeを制御する
        DialogVM.IsOpen.Value = true;

        //ダイアログのOpenプロパティが変更されるのを待つ
        await DialogVM.IsOpen;

        //ダイアログ結果を文字列に加工して表示
        DialogResultText.Value = DialogVM.IsOK.Value == true ? "おーけー" : "きゃんせる";
    }
}
DialogWindowViewModel.cs
public class DialogWindowViewModel
{
    /// <summary>
    /// ダイアログのOpen状態
    /// </summary>
    public ReactiveProperty<bool> IsOpen { get; } = new ReactiveProperty<bool>(initialValue: false);

    /// <summary>
    /// ダイアログ結果(初期状態はNull)
    /// </summary>
    public ReactiveProperty<bool?> IsOK { get; } = new ReactiveProperty<bool?>(initialValue: null);

    public ReactiveCommand OkCommand { get; } = new ReactiveCommand();
    public ReactiveCommand CancelCommand { get; } = new ReactiveCommand();

    public DialogWindowViewModel()
    {
        //ダイアログが開いたときにダイアログ結果をNullに設定
        IsOpen
            .Where(x => x)
            .Subscribe(_ =>
                IsOK.Value = null);

        OkCommand.Subscribe(() =>
            {
                IsOK.Value = true;
                IsOpen.Value = false;
            });

        CancelCommand.Subscribe(() =>
            {
                IsOK.Value = false;
                IsOpen.Value = false;
            });
    }
}

環境

VisualStudio2017
.NET Framework 4.7.1
C#7.1
ReactiveProperty 5.2.0
MaterialDesignThemes 2.4.1.1101

12
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
15