WPFで使える『おしゃれ』かつ『自由』に表示することができるオリジナルのメッセージボックスの作成方法を紹介します。
オリジナルのメッセージボックスの完成イメージはこちら(下図)です。
MessageBox.Showでよく使われるWindowsスタイルなデザインを変えたい方には特におすすめの記事となりますので、ぜひ最後まで読んでみてください。
オススメの参考書
C#の使い方が丁寧に解説しており、「基礎からしっかりと学びたい」という初心者の方にオススメの一冊です。サンプルコードも記載してあり、各章の最後に復習問題があるので理解度を確認しながら読み進めることができます。新しい C# のバージョンにも対応している書籍です。
メッセージボックスとは
メッセージボックス(MessageBox)とは、エラーや警告を表示し、ユーザーへメッセージを伝達する場合に使用するメッセージ画面です。
C#でメッセージボックスを表示するには、MessageBox
クラスのShow
メソッドを使います。クラスの拡張機能(オーバーロード)によって様々なボタンを配置したり、アイコンを「!(ビックリマーク)」や「?(クエスチョン)」などに変更することができます。
例えば、次のようなコードを記述すればメッセージボックスを表示することができます。
MessageBox.Show(
"ここがメッセージです。",
"ここがタイトルです。",
MessageBoxButton.OKCancel,
MessageBoxImage.Information);
実行結果は次の通りです。
画面中央に表示されているのがメッセージボックスです。
WPFで綺麗なUIデザインを作成した場合、デフォルトのメッセージボックスだと少し違和感を覚えてしまいます。テキストやボタンの位置・文字の色は固定で、残念ながらデザインを変更することはできません。
それならWPFでいい感じのメッセージボックスをデザインすればいいじゃんていうことで作成してみました。デフォルトのメッセージボックスのように好きな時にいつでも表示できるメッセージボックスを作っています。
おしゃれなメッセージボックスを作成
この記事で度々紹介している『Material Design』を適用させたプロジェクトを用意します。
今回は Material Design のダイアログホストを利用して、動的に表示させる独自のメッセージボックスを作成していきます。
DialogHostを追加
メッセージボックスを表示させる Window のデザインを開いて、XAML にmaterialDesign:DialogHost
を追加します。
ここでは Window の画面にある全てのコントロールはDialogHost
の子要素として扱います。
メッセージボックスを表示させる Window の XAML は次のようになります。
<Window
x:Class="MessageBoxSample.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:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="450"
Height="300"
mc:Ignorable="d">
<materialDesign:DialogHost>
<Grid>
<Label
Margin="10,10,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Content="これはメッセージボックスを表示するサンプルコードです。" />
<Button
Width="210"
Height="30"
Margin="10,41,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Click="Button_Click"
Content="メッセージボックスを表示する" />
</Grid>
</materialDesign:DialogHost>
</Window>
メッセージボックスをデザイン
プロジェクトに新しくユーザーコントロールを追加します。
ユーザーコントロールを追加するには、まず ソリューション エクスプローラーを開きます。
プロジェクトの名前にカーソルを合わせてから右クリックして、表示された画面から「追加」 > 「新しい項目」の順に選択して、ユーザーコントロールを追加します。
このユーザーコントロールがメッセージボックスのデザインになります。
MaterialDesignで用意されているアイコンや TextBlock を配置していい感じにデザインします。
ユーザーコントロールのXAMLは以下の通りです。プロパティを駆使して微調整しているのでコードが少し長くなっています。
<UserControl
x:Class="MessageBoxSample.MsgBox"
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"
Width="300"
Height="150"
mc:Ignorable="d">
<Grid
Width="300"
Height="150"
HorizontalAlignment="Center">
<StackPanel Margin="15" Orientation="Horizontal">
<materialDesign:PackIcon
Width="50"
Height="50"
Foreground="{StaticResource PrimaryHueMidBrush}"
Kind="AlertCircleOutline" />
<StackPanel Orientation="Vertical">
<TextBlock
Width="200"
FontSize="16"
Foreground="DarkSlateGray"
TextWrapping="Wrap">
Message
</TextBlock>
<TextBlock
Name="txtMessage"
Width="200"
Margin="15,5"
FontSize="14"
Foreground="DarkGray"
TextWrapping="Wrap" />
</StackPanel>
</StackPanel>
<StackPanel
Margin="15"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Orientation="Horizontal">
<Button
Width="85"
Margin="2"
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
Content="OK"
Style="{DynamicResource MaterialDesignFlatButton}" />
<Button
Width="85"
Margin="2"
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
Content="Cancel"
Style="{DynamicResource MaterialDesignFlatButton}" />
</StackPanel>
</Grid>
</UserControl>
コードビハインド側はとてもシンプルです。ユーザーコントロールのコンストラクタに表示するメッセージの引数を追加します。受け取った引数を TextBlock の Textプロパティ に設定します。
using System.Windows.Controls;
namespace MessageBoxSample
{
public partial class MsgBox : UserControl
{
public MsgBox(string message)
{
InitializeComponent();
//メッセージをテキストボックスへ反映
txtMessage.Text = message;
}
}
}
メッセージボックスを表示
メッセージボックスを表示させるWindowにDialogHost
のShow
メソッドを追加します。
この時、Show
メソッドの引数に先ほど作成したユーザーコントロールのオブジェクトを引数として渡します。
メッセージボックスに表示するメッセージは、ユーザーコントロールのインスタンスを生成する時に指定します。
using System.Windows;
using MaterialDesignThemes.Wpf;
namespace MessageBoxSample
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
await DialogHost.Show(new MsgBox("ボタンがクリックされました。"));
}
}
}
ここで注意したいのは、DialogHost のShow
メソッドは非同期で実行されます。C#の非同期処理のお約束である「async」と「await」は必ず付けるようにしましょう。このお約束を書いていないと、メッセージボックスを表示したら次の処理が実行されてしまいます。
プログラムの実行結果
ここまで出来たらプログラムを実行してみましょう。
MainWindow のボタンをクリックすると、独自に作成したメッセージボックスが表示されます。
従来のメッセージボックスとは違い、オリジナルのメッセージボックスを作成することができます。
また、DialogHost
のShow
メソッドを呼び出せば、好きなタイミングでメッセージボックスを表示することができます。
独自メッセージボックスの特徴
- メッセージの色をカスタマイズできる
- アイコンの種類・色をカスタマイズできる(MaterialDesignのアイコンに限る)
- メッセージボックスを表示したウィンドウを薄暗くする
- デザインを自由に変更できる
メッセージボックスを更に拡張
先ほど作成したメッセージボックスをもう少し拡張させます。
戻り値の取得
ユーザーに対してメッセージボックスを表示して、ユーザーがどのボタンをクリックしたかを把握したい場合があります。
DialogHost
のShow
メソッドでメッセージボックスを表示した場合も戻り値の取得が可能です。
上記で紹介しているソースコードだと null しか返さないので、メッセージボックスのデザイン担当のユーザーコントロールに配置しているボタンに戻り値と返す値を設定します。
今回の場合、ボタンのCommad
でDialogHost.CloseDialogCommand
が実行されたら戻り値を返す必要があるので、CommadParameter
プロパティを使って戻り値となるデータを設定します。
<UserControl
x:Class="MessageBoxSample.MsgBox"
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:local="clr-namespace:MessageBoxSample"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:system="clr-namespace:System;assembly=mscorlib"
Width="300"
Height="150"
mc:Ignorable="d">
<Grid
Width="300"
Height="150"
HorizontalAlignment="Center">
<StackPanel Margin="15" Orientation="Horizontal">
<materialDesign:PackIcon
Width="50"
Height="50"
Foreground="{StaticResource PrimaryHueMidBrush}"
Kind="AlertCircleOutline" />
<StackPanel Orientation="Vertical">
<TextBlock
Width="200"
FontSize="16"
Foreground="DarkSlateGray"
TextWrapping="Wrap">
Message
</TextBlock>
<TextBlock
Name="txtMessage"
Width="200"
Margin="15,5"
FontSize="14"
Foreground="DarkGray"
TextWrapping="Wrap" />
</StackPanel>
</StackPanel>
<StackPanel
Margin="15"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Orientation="Horizontal">
<Button
Width="85"
Margin="2"
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
Content="OK"
Style="{DynamicResource MaterialDesignFlatButton}">
<Button.CommandParameter>
<system:Boolean>True</system:Boolean>
</Button.CommandParameter>
</Button>
<Button
Width="85"
Margin="2"
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
Content="Cancel"
Style="{DynamicResource MaterialDesignFlatButton}">
<Button.CommandParameter>
<system:Boolean>False</system:Boolean>
</Button.CommandParameter>
</Button>
</StackPanel>
</Grid>
</UserControl>
あとはメッセージボックスを表示させる Window 側で戻り値を受け取って、if 条件で分岐させれば、ユーザーがクリックしたボタンに応じて処理を分岐させることができます。
using System.Windows;
using System.Diagnostics;
using MaterialDesignThemes.Wpf;
namespace MessageBoxSample
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
var result = await DialogHost.Show(new MsgBox("ボタンがクリックされました。"));
if ((bool)result)
{
Debug.WriteLine("OKボタンがクリックされました。");
}
else
{
Debug.WriteLine("Cancelボタンがクリックされました。");
}
}
}
}
メッセージの表示・非表示
メッセージとして表示するテキストが多いと、ユーザーにとっては分かりにくいメッセージとなることがあります。
メッセージの内容をユーザーが知りたい時にだけ表示するような仕組みがあるといいかもしれませんね。
メッセージの表示・非表示は Expander コントロールを使えば以外と簡単に実装できます。
<UserControl
x:Class="MessageBoxSample.MsgBox"
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:local="clr-namespace:MessageBoxSample"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:system="clr-namespace:System;assembly=mscorlib"
Width="300"
Height="150"
mc:Ignorable="d">
<Grid
Width="300"
Height="150"
HorizontalAlignment="Center">
<StackPanel Margin="15" Orientation="Horizontal">
<materialDesign:PackIcon
Width="50"
Height="50"
Foreground="{StaticResource PrimaryHueMidBrush}"
Kind="AlertCircleOutline" />
<Expander
Width="220"
HorizontalAlignment="Stretch"
Foreground="DarkSlateGray"
Header="Message">
<StackPanel Orientation="Vertical">
<TextBlock
Name="txtMessage"
Width="200"
FontSize="14"
Foreground="DarkGray"
TextWrapping="Wrap" />
</StackPanel>
</Expander>
</StackPanel>
<StackPanel
Margin="15"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Orientation="Horizontal">
<Button
Width="85"
Margin="2"
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
Content="OK"
Style="{DynamicResource MaterialDesignFlatButton}">
<Button.CommandParameter>
<system:Boolean>True</system:Boolean>
</Button.CommandParameter>
</Button>
<Button
Width="85"
Margin="2"
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
Content="Cancel"
Style="{DynamicResource MaterialDesignFlatButton}">
<Button.CommandParameter>
<system:Boolean>False</system:Boolean>
</Button.CommandParameter>
</Button>
</StackPanel>
</Grid>
</UserControl>
上記のコードを実行すると次のようなメッセージボックスが表示されます。
まとめ
この記事では動的におしゃれなメッセージボックスを表示する方法を紹介しました。
『Material Design』がプロジェクトにインストールされていることが条件となってしまいますが、従来のMessageBoxより見た目はぐっと良くなると思います。
自由にカスタマイズができるので全て網羅できていないですが、この記事が何かの参考になれば幸いです。
以上、最後まで読んで頂きありがとうございました。