C#
WPF
PRISM
MahApps.Metro

WPF PrismのPopupWindowActionで出すダイアログをMetroWindowにする


はじめに

Prism.Wpfでダイアログを出す時、


MainWindow.xaml

<i:Interaction.Triggers>

<prism:InteractionRequestTrigger SourceObject="{Binding Hoge}">
<prism:PopupWindowAction/>
</prism:InteractionRequestTrigger>
<i:Interaction.Triggers>

Viewにこんな感じでInteractionRequestTriggerを定義して、ViewModelの表示させたい処理でInteractionRequestをRaiseすると思う

普通に使う分には何の問題もないのだが、このダイアログをMahApps.MetroのMetroWindowにしようとするといい方法が見つからない

MetroWindowをだせるものを作った


2019/03/18 更新

Prism v7.1以降ではPopupWindowActionCreateDefaultWindow()がvirtualに変更されているため、PopupWindowActionクラスを継承してoverrideするだけでよくなった

また、v7.2-preではIDialogServiceという新たなダイアログ表示機能が用意されたので、正式にリリースされればこちらを使用するようになると思う

使い方を記事にしてくれている方がいたのでリンクを貼っておく

Prism7.2(pre)の新機能 IDialogServiceを試してみた


できたもの

Prism.Interactivity.MahAppsPack


PopupMetroWindowAction.cs

public class PopupMetroWindowAction : PopupWindowAction

{
#region MetroStyle
public ResourceDictionary MetroStyle
{
get { return (ResourceDictionary)GetValue(MetroStyleProperty); }
set { SetValue(MetroStyleProperty, value); }
}
public static readonly DependencyProperty MetroStyleProperty =
DependencyProperty.Register("MetroStyle", typeof(ResourceDictionary), typeof(PopupMetroWindowAction), new PropertyMetadata(null));
#endregion
#region Accent
public Accents? Accent
{
get { return (Accents?)GetValue(AccentProperty); }
set { SetValue(AccentProperty, value); }
}
public static readonly DependencyProperty AccentProperty =
DependencyProperty.Register("Accent", typeof(Accents?), typeof(PopupMetroWindowAction), new PropertyMetadata(null));
#endregion
#region Theme
public Themes? Theme
{
get { return (Themes?)GetValue(ThemeProperty); }
set { SetValue(ThemeProperty, value); }
}
public static readonly DependencyProperty ThemeProperty =
DependencyProperty.Register("Theme", typeof(Themes?), typeof(PopupMetroWindowAction), new PropertyMetadata(null));
#endregion

protected override Window CreateWindow()
{
MetroWindow window = new DefaultMetroWindow();
this.SetMetroStyle(window);
return window;
}
protected override Window CreateDefaultWindow(INotification notification)
{
MetroWindow window = null;
if (notification is IConfirmation) window = new DefaultConfirmationMetroWindow() { Confirmation = (IConfirmation)notification };
else window = new DefaultNotificationMetroWindow() { Notification = notification };
this.SetMetroStyle(window);
return window;
}
/// <summary>
/// <see cref="MetroWindow"/>にMetroStyle(AccentとTheme)をセットする
/// <para>MetroStyleが設定されていれば最優先で適用される</para>
/// <para>MetroStyleが設定されておらず、AccentとThemeが設定されていればこれらを適用する</para>
/// <para>MetroStyleが設定されておらず、AccentとThemeの両方が設定されていなければOwnerと同じものを適用する</para>
/// </summary>
/// <param name="window"></param>
protected virtual void SetMetroStyle(MetroWindow window)
{
if (window == null) return;
if (this.MetroStyle != null)
{
window.Resources.MergedDictionaries.Add(this.MetroStyle);
return;
}
if (this.Accent.HasValue && this.Theme.HasValue)
{
MahApps.Metro.ThemeManager.ChangeAppStyle(
window,
MahApps.Metro.ThemeManager.GetAccent(AccentsExtensions.ToStringFromEnum(this.Accent.Value)),
MahApps.Metro.ThemeManager.GetAppTheme(ThemesExtensions.ToStringFromEnum(this.Theme.Value)));
return;
}
window.Owner = Window.GetWindow(this.AssociatedObject);
if (window.Owner is MetroWindow)
{
window.Resources.MergedDictionaries.AddRange(window.Owner.Resources.MergedDictionaries);
}
}
}


CreateWindow()CreateDefaultWindow()をoverrideし、PrismのDefaultPopupWindowsと同様の適切なMetroWindowを生成してSetMetroStyle()を呼んでいる

SetMetroStyle()ではMetroWindowにStyle(Accent,Theme)を適用している

MetroStyle,Accent,Themeはそれぞれ依存関係プロパティで定義し、xaml側から指定できるようにしている

MetroStyleは、直接ResourceDictionaryを記述するのもOKだが、別ファイルに

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<!--MahApps.Metro resource dictionaries. Make sure that all file names are Case Sensitive!-->
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Colors.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/FlatButton.xaml" />
<!--Accent and AppTheme setting-->
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/Red.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/BaseLight.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

こんな感じでMahApps.Metroのスタイルを定義してResourceDictionaryのSourceプロパティにこのファイルを指定するほうがいいと思う

また、ダイアログの呼び出し元のWindowがMetroWindowで、これらのプロパティを指定していない場合は呼び出し元と同じStyleを適用するようにしている


使い方

リポジトリの中にサンプルもいれている

Prism.Interactivity.MahAppsPack/Prism.Interactivity.MahAppsPack-Samples/

以下、主要部分抜粋


MainWindow.xaml

<i:Interaction.Triggers>

<!--Metro-->
<prism:InteractionRequestTrigger SourceObject="{Binding MetroNotification}">
<prismMetro:PopupMetroWindowAction
WindowStartupLocation="CenterOwner"
IsModal="True"/>
</prism:InteractionRequestTrigger>
<prism:InteractionRequestTrigger SourceObject="{Binding MetroConfirmation}">
<prismMetro:PopupMetroWindowAction
CenterOverAssociatedObject="True"
IsModal="True"/>
</prism:InteractionRequestTrigger>
<prism:InteractionRequestTrigger SourceObject="{Binding MetroWindowNotification}">
<prismMetro:PopupMetroWindowAction
CenterOverAssociatedObject="True"
IsModal="True">
<prismMetro:PopupMetroWindowAction.MetroStyle>
<ResourceDictionary Source="/Prism.Interactivity.MahAppsPack-Samples;component/MetroWindowStyle.xaml"/>
</prismMetro:PopupMetroWindowAction.MetroStyle>
<prismMetro:PopupMetroWindowAction.WindowContent>
<local:SubWindow/>
</prismMetro:PopupMetroWindowAction.WindowContent>
</prismMetro:PopupMetroWindowAction>
</prism:InteractionRequestTrigger>
</i:Interaction.Triggers>


SubWindow.xaml

<i:Interaction.Triggers>

<prism:InteractionRequestTrigger SourceObject="{Binding Notification}">
<!--指定したAccentとThemeを適用-->
<prismMetro:PopupMetroWindowAction
CenterOverAssociatedObject="True"
Accent="{Binding Accent.Value}"
Theme="{Binding Theme.Value}"/>
</prism:InteractionRequestTrigger>
<prism:InteractionRequestTrigger SourceObject="{Binding Confirmation}">
<!--OwnerのAccentとThemeを適用-->
<prismMetro:PopupMetroWindowAction
CenterOverAssociatedObject="True"/>
</prism:InteractionRequestTrigger>
</i:Interaction.Triggers>


CustomContent

PopupWindowActionを継承しているため、当然IInteractionRequestAwareを実装して独自のContentを表示することもできる

使い方は以下参照

PrismEdu/05.InteractionRequest/


おわりに

個人的にやりたかったことができたので満足

おかしい点や他にいいやり方があれば教えて下さい(人∀・)