【Material Design Toolkit】 Dialog

 Material Design ToolkitのDialogはウインドウ上にダイアログを表示する機能で、表示する内容も自分で作成することから新しいウインドウを表示する感覚に近いと思います。(ファイル選択ダイアログやMessageBoxとは使い方が異なります)
 ダイアログの制御方法は複数の方法が用意されていますので、状況に合わせて一番使いやすい方法を選択してください。

1.イベントを利用する方法
 最初に説明する方法はDialogHostのイベントを利用する方法です。
 このサンプルではダイアログが閉じられた際にイベントでダイアログの戻り値を取得します。
<UserControl x:Class="DialogDemo.Views.Dialog1"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
             xmlns:system="clr-namespace:System;assembly=mscorlib"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">

    <materialDesign:DialogHost DialogClosing="DialogHost_DialogClosing">
        <materialDesign:DialogHost.DialogContent>
            <StackPanel Margin="10">
                <TextBlock Text="Dialog Sample1"/>
                <TextBlock Text="コードビハインド(DialogClosingイベント)を利用したダイアログ"/>
                <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
                    <Button Style="{StaticResource MaterialDesignFlatButton}" Margin="5"
                                Content="OK" Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}">
                        <Button.CommandParameter>
                            <system:Boolean>True</system:Boolean>
                        </Button.CommandParameter>
                    </Button>
                    <Button Style="{StaticResource MaterialDesignFlatButton}" Margin="5"
                                Content="Cancel" Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}">
                        <Button.CommandParameter>
                            <system:Boolean>False</system:Boolean>
                        </Button.CommandParameter>
                    </Button>
                </StackPanel>
            </StackPanel>
        </materialDesign:DialogHost.DialogContent>

        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>

            <TextBlock Grid.Row="0" Grid.Column="0" Text="Dialogの戻り値:"/>
            <TextBlock Grid.Row="0" Grid.Column="1" Name="DialogResultText"/>

            <Button Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2"
                Content="Dialog Open"
                Command="{x:Static materialDesign:DialogHost.OpenDialogCommand}"/>
        </Grid>
    </materialDesign:DialogHost>
</UserControl>
using MaterialDesignThemes.Wpf;
using System.Windows.Controls;

namespace DialogDemo.Views
{
    /// <summary>
    /// Dialog1.xaml の相互作用ロジック
    /// </summary>
    public partial class Dialog1 : UserControl
    {
        public Dialog1()
        {
            InitializeComponent();
        }

        private void DialogHost_DialogClosing(object sender, DialogClosingEventArgs eventArgs)
        {
            DialogResultText.Text = eventArgs.Parameter.ToString();
        }
    }
}
 このコードでは以下の様なダイアログが表示されます。
DialogSample1.png
 ダイアログを表示するためには必ずDialogHostが必要になります。
 DialogHostの位置はどこでもよいですが、この領域内にダイアログが表示されるのであまり狭い場所には設定しない方がよさそうです。
 サンプル1ではDialogHostのDialogClosingイベントを利用してダイアログの戻り値を取得しています。
 表示するダイアログの内容はDialogHost.DialogContentに定義します。
 ダイアログの表示はコマンドを使用して行います。
 ダイアログを表示したい場合はmaterialDesign:DialogHost.OpenDialogCommandを使用します。
 また、ダイアログを閉じたい場合はmaterialDesign:DialogHost.CloseDialogCommandを使用します。
 ダイアログを閉じる際にCommandParameterに値を渡して戻り値を返すこともできます。
 このサンプルの場合はOKボタンを押してダイアログを閉じた場合はTrueが、Cancelボタンを押してダイアログを閉じた場合はFalseが返ってきます。
 このサンプルではbool値を返しましたが他の型(文字列等)を返すこともできます。
 この戻り値はDialogClosingイベントのイベント変数を使って受け取ることができます。
 eventArgs.ParameterにCommandParameterの値が入っているのでキャストして使用してください。

2.OpenDialogCommandの引数に表示するダイアログを指定する方法
 2つ目の方法はダイアログの内容をOpenDialogCommandの引数として渡す方法です。
 1つめの方法ではDialogContentの内容が固定されていましたが、この方法では1つのDialogHostで色々な内容のダイアログを表示することができます。
<UserControl x:Class="DialogDemo.Views.Dialog2"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <StackPanel Grid.Row="0">
            <TextBlock Text="Dialog Sample2"/>
            <TextBlock Text=""/>
        </StackPanel>

        <materialDesign:DialogHost Grid.Row="1">
            <StackPanel>
                <Button Command="{x:Static materialDesign:DialogHost.OpenDialogCommand}"
                        Margin="10" Content="ダイアログを表示1">
                    <Button.CommandParameter>
                        <StackPanel Margin="10">
                            <TextBlock Text="OpenDialogCommandのCommandParameterによるダイアログ1"/>
                            <Button Content="閉じる"
                                    Style="{StaticResource MaterialDesignFlatButton}"
                                    Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"/>
                        </StackPanel>
                    </Button.CommandParameter>
                </Button>

                <Button Command="{x:Static materialDesign:DialogHost.OpenDialogCommand}"
                        Margin="10" Content="ダイアログを表示2">
                    <Button.CommandParameter>
                        <StackPanel Margin="10">
                            <TextBlock Text="OpenDialogCommandのCommandParameterによるダイアログ2"/>
                            <Button Content="閉じる"
                                    Style="{StaticResource MaterialDesignFlatButton}"
                                    Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"/>
                        </StackPanel>
                    </Button.CommandParameter>
                </Button>
            </StackPanel>
        </materialDesign:DialogHost>
    </Grid>
</UserControl>
 このコードでは以下の様なダイアログが表示されます。
DialogSample2.png
 この方法ではDialogHostのDialogContentには何も指定しません。
 かわりにmaterialDesign:DialogHost.OpenDialogCommandのCommandParameterにダイアログコンテンツを指定します。

 上記のサンプルコードではCommandParameter内に直接ダイアログコンテンツを記載しましたが、UserControl等で作成したダイアログを使用することもできます。
 以下のサンプルコードはダイアログをUserControlで作成しておき、ViewModelをバインディングして呼び出すコードです。
 使用しているSampleDialogとSampleDialogViewModelは「3.ViewModelからダイアログ表示を行う方法」で使用するダイアログを流用しています。
<UserControl x:Class="DialogDemo.Views.Dialog5"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
             xmlns:local="clr-namespace:DialogDemo.Views"
             xmlns:v="clr-namespace:DialogDemo.Views"
             xmlns:vm="clr-namespace:DialogDemo.ViewModels"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <UserControl.DataContext>
        <vm:Dialog5ViewModel/>
    </UserControl.DataContext>
    
    <materialDesign:DialogHost>
        <StackPanel>
            <Button Content="ダイアログ表示" DataContext="{Binding ViewModel}"
                    Command="{x:Static materialDesign:DialogHost.OpenDialogCommand}">
                <Button.CommandParameter>
                    <v:SampleDialog/>
                </Button.CommandParameter>
            </Button>

            <Button Content="ダイアログ表示(バインディング失敗)" 
                    Command="{x:Static materialDesign:DialogHost.OpenDialogCommand}">
                <Button.CommandParameter>
                    <v:SampleDialog DataContext="{Binding ViewModel}"/>
                </Button.CommandParameter>
            </Button>
        </StackPanel>
    </materialDesign:DialogHost>
</UserControl>
using Livet;

namespace DialogDemo.ViewModels
{
    public class Dialog5ViewModel : ViewModel
    {
        public Dialog5ViewModel()
        {
            ViewModel = new SampleDialogViewModel();
        }

        #region ViewModel変更通知プロパティ
        private SampleDialogViewModel _ViewModel;

        public SampleDialogViewModel ViewModel
        {
            get
            { return _ViewModel; }
            set
            {
                if (_ViewModel == value)
                    return;
                _ViewModel = value;
                RaisePropertyChanged();
            }
        }
        #endregion
    }
}
 ダイアログの指定方法はDialog Sample2と同じですが、ViewModelをバインディングする場合は少し注意が必要です。
 CommandParameter以下のSampleDialogのDataContentにViewModelをバインディングしてもうまくいきません。
 SampleDialogにViewModelを渡すためにはButtonのDataContentにViewModelをバインディングします。
 実際にダイアログを表示させてみると以下の様になります。
DialogSample5.png

3.ViewModelからダイアログ表示を行う方法
 3つ目の方法はViewModelからダイアログ表示を行う方法です。
 この方法では表示するダイアログをUserControl等で定義しておく必要があります。
 以下のコードがサンプル3で使用するダイアログとそのViewModelです。
<UserControl x:Class="DialogDemo.Views.SampleDialog"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
             xmlns:system="clr-namespace:System;assembly=mscorlib"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <StackPanel Margin="10">
        <TextBlock Text="Dialog Sample3"/>
        <TextBlock Text="{Binding Time, StringFormat=時刻:{0}}"/>

        <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
            <Button Content="OK" Style="{StaticResource MaterialDesignFlatButton}"
                    Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}">
                <Button.CommandParameter>
                    <system:Boolean>True</system:Boolean>
                </Button.CommandParameter>
            </Button>
            <Button Content="Cancel" Style="{StaticResource MaterialDesignFlatButton}"
                Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}">
                <Button.CommandParameter>
                    <system:Boolean>False</system:Boolean>
                </Button.CommandParameter>
            </Button>
        </StackPanel>
    </StackPanel>
</UserControl>
using Livet;
using System;

namespace DialogDemo.ViewModels
{
    public class SampleDialogViewModel : ViewModel
    {
        public SampleDialogViewModel()
        {
            Time = DateTime.Now;
        }

        #region Time変更通知プロパティ
        private DateTime _Time;

        public DateTime Time
        {
            get
            { return _Time; }
            set
            { 
                if (_Time == value)
                    return;
                _Time = value;
                RaisePropertyChanged();
            }
        }
        #endregion

    }
}
 このダイアログはViewModelのTimeプロパティで指定された時刻を表示するだけのダイアログです。
 OKボタンとCancelボタンにはそれぞれ戻り値を設定しています。

<UserControl x:Class="DialogDemo.Views.Dialog3"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
             xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
             xmlns:l="http://schemas.livet-mvvm.net/2011/wpf"
             xmlns:vm="clr-namespace:DialogDemo.ViewModels"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <UserControl.DataContext>
        <vm:Dialog3ViewModel/>
    </UserControl.DataContext>
    
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
               
        <materialDesign:DialogHost Grid.Column="0" Identifier="Dialog1" CloseOnClickAway="True">
            <StackPanel>
                <TextBlock Text="{Binding Result1, StringFormat=ダイアログ1の結果:{0}}"/>
                <Button Content="ダイアログを表示">
                    <i:Interaction.Triggers>
                        <i:EventTrigger EventName="Click">
                            <l:LivetCallMethodAction MethodTarget="{Binding}" MethodName="Open1"/>
                        </i:EventTrigger>
                    </i:Interaction.Triggers>
                </Button>
            </StackPanel>
        </materialDesign:DialogHost>

        <materialDesign:DialogHost Grid.Column="1" Identifier="Dialog2">
            <StackPanel>
                <TextBlock Text="{Binding Result2, StringFormat=ダイアログ2の結果:{0}}"/>
                <Button Content="ダイアログを表示">
                    <i:Interaction.Triggers>
                        <i:EventTrigger EventName="Click">
                            <l:LivetCallMethodAction MethodTarget="{Binding}" MethodName="Open2"/>
                        </i:EventTrigger>
                    </i:Interaction.Triggers>
                </Button>
            </StackPanel>
        </materialDesign:DialogHost>
    </Grid>
</UserControl>
using DialogDemo.Views;
using Livet;
using MaterialDesignThemes.Wpf;

namespace DialogDemo.ViewModels
{
    public class Dialog3ViewModel : ViewModel
    {

        #region Result1変更通知プロパティ
        private bool _Result1;

        public bool Result1
        {
            get
            { return _Result1; }
            set
            { 
                if (_Result1 == value)
                    return;
                _Result1 = value;
                RaisePropertyChanged();
            }
        }
        #endregion

        public async void Open1()
        {
            var dialog = new SampleDialog()
            {
                DataContext = new SampleDialogViewModel()
            };

            // ダイアログ外をクリックして閉じた場合はnullが返ってくる
            var result = await DialogHost.Show(dialog, "Dialog1");
            if(result != null) Result1 = (bool)result;
        }


        #region Result2変更通知プロパティ
        private bool _Result2;

        public bool Result2
        {
            get
            { return _Result2; }
            set
            { 
                if (_Result2 == value)
                    return;
                _Result2 = value;
                RaisePropertyChanged();
            }
        }
        #endregion

        public async void Open2()
        {
            var dialog = new SampleDialog()
            {
                DataContext = new SampleDialogViewModel()
            };

            var result = await DialogHost.Show(dialog, "Dialog2");

            Result2 = (bool)result;
        }

    }
}
 このサンプルのダイアログは以下の様に表示されます。
DialogSample3.png
 このサンプルコードではDialogHostを2つ用意しました。
 DialogHostを複数使用する場合はDialogHostのIdentifierプロパティに名前を付けておきます。
 この名前を使用してダイアログを表示させるDialogHostを指定します。
 名前を付けていないなどで使用するDialogHostが特定できない場合は例外が発生します。
 1つめのDialogHostではCloseOnClickAwayプロパティにTrueを指定しています。
 CloseOnClickAwayがTrueの場合はダイアログ外側の黒背景部分をクリックするとダイアログが閉じます。
 ボタンをクリックした際にViewModelのメソッドでダイアログの表示を行っています。
 Open1メソッド(Open2もほぼ同じ)では表示するダイアログインスタンスを作成します。(必要があればDataContentにViewModelを設定します。)
 DialogHostのShowメソッドに表示したいダイアログインスタンスと使用するDialogHostの名前を指定します。
 Showメソッドは戻り値にダイアログを閉じた際のCommandParameterの値を返します。
 欄外(黒背景)をクリックしてダイアログを閉じた場合は戻り値が存在しないためnullが返ってきます。

4.DialogHostのIsOpenプロパティを使用する方法
 DialoghostのIsOpenプロパティを使用するとコマンドを使用しなくてもダイアログの表示、非表示を制御できます。
 まず、このサンプルで表示するダイアログのコードです。
<UserControl x:Class="DialogDemo.Views.NameDialog"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
             xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
             xmlns:l="http://schemas.livet-mvvm.net/2011/wpf"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <StackPanel Margin="10">
        <TextBlock Text="Dialog Sample4"/>
        <TextBox Text="{Binding Name}" materialDesign:HintAssist.Hint="(名前)"/>

        <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
            <Button Content="OK" Style="{StaticResource MaterialDesignFlatButton}">
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="Click">
                        <l:LivetCallMethodAction MethodTarget="{Binding}" MethodName="AcceptDialog"/>
                    </i:EventTrigger>
                </i:Interaction.Triggers>
            </Button>
            <Button Content="Cancel" Style="{StaticResource MaterialDesignFlatButton}">
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="Click">
                        <l:LivetCallMethodAction MethodTarget="{Binding}" MethodName="CancelDialog"/>
                    </i:EventTrigger>
                </i:Interaction.Triggers>
            </Button>
        </StackPanel>
    </StackPanel>
</UserControl>
 このダイアログは名前入力欄を持つダイアログです。
 OKボタンを押した際にはAcceptDialogメソッドを、Cancelボタンを押した際にはCancelDialogメソッドが呼ばれるようになっています。
 ただ閉じるだけなら共通のメソッドがあればよいのですが、OKボタンとCancelボタンどちらが押されたかを判断するために別々のメソッドを割り当てています。
<UserControl x:Class="DialogDemo.Views.Dialog4"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
             xmlns:l="http://schemas.livet-mvvm.net/2011/wpf"
             xmlns:vm="clr-namespace:DialogDemo.ViewModels"
             xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <UserControl.DataContext>
        <vm:Dialog4ViewModel/>
    </UserControl.DataContext>

    <materialDesign:DialogHost DialogContent="{Binding DialogView}"
                               IsOpen="{Binding IsDialogOpen}">
        <StackPanel>
            <TextBlock Text="Dialog Sample4"/>
            <TextBlock Text="DialogHostのIsOpenによるダイアログ制御"/>
            <TextBlock Text="{Binding Name, StringFormat=名前:{0}}"/>
            <Button Content="ダイアログを表示">
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="Click">
                        <l:LivetCallMethodAction MethodTarget="{Binding}" MethodName="OpenDialog"/>
                    </i:EventTrigger>
                </i:Interaction.Triggers>
            </Button>
        </StackPanel>
    </materialDesign:DialogHost>
</UserControl>
using DialogDemo.Views;
using Livet;

namespace DialogDemo.ViewModels
{
    public class Dialog4ViewModel : ViewModel
    {
        public Dialog4ViewModel()
        {
            IsDialogOpen = false;
        }

        #region Name変更通知プロパティ
        private string _Name;

        public string Name
        {
            get
            { return _Name; }
            set
            { 
                if (_Name == value)
                    return;
                _Name = value;
                RaisePropertyChanged();
            }
        }
        #endregion

        #region IsDialogOpen変更通知プロパティ
        private bool _IsDialogOpen;

        public bool IsDialogOpen
        {
            get
            { return _IsDialogOpen; }
            set
            { 
                if (_IsDialogOpen == value)
                    return;
                _IsDialogOpen = value;
                RaisePropertyChanged();
            }
        }
        #endregion


        #region DialogView変更通知プロパティ
        private object _DialogView;

        public object DialogView
        {
            get
            { return _DialogView; }
            set
            { 
                if (_DialogView == value)
                    return;
                _DialogView = value;
                RaisePropertyChanged();
            }
        }
        #endregion

        public void OpenDialog()
        {
            Name = "";
            DialogView = new NameDialog()
            {
                DataContext = this
            };

            IsDialogOpen = true;
        }

        public void AcceptDialog()
        {
            IsDialogOpen = false;
        }

        public void CancelDialog()
        {
            IsDialogOpen = false;
            Name = "";
        }
    }
}
 このコードを実行すると以下の様なダイアログが表示されます。
DialogSample4.png
 ダイアログの表示・非表示をViewModelのIsDialogOpen(DialogHostのIsOpenとバインディング)で制御します。
 ダイアログを表示するのはDialog4、ダイアログを閉じるのはNameDialogの役目です。
 異なるViewでIsDialogOpenプロパティにアクセスする必要があるため、このサンプルではDialog4とNameDialog共通のViewModelを作成しました。(他の方法でもかまいません。とにかく、両方のViewからIsDialogOpenを設定できればOK)
 OpenDialogメソッドでは表示するダイアログインスタンスを作成し(DialogHostのDialogContentにバインディングしてある)、IsDialogOpenをtrueにしています。
 ダイアログを閉じる際にはIsDialogOpenをfalseにするだけです。


 ダイアログを表示する方法を4種類紹介しましたが、個人的には2番目か3番目の方法が使いやすいと思っています。
 
スポンサーサイト
カレンダー
07 | 2017/08 | 09
- - 1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31 - -
全記事表示リンク

全ての記事を表示する

カテゴリ
タグリスト

月別アーカイブ
08  07  06  05  04  03  02  01  12  11  10  09  08  07  06  04  03  02  01  12  11  10  09  08  07  06  05  04  03  02  01  12  11  10  09 
最新記事
リンク
最新コメント
検索フォーム
Amazon