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

# はじめに
Prism.Wpfでダイアログを出す時、

```xml: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以降では`PopupWindowAction`の`CreateDefaultWindow()`がvirtualに変更されているため、`PopupWindowAction`クラスを継承してoverrideするだけでよくなった
また、v7.2-preでは`IDialogService`という新たなダイアログ表示機能が用意されたので、正式にリリースされればこちらを使用するようになると思う
使い方を記事にしてくれている方がいたのでリンクを貼っておく
[Prism7.2(pre)の新機能 IDialogServiceを試してみた](https://qiita.com/kwhrkzk/items/8d5c78e68b34fef38ff2)

# できたもの
[Prism.Interactivity.MahAppsPack](https://github.com/Kuro4/Prism.Interactivity.MahAppsPack)

```csharp: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](https://github.com/PrismLibrary/Prism/tree/master/Source/Wpf/Prism.Wpf/Interactivity/DefaultPopupWindows)と同様の適切なMetroWindowを生成して`SetMetroStyle()`を呼んでいる
`SetMetroStyle()`ではMetroWindowにStyle(Accent,Theme)を適用している
MetroStyle,Accent,Themeはそれぞれ依存関係プロパティで定義し、xaml側から指定できるようにしている
MetroStyleは、直接ResourceDictionaryを記述するのもOKだが、別ファイルに

```xml
<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/](https://github.com/Kuro4/Prism.Interactivity.MahAppsPack/tree/master/Prism.Interactivity.MahAppsPack-Samples)
以下、主要部分抜粋

```xml: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>
```

```xml: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/](https://github.com/runceel/PrismEdu/tree/master/05.InteractionRequest)

# おわりに
個人的にやりたかったことができたので満足
おかしい点や他にいいやり方があれば教えて下さい(人∀・)