Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

はじめに

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/

おわりに

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

kuro4
ほぼほぼ趣味でプログラム書いてます。 なにかあればTwitterの方まで。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした