WPFでMaterialDesign導入したからMessageBoxもおしゃれにしたい!


はじめに 

WPFでMaterialDesignThemes導入してせっかく見た目をかっこよくしても、
MessageBoxを表示させてみたら標準のままでダサかった…。

MaterialDesignThemesでダイアログを出す方法はなんとなくわかったけど、
標準のMessageBoxライクに簡単に表示させたい。

こちらは、そんな方に向けた記事です。

環境

  • .NET 5.0
  • C#
  • Prism.Unity
  • MaterialDesignThemes

ダイアログView

まずはUserControlとして、さくっとダイアログ本体となるものを用意します。
これさえ作っちゃえば、あとはサクサクッとできちゃいます。

MyMessageBox.xaml
<UserControl
    x:Class="TestApp.Views.MyMessageBox"
    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:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">
    <Grid Margin="16">
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <StackPanel Grid.Row="0" Orientation="Vertical">
            <TextBlock
                FontSize="20"
                FontWeight="Bold"
                Style="{StaticResource MaterialDesignBody1TextBlock}"
                Text="{Binding DialogTitle}" />
            <TextBlock
                FontSize="16"
                FontWeight="Medium"
                Style="{StaticResource MaterialDesignBody1TextBlock}"
                Text="{Binding DialogText}" />
        </StackPanel>
        <StackPanel
            Grid.Row="1"
            Margin="0,16,0,0"
            HorizontalAlignment="Right"
            Orientation="Horizontal">
            <Button
                Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
                CommandParameter="キャンセル"
                Content="キャンセル"
                IsCancel="True"
                Style="{StaticResource MaterialDesignFlatButton}" />
            <Button
                Margin="8,0,0,0"
                Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
                CommandParameter="OK"
                Content="OK"
                IsDefault="True"
                Style="{StaticResource MaterialDesignFlatButton}" />
        </StackPanel>
    </Grid>
</UserControl>

縦並びのダイアログのタイトル・テキストエリア、
横並びのキャンセル・OKボタンエリアに分かれてます。

文字の大きさやボタンの配置などは以下を参考に。

CommandParameterはダイアログのボタンを押したときに返ってくる値ですね。
文字列じゃなくても、boolにしたり、intにしたり、お好きなように。

ダイアログを表示させるViewとViewModel

ManWindow.xaml
<Window
    x:Class="TestApp.Views.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
    xmlns:prism="http://prismlibrary.com/"
    Title="{Binding Title}"
    Width="525"
    Height="350"
    prism:ViewModelLocator.AutoWireViewModel="True">
    <materialDesign:DialogHost CloseOnClickAway="True" DialogTheme="Inherit">
        <Grid>
            <Button
                Width="100"
                Command="{Binding ClickCommand}"
                Content="ボタン"
                Style="{StaticResource MaterialDesignRaisedButton}" />
            <ContentControl prism:RegionManager.RegionName="ContentRegion" />
        </Grid>
    </materialDesign:DialogHost>
</Window>

DialogHostで囲んだ部分を覆うようにダイアログのグレー背景、ダイアログが表示されるようになります。
なので画面全体に対して表示させたい場合は上記のようにします。
CloseOnClickAwayプロパティはダイアログの外側をクリックで閉じられるようにするかどうかのプロパティです。

あとはボタン等のコントロールにCommandを設定してやり、ViewModelで呼ぶだけ。

MainWindowViewModel.cs
using MaterialDesignThemes.Wpf;
using Prism.Commands;
using Prism.Mvvm;
using System.Diagnostics;
using TestApp.Views;

namespace TestApp.ViewModels
{
    public class MainWindowViewModel : BindableBase
    {
        public MainWindowViewModel()
        {

        }

        // プロパティ
        private string _title = "MessageBoxApplication";
        public string Title
        {
            get { return _title; }
            set { SetProperty(ref _title, value); }
        }

        private string _dialogTitle;
        public string DialogTitle
        {
            get { return _dialogTitle; }
            set { SetProperty(ref _dialogTitle, value); }
        }

        private string _dialogText;
        public string DialogText
        {
            get { return _dialogText; }
            set { SetProperty(ref _dialogText, value); }
        }

        // コマンド
        private DelegateCommand<string> _clickCommand;
        public DelegateCommand<string> ClickCommand =>
            _clickCommand ?? (_clickCommand = new DelegateCommand<string>(ExecuteCommandName));

        // コマンド実行メソッド
        private async void ExecuteCommandName(string parameter)
        {
            DialogTitle = "確認";
            DialogText = "ダイアログの説明文です。よろしいですか?";
            var result = await DialogHost.Show(new MyMessageBox());

            if ((string)result == "OK")
            {
                Debug.Print("OKが押されました。");
            }
            else if ((string)result == "キャンセル")
            {
                Debug.Print("キャンセルが押されました。");
            }
            else
            {
                Debug.Print("ダイアログの外が押されました。");
            }
        }
    }
}

ダイアログのタイトル・テキスト設定する部分が不格好なので改善の余地ありですが…。
とりあえず
var result = await DialogHost.Show(new MyMessageBox());
で標準のMessageBox.Showライクにダイアログが出せるようになります。


最後に

MaterialDesignThemesでのダイアログの出し方はほかにもいくつかありますが、
自分はこの形が一番しっくりきました。
Viewだけでもできるけど、Viewのコード量が増えるのが微妙だったり…。
やりやすい方法はいろいろ探せば出てくると思うので、この記事は参考程度で、自分に合ったやり方を見つけるのがいいと思います!

参考サイト