4
12

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-03-19

はじめに

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/

おわりに

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

4
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
12