WPF Prism episode: 13 ~ カスタマイズしたらメッセージボックスだった件 ~
前回は Prism に標準組み込みのメッセージボックスを表示する方法を紹介したので、今回は前回エントリに引き続き Prism の INotification と IConfirmation を応用して自作したメッセージボックスを表示する方法を紹介します。
2019/7/25 に Prism 7.2 がリリースされ、ダイアログの表示に使用していた INotification、IConfirmation が廃止になり、新たに IDialogService が導入されました。
【WPF Prism episode: 16】で使用方法を紹介しています。
尚、この記事は Visual Studio 2017 Community Edition で .NET Framework 4.7.2 以上 と C# + Prism 7.1 + ReactiveProperty + Extended WPF Toolkit™ を使用して 、WPF アプリケーションを MVVM パターンで作成するのが目的で、C# での基本的なコーディング知識を持っている人が対象です。
Prism のメッセージボックスをカスタマイズする
前回紹介した Prism 組み込みのメッセージボックスは、確認メッセージボックスの「OK」、「キャンセル」ボタンを「はい」と「いいえ」に変更したり、MessageBox クラスに表示されるようなお馴染みのアイコン等を追加するようなことはできませんが自作したメッセージボックスを表示することはできます。
ただ、自作とは言っても開発者が指定できるのはメッセージボックスのコンテンツ(中身)部分を UserControl として作成できるだけで、外側の枠(ウィンドウ)部分は Prism が内部で生成するため基本的には手が出せません。
INotification で表示するメッセージボックス(OK ボタンのみのメッセージボックス)をカスタマイズする例は episode: 2 でも紹介した Prism 公式サンプル No.27 CustomContent にある(2022/8/23 現在、ページが存在しないようです)ので、ここでは「OK」・「キャンセル」ボタンのある確認メッセージボックスをカスタマイズします。
とは言っても公式サンプルに少し手を加えるだけで、ほとんど公式サンプルと変わりません。
後述しますが、公式サンプルの Notification ベースのメッセージボックスはコードビハインドで作成されているので、MVVM パターンで作成する例を紹介しようと思って作ってみたんですが… メッセージボックスを MVVM パターンで作成するメリットはありませんでした。
メッセージボックスのプロパティやコマンドを VM にバインドするとしても、バインドが必要な項目自体多くありませんし煩雑になるだけで紹介する程の情報はありませんでした。
どうしても全て MVVM パターンで作らないと気が済まないという場合以外ではコードビハインドで作成することをお勧めするので、ここで紹介する自作メッセージボックスも公式サンプルと同じくコードビハインドで作成します。
Prism 組み込みのメッセージボックスをカスタマイズするための XAML
まず最初にメッセージボックスのコンテンツ部をレイアウトするための UserControl をプロジェクトへ追加しますが、ここでは前回エントリでメッセージボックス表示用 Service を追加した WpfTestAppServices プロジェクトへ【Prism UserControl】を追加します。
今回は Service 用のプロジェクトが既に存在したため流用しましたが、本来このようなプロジェクトは自作のベースライブラリ用に独立したプロジェクトとして作成した方が良いと思います。
WpfTestAppServices プロジェクトは元々クラスライブラリプロジェクトとして作成したため、UserControl をプロジェクトに追加するためには Presentation.Core 等を別途参照に追加する必要があり、実際に新しいプロジェクトを作成する場合は Prism Module プロジェクトか、カスタムコントロールプロジェクトで作成する方が楽だと思います。
平凡な見た目ですが、下 fig. 1 のようなメッセージボックスを作成します。
UserControl をプロジェクトへ追加したら以下のようにXAML を編集します。ただ、以下のサンプルコード通りに作る必要はなく、確認メッセージボックスなので「はい」、「いいえ」の役割を持つ要素は必須ですが、それ以外は好きなように作成して構いません。
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 | <UserControl x:Class="WpfTestApp.Views.ConfirmPopup" 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" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"> <Grid> <StackPanel> <TextBlock Text="{Binding Title}" FontSize="24" HorizontalAlignment="Center" /> <StackPanel Orientation="Horizontal"> <Image Source="pack://application:,,,/WpfTestAppServices;component/icons8-ask-question-96.png" Width="64" Height="64" /> <TextBlock Text="{Binding Content}" Margin="10"/> </StackPanel> <StackPanel Orientation="Horizontal"> <Button Content="はい" Margin="25" Width="80" IsDefault="True" Click="YesButton_Click" /> <Button Content="いいえ" Margin="25" Width="80" IsCancel="True" Click="NoButton_Click" /> </StackPanel> </StackPanel> </Grid> </UserControl> |
今までこの連載で紹介してきた UserControl との最大の違いは、名前空間のエイリアスを宣言した中に Prism のエイリアスが存在しない事と、【prism:ViewModelLocator.AutowireViewModel=”True”】も存在しない事です。
このメッセージボックスはコードビハインドだけで動作するため VM は使用しないので、AutowireViewModel=”True” が指定されている場合は削除してください。
IInteractionRequestAware を経由してメッセージボックスを操作する
MVVM パターンでコマンドを指定する場合と違い、コードビハインドにイベントハンドラを追加するには、Windows Form と同じく XAML のデザインビューから対象コントロールをダブルクリックするか、下 fig.2 のようにプロパティウィンドウを使用します。
プロパティウィンドウからイベントハンドラを追加する方法も Windows Form と全く同じで、プロパティウィンドウ右上の稲妻アイコンをクリックしてイベント用の View に切り替え、追加したいイベント(ここでは Click)をダブルクリックすれば XAML とコードビハインドにイベントが自動的に追加されます。
(イベント名を入力して Enter でも追加されます)
そして確認メッセージボックスのコードビハインドを以下のように編集します。
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | using System; using System.Windows.Controls; using Prism.Interactivity.InteractionRequest; namespace WpfTestApp.Views { /// <summary> /// Interaction logic for ConfirmPopup /// </summary> public partial class ConfirmPopup : UserControl, IInteractionRequestAware { private IConfirmation confirmation; /// <summary>Prismとやり取りするINotificationインタフェースを取得・設定します。</summary> public INotification Notification { get { return this.confirmation; } set { this.confirmation = (IConfirmation)value; } } /// <summary>終了処理を表すActionを取得・設定します。。</summary> public Action FinishInteraction { get; set; } /// <summary>はいボタンのイベントハンドラ</summary> /// <param name="sender">イベントのソース。</param> /// <param name="e">イベントデータを表すRoutedEventArgs。</param> private void YesButton_Click(object sender, System.Windows.RoutedEventArgs e) { this.confirmation.Confirmed = true; this.FinishInteraction?.Invoke(); } /// <summary>いいえボタンのイベントハンドラ</summary> /// <param name="sender">イベントのソース。</param> /// <param name="e">イベントデータを表すRoutedEventArgs。</param> private void NoButton_Click(object sender, System.Windows.RoutedEventArgs e) { this.confirmation.Confirmed = false; this.FinishInteraction?.Invoke(); } /// <summary>コンストラクタ。</summary> public ConfirmPopup() { InitializeComponent(); } } } |
まず今まで紹介してきた UserControl と違い、メッセージボックスに使用する場合は 10 行目のように IInteractionRequestAware インタフェースを継承する必要があり、Notification、FinishInteraction プロパティは IInteractionRequestAware インタフェースのメンバです。
公式サンプルとほんの少し違うのは、12 ~ 19 行目の Notification プロパティを自動実装プロパティではなく通常のプロパティにした部分で、これは Prism からセットされる INotification を IConfirmation にキャストするためです。
大した意味は無く、「はい」・「いいえ」ボタン Click のイベントハンドラ内で IConfirmation を使用したいからで、プロパティ設定時にキャストせず個々のベントハンドラ内でキャストしても構いません。
そして前述の通り、27 ~ 31 行目、36 ~ 40 行目のイベントハンドラ内で IConfirmation.Confirmed プロパティに戻り値をセットして、戻り値セット後に IInteractionRequestAware.FinishInteraction.Invoke メソッドを実行してメッセージボックスを閉じています。
実際に作ってみると XAML へ定義した Title と Content はどこにバインドされるか不思議に思うかもしれませんが、VM から InteractionRequest.Raise メソッドでメッセージボックスを表示すると Prism はパラメータに渡した INotification をメッセージボックスの DataContext に設定します。
そのため、INotification の Title、Content プロパティがバインドされると言う仕組みです。
PopupWindowAction.WindowContent に自作したメッセージボックスを指定する
自作したメッセージボックスを表示するには、前回エントリの episode: 12 で紹介した、MainWindow.xaml の記述を以下のように少し変更するだけで表示できます。
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 | <Window x:Class="WpfTestApp.Views.MainWindow" ~ 略 ~ xmlns:local="clr-namespace:WpfTestApp.Views" xmlns:srvc="clr-namespace:WpfTestApp.Views;assembly=WpfTestAppServices" ~ 略 ~ WindowStartupLocation="CenterScreen" IsTabStop="False"> ~ 略 ~ <i:Interaction.Triggers> <prism:InteractionRequestTrigger SourceObject="{Binding MessageBoxRequest}"> <prism:PopupWindowAction IsModal="True" WindowStartupLocation="CenterOwner"> <prism:PopupWindowAction.WindowStyle> <Style TargetType="Window"> <Setter Property="ShowInTaskbar" Value="False" /> <Setter Property="SizeToContent" Value="WidthAndHeight" /> <Setter Property="ResizeMode" Value="NoResize" /> </Style> </prism:PopupWindowAction.WindowStyle> <prism:PopupWindowAction.WindowContent> <srvc:ConfirmPopup /> </prism:PopupWindowAction.WindowContent> </prism:PopupWindowAction> </prism:InteractionRequestTrigger> </i:Interaction.Triggers> ~ 略 ~ </Window> |
自作したメッセージボックスは、MainWindow と名前空間は同じですが別アセンブリなので、MainWindow から参照できるようにアセンブリ名を付与したエイリアスを宣言しています。(4 行目)
前回のエントリで紹介した内容とほとんど変わりませんが、PopupWindowAction.WindowContent に作成した UserControl 名(ConfirmPopup)を指定するだけです。
但し、16 行目に新規スタイル ResizeMode を追加してメッセージボックスウィンドウのリサイズを禁止しています。これを指定しないとリサイズ可能なメッセージボックスが表示されてしまうので指定した方が良いでしょう。
又、前回エントリで作成した MessageBoxService も変更せずそのまま使用できます。
必要があれば、メッセージボックスを呼び出すための IMessageBoxService.ShowConfirmMessage に渡す IConfirmation の Title、Content プロパティを変更してください。
後は前回のエントリで紹介した通りメッセージボックスを表示するオペレーションをすれば以下のように自作したメッセージボックスが表示されます。
このメッセージボックスのデザインはショボイですが、UserControl 上にデザインすることでメッセージボックスの外観を自分の好きなようにカスタマイズできるようになります。
尚、このサンプルのメッセージボックスに使用しているアイコンは episode: 5 でも紹介した Icons8 で配布されている画像を使用しています。
次は Prism で作成したダイアログウィンドウを MVVM パターンで表示する方法を紹介する予定でしたが、長くなったので次回へ持ち越しすることにします。
いつも通り今回作成したサンプルも GitHub リポジトリに上げています。
次回記事「ListBox 相手は ReactiveCollection、ダイアログな、Prism。【episode: 14 WPF Prism】」