SourceChord

C#とXAML好きなプログラマの備忘録。最近はWPF系の話題が中心です。

独自のMessageBoxを作る Part2

MessageBoxはMessageBoxButton型の引数を受けることで、
表示するボタンの種類や、MessageBox.Show()メソッドの戻り値などの動作が変わります。

今回は、その辺の実装を行います。


MessageBoxの動作の違い

MessageBoxButton型の引数の値により、以下のようにメッセージボックス上に表示されるボタンの種類とShowメソッドの戻り値が変わります。

MessageBoxButtonの指定 ボタンの種類 Showメソッドの戻り値 補足
指定なし OK OK OKの場合と同じ動作
OK OK OK
「閉じる」 OK
OKCancel OK OK
Cancel Cancel
「閉じる」 Cancel
YesNo Yes Yes この時は、「閉じる」ボタンが無効になる。また、この時はAlt+F4でメッセージボックスを閉じれないようになる。
No No
YesNoCancel Yes Yes
No No
Cancel Cancel
「閉じる」 「閉じる」


ということで、これと同じ動作になるように、ボタンの表示切り替え&戻り値の切り替えを行っていきます。

ボタンの表示切り替え

表示内容の切り替えはVisualStateManagerを使って切り替えをしました。
MessageBoxButtonの4種類の値に応じたVisualStateを定義して、各Stateの時に対応するButtonのVisibilityを変更しています。

メッセージボックスの戻り値

このコントロール上のButtonクリックイベントをまとめて取得し、
イベントの発生元のButtonの名前で戻り値を振り分けています。

コード

CustomMessageBox.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace CustomControls
{
    /// <summary>
    /// このカスタム コントロールを XAML ファイルで使用するには、手順 1a または 1b の後、手順 2 に従います。
    ///
    /// 手順 1a) 現在のプロジェクトに存在する XAML ファイルでこのカスタム コントロールを使用する場合
    /// この XmlNamespace 属性を使用場所であるマークアップ ファイルのルート要素に
    /// 追加します:
    ///
    ///     xmlns:MyNamespace="clr-namespace:CustomControls"
    ///
    ///
    /// 手順 1b) 異なるプロジェクトに存在する XAML ファイルでこのカスタム コントロールを使用する場合
    /// この XmlNamespace 属性を使用場所であるマークアップ ファイルのルート要素に
    /// 追加します:
    ///
    ///     xmlns:MyNamespace="clr-namespace:CustomControls;assembly=CustomControls"
    ///
    /// また、XAML ファイルのあるプロジェクトからこのプロジェクトへのプロジェクト参照を追加し、
    /// リビルドして、コンパイル エラーを防ぐ必要があります:
    ///
    ///     ソリューション エクスプローラーで対象のプロジェクトを右クリックし、
    ///     [参照の追加] の [プロジェクト] を選択してから、このプロジェクトを参照し、選択します。
    ///
    ///
    /// 手順 2)
    /// コントロールを XAML ファイルで使用します。
    ///
    ///     <MyNamespace:CustomMessageBox/>
    ///
    /// </summary>
    [TemplatePart(Name = "OkButtonPart", Type = typeof(Button))]
    [TemplatePart(Name = "YesButtonPart", Type = typeof(Button))]
    [TemplatePart(Name = "NoButtonPart", Type = typeof(Button))]
    [TemplatePart(Name = "CancelButtonPart", Type = typeof(Button))]
    [TemplateVisualState(Name = "Ok", GroupName = "MessageBoxButtonStates")]
    [TemplateVisualState(Name = "OkCancel", GroupName = "MessageBoxButtonStates")]
    [TemplateVisualState(Name = "YesNo", GroupName = "MessageBoxButtonStates")]
    [TemplateVisualState(Name = "YesNoCancel", GroupName = "MessageBoxButtonStates")]
    public class CustomMessageBox : Window
    {
        static CustomMessageBox()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomMessageBox), new FrameworkPropertyMetadata(typeof(CustomMessageBox)));
        }

        #region メッセージボックス表示用のstaticメソッド
        public static MessageBoxResult Show(Window owner, string messageBoxText)
        {
            return CustomMessageBox.Show(owner, messageBoxText, string.Empty);
        }

        public static MessageBoxResult Show(Window owner, string messageBoxText, string caption)
        {
            return CustomMessageBox.Show(owner, messageBoxText, string.Empty, MessageBoxButton.OK);
        }

        public static MessageBoxResult Show(Window owner, string messageBoxText, string caption, MessageBoxButton button)
        {
            var msg = new CustomMessageBox()
            {
                Owner = Window.GetWindow(owner),
                Title = caption,
                Message = messageBoxText,
                ButtonType = button,
                WindowStartupLocation = System.Windows.WindowStartupLocation.CenterOwner,
            };
            msg.ShowDialog();

            return msg.Result;
        }
        #endregion

        #region Dependency Properties
        /// <summary>
        /// メッセージボックス内に表示するテキストを取得または設定します。
        /// </summary>
        public string Message
        {
            get { return (string)GetValue(MessageProperty); }
            set { SetValue(MessageProperty, value); }
        }
        // Using a DependencyProperty as the backing store for Message.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MessageProperty =
            DependencyProperty.Register("Message", typeof(string), typeof(CustomMessageBox), new PropertyMetadata(string.Empty));


        /// <summary>
        /// メッセージボックスに表示するボタンのタイプを取得または設定します。
        /// </summary>
        public MessageBoxButton ButtonType
        {
            get { return (MessageBoxButton)GetValue(ButtonTypeProperty); }
            set { SetValue(ButtonTypeProperty, value); }
        }
        // Using a DependencyProperty as the backing store for ButtonType.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ButtonTypeProperty =
            DependencyProperty.Register("ButtonType", typeof(MessageBoxButton), typeof(CustomMessageBox), new PropertyMetadata(MessageBoxButton.OK, OnButtonTypeChanged));

        private static void OnButtonTypeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var win = d as CustomMessageBox;
            if (win != null)
            {
                win.UpdateStates(true);
            }
        }

        /// <summary>
        /// メッセージボックスの結果を取得します。
        /// </summary>
        public MessageBoxResult Result
        {
            get { return (MessageBoxResult)GetValue(ResultProperty); }
            protected set { SetValue(ResultProperty, value); }
        }
        // Using a DependencyProperty as the backing store for Result.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ResultProperty =
            DependencyProperty.Register("Result", typeof(MessageBoxResult), typeof(CustomMessageBox), new PropertyMetadata(MessageBoxResult.None));

        /// <summary>
        /// ボタン領域背景部分のBorderのスタイルを取得または設定します。
        /// </summary>
        public Style ButtonGridBorderStyle
        {
            get { return (Style)GetValue(ButtonGridBorderStyleProperty); }
            set { SetValue(ButtonGridBorderStyleProperty, value); }
        }
        // Using a DependencyProperty as the backing store for ButtonGridStyle.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ButtonGridBorderStyleProperty =
            DependencyProperty.Register("ButtonGridBorderStyle", typeof(Style), typeof(CustomMessageBox), new PropertyMetadata(null));

        /// <summary>
        /// OKボタンのスタイルを取得または設定します。
        /// </summary>
        public Style OkButtonStyle
        {
            get { return (Style)GetValue(OkButtonStyleProperty); }
            set { SetValue(OkButtonStyleProperty, value); }
        }
        // Using a DependencyProperty as the backing store for OkButtonStyle.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty OkButtonStyleProperty =
            DependencyProperty.Register("OkButtonStyle", typeof(Style), typeof(CustomMessageBox), new PropertyMetadata(null));

        /// <summary>
        /// Yesボタンのスタイルを取得または設定します。
        /// </summary>
        public Style YesButtonStyle
        {
            get { return (Style)GetValue(YesButtonStyleProperty); }
            set { SetValue(YesButtonStyleProperty, value); }
        }
        // Using a DependencyProperty as the backing store for YesButtonStyle.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty YesButtonStyleProperty =
            DependencyProperty.Register("YesButtonStyle", typeof(Style), typeof(CustomMessageBox), new PropertyMetadata(null));

        /// <summary>
        /// Noボタンのスタイルを取得または設定します。
        /// </summary>
        public Style NoButtonStyle
        {
            get { return (Style)GetValue(NoButtonStyleProperty); }
            set { SetValue(NoButtonStyleProperty, value); }
        }
        // Using a DependencyProperty as the backing store for NoButtonStyle.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty NoButtonStyleProperty =
            DependencyProperty.Register("NoButtonStyle", typeof(Style), typeof(CustomMessageBox), new PropertyMetadata(null));

        /// <summary>
        /// Cancelボタンのスタイルを取得または設定します。
        /// </summary>
        public Style CancelButtonStyle
        {
            get { return (Style)GetValue(CancelButtonStyleProperty); }
            set { SetValue(CancelButtonStyleProperty, value); }
        }
        // Using a DependencyProperty as the backing store for CancelButtonStyle.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CancelButtonStyleProperty =
            DependencyProperty.Register("CancelButtonStyle", typeof(Style), typeof(CustomMessageBox), new PropertyMetadata(null));

        /// <summary>
        /// OKボタンに表示する文字列を取得または設定します。
        /// </summary>
        public string OkButtonContent
        {
            get { return (string)GetValue(OkButtonContentProperty); }
            set { SetValue(OkButtonContentProperty, value); }
        }
        // Using a DependencyProperty as the backing store for OkButtonContent.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty OkButtonContentProperty =
            DependencyProperty.Register("OkButtonContent", typeof(string), typeof(CustomMessageBox), new PropertyMetadata("OK"));

        /// <summary>
        /// Yesボタンに表示する文字列を取得または設定します。
        /// </summary>
        public string YesButtonContent
        {
            get { return (string)GetValue(YesButtonContentProperty); }
            set { SetValue(YesButtonContentProperty, value); }
        }
        // Using a DependencyProperty as the backing store for YesButtonContent.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty YesButtonContentProperty =
            DependencyProperty.Register("YesButtonContent", typeof(string), typeof(CustomMessageBox), new PropertyMetadata("はい"));

        /// <summary>
        /// Noボタンに表示する文字列を取得または設定します。
        /// </summary>
        public string NoButtonContent
        {
            get { return (string)GetValue(NoButtonContentProperty); }
            set { SetValue(NoButtonContentProperty, value); }
        }
        // Using a DependencyProperty as the backing store for NoButtonContent.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty NoButtonContentProperty =
            DependencyProperty.Register("NoButtonContent", typeof(string), typeof(CustomMessageBox), new PropertyMetadata("いいえ"));

        /// <summary>
        /// キャンセルボタンに表示する文字列を取得または設定します。
        /// </summary>
        public string CancelButtonContent
        {
            get { return (string)GetValue(CancelButtonContentProperty); }
            set { SetValue(CancelButtonContentProperty, value); }
        }
        // Using a DependencyProperty as the backing store for CancelButtonContent.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CancelButtonContentProperty =
            DependencyProperty.Register("CancelButtonContent", typeof(string), typeof(CustomMessageBox), new PropertyMetadata("キャンセル"));

        #endregion



        protected override void OnSourceInitialized(EventArgs e)
        {
            base.OnSourceInitialized(e);

            // ウィンドウ左上のアイコンを消す
            IconHelper.RemoveIcon(this);
            // ボタンの押下イベントを登録
            this.AddHandler(System.Windows.Controls.Primitives.ButtonBase.ClickEvent, new RoutedEventHandler(this.Button_Click));
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            UpdateStates(false);
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Button button = e.OriginalSource as Button;
            if (button == null)
                return;

            switch (button.Name)
            {
                case "OkButtonPart":
                    this.Result = MessageBoxResult.OK;
                    break;
                case "YesButtonPart":
                    this.Result = MessageBoxResult.Yes;
                    break;
                case "NoButtonPart":
                    this.Result = MessageBoxResult.No;
                    break;
                case "CancelButtonPart":
                    this.Result = MessageBoxResult.Cancel;
                    break;
                default:
                    this.Result = MessageBoxResult.None;
                    break;
            }

            this.DialogResult = true;
        }

        protected override void OnClosed(EventArgs e)
        {
            base.OnClosed(e);
            if (this.Result == MessageBoxResult.None)
            {
                // メッセージボックスの「閉じる」ボタンが押された場合の処理
                switch (this.ButtonType)
                {
                    case MessageBoxButton.OK:
                        this.Result = MessageBoxResult.OK;
                        break;
                    case MessageBoxButton.OKCancel:
                        this.Result = MessageBoxResult.Cancel;
                        break;
                    case MessageBoxButton.YesNo:
                        // YesNoの動作の時は、必ず「Yes」「No」どちらかのボタンを押して選択されなければならない。
                        this.Result = MessageBoxResult.None;
                        throw new Exception();
                    case MessageBoxButton.YesNoCancel:
                        this.Result = MessageBoxResult.Cancel;
                        break;
                    default:
                        break;
                }
            }
        }


        /// <summary>
        /// このコントロールのVisualStateを更新します
        /// </summary>
        /// <param name="useTransitions"></param>
        private void UpdateStates(bool useTransitions)
        {
            switch (this.ButtonType)
            {
                case MessageBoxButton.OK:
                    VisualStateManager.GoToState(this, "Ok", useTransitions);
                    break;
                case MessageBoxButton.OKCancel:
                    VisualStateManager.GoToState(this, "OkCancel", useTransitions);
                    break;
                case MessageBoxButton.YesNo:
                    VisualStateManager.GoToState(this, "YesNo", useTransitions);
                    break;
                case MessageBoxButton.YesNoCancel:
                    VisualStateManager.GoToState(this, "YesNoCancel", useTransitions);
                    break;
                default:
                    break;
            }
        }
    }

    internal static class IconHelper
    {
        [DllImport("user32.dll")]
        static extern int GetWindowLong(IntPtr hwnd, int index);

        [DllImport("user32.dll")]
        static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle);

        [DllImport("user32.dll")]
        static extern bool SetWindowPos(IntPtr hwnd, IntPtr hwndInsertAfter, int x, int y, int width, int height, uint flags);

        [DllImport("user32.dll")]
        static extern IntPtr SendMessage(IntPtr hwnd, uint msg, IntPtr wParam, IntPtr lParam);

        const int GWL_EXSTYLE = -20;
        const int WS_EX_DLGMODALFRAME = 0x0001;
        const int SWP_NOSIZE = 0x0001;
        const int SWP_NOMOVE = 0x0002;
        const int SWP_NOZORDER = 0x0004;
        const int SWP_FRAMECHANGED = 0x0020;
        const uint WM_SETICON = 0x0080;

        public static void RemoveIcon(Window window)
        {
            IntPtr hwnd = new WindowInteropHelper(window).Handle;

            int extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
            SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_DLGMODALFRAME);

            SetWindowPos(hwnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
        }
    }
}
Generic.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:CustomControls">
    <Style x:Key="MessageBoxButtonBaseStyle" TargetType="{x:Type Button}">
        <Setter Property="MinWidth" Value="88" />
        <Setter Property="Margin" Value="4" />
        <Setter Property="VerticalAlignment" Value="Center" />
        <Setter Property="Padding" Value="3" />
    </Style>

    <Style TargetType="{x:Type local:CustomMessageBox}">
        <Setter Property="Background" Value=" White" />
        <Setter Property="MinWidth" Value="154" />
        <Setter Property="MinHeight" Value="140" />
        <Setter Property="ResizeMode" Value="NoResize" />
        <Setter Property="ShowInTaskbar" Value="False" />
        <Setter Property="SizeToContent" Value="WidthAndHeight" />

        <Setter Property="ButtonGridBorderStyle">
            <Setter.Value>
                <Style TargetType="{x:Type Border}">
                    <Setter Property="Background" Value="#F0F0F0" />
                </Style>
            </Setter.Value>
        </Setter>
        <Setter Property="OkButtonStyle">
            <Setter.Value>
                <Style BasedOn="{StaticResource MessageBoxButtonBaseStyle}" TargetType="{x:Type Button}" />
            </Setter.Value>
        </Setter>
        <Setter Property="YesButtonStyle">
            <Setter.Value>
                <Style BasedOn="{StaticResource MessageBoxButtonBaseStyle}" TargetType="{x:Type Button}" />
            </Setter.Value>
        </Setter>
        <Setter Property="NoButtonStyle">
            <Setter.Value>
                <Style BasedOn="{StaticResource MessageBoxButtonBaseStyle}" TargetType="{x:Type Button}" />
            </Setter.Value>
        </Setter>
        <Setter Property="CancelButtonStyle">
            <Setter.Value>
                <Style BasedOn="{StaticResource MessageBoxButtonBaseStyle}" TargetType="{x:Type Button}" />
            </Setter.Value>
        </Setter>

        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:CustomMessageBox}">
                    <Border Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}">
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="MessageBoxButtonStates">
                                <VisualState x:Name="Ok">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="OkButtonPart" Storyboard.TargetProperty="(UIElement.Visibility)">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}" />
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="OkCancel">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="OkButtonPart" Storyboard.TargetProperty="(UIElement.Visibility)">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="CancelButtonPart" Storyboard.TargetProperty="(UIElement.Visibility)">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}" />
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="YesNo">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="YesButtonPart" Storyboard.TargetProperty="(UIElement.Visibility)">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="NoButtonPart" Storyboard.TargetProperty="(UIElement.Visibility)">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}" />
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="YesNoCancel">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="YesButtonPart" Storyboard.TargetProperty="(UIElement.Visibility)">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="NoButtonPart" Storyboard.TargetProperty="(UIElement.Visibility)">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="CancelButtonPart" Storyboard.TargetProperty="(UIElement.Visibility)">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}" />
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition />
                                <RowDefinition Height="48" />
                            </Grid.RowDefinitions>
                            <!--  メッセージ本文  -->
                            <TextBlock Margin="12,26,39,27"
                                       HorizontalAlignment="Left"
                                       VerticalAlignment="Top"
                                       Text="{TemplateBinding Message}"
                                       TextOptions.TextFormattingMode="Display">
                                <TextBlock.Style>
                                    <Style TargetType="{x:Type TextBlock}">
                                        <!--  メッセージが空の時は、TextBlockを非表示にする  -->
                                        <Style.Triggers>
                                            <Trigger Property="Text" Value="{x:Null}">
                                                <Setter Property="Visibility" Value="Collapsed" />
                                            </Trigger>
                                            <Trigger Property="Text" Value="">
                                                <Setter Property="Visibility" Value="Collapsed" />
                                            </Trigger>
                                        </Style.Triggers>
                                    </Style>
                                </TextBlock.Style>
                            </TextBlock>

                            <!--  メッセージボックス下部のボタン領域  -->
                            <Border Grid.Row="1" Style="{TemplateBinding ButtonGridBorderStyle}">
                                <Grid Margin="38,0,5,0" HorizontalAlignment="Right">
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="Auto" />
                                        <ColumnDefinition Width="Auto" />
                                        <ColumnDefinition Width="Auto" />
                                        <ColumnDefinition Width="Auto" />
                                    </Grid.ColumnDefinitions>
                                    <Button x:Name="OkButtonPart"
                                            Content="{TemplateBinding OkButtonContent}"
                                            Style="{TemplateBinding OkButtonStyle}"
                                            Visibility="Collapsed" />
                                    <Button x:Name="YesButtonPart"
                                            Grid.Column="1"
                                            Content="{TemplateBinding YesButtonContent}"
                                            Style="{TemplateBinding YesButtonStyle}"
                                            Visibility="Collapsed" />
                                    <Button x:Name="NoButtonPart"
                                            Grid.Column="2"
                                            Content="{TemplateBinding NoButtonContent}"
                                            Style="{TemplateBinding NoButtonStyle}"
                                            Visibility="Collapsed" />
                                    <Button x:Name="CancelButtonPart"
                                            Grid.Column="3"
                                            Content="{TemplateBinding CancelButtonContent}"
                                            Style="{TemplateBinding CancelButtonStyle}"
                                            Visibility="Collapsed" />
                                </Grid>
                            </Border>
                        </Grid>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

表示のカスタマイズ

前回と同様にApp.xamlに以下のようなStyleを書いておくことで、表示のカスタマイズができます。

使い方
var result = CustomMessageBox.Show(this, "CustomMessageBoxのテスト\nボタンの種類も切り替えできます。", "タイトル", MessageBoxButton.YesNoCancel);
App.xaml
<Application x:Class="CustomMessageBoxSample.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:ctrl="clr-namespace:CustomControls;assembly=CustomControls"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <Style x:Key="CustomButtonStyle" TargetType="{x:Type Button}">
            <Setter Property="MinWidth" Value="88" />
            <Setter Property="Margin" Value="4" />
            <Setter Property="VerticalAlignment" Value="Center" />
            <Setter Property="Padding" Value="3" />
            <Setter Property="Height" Value="30" />
            <Setter Property="Foreground" Value="White" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Button}">
                        <Border CornerRadius="5"
                                            BorderThickness="2"
                                            Background="#AA000000"
                                            BorderBrush="#AAFFFFFF">
                            <ContentPresenter VerticalAlignment="Center"
                                                          HorizontalAlignment="Center"/>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        
        <Style TargetType="{x:Type ctrl:CustomMessageBox}">
            <Setter Property="Background">
                <Setter.Value>
                    <LinearGradientBrush EndPoint="0,1">
                        <GradientStop Color="#FFAEC9F7"/>
                        <GradientStop Color="LightGray" Offset="1"/>
                    </LinearGradientBrush>
                </Setter.Value>
            </Setter>
            <Setter Property="ButtonGridBorderStyle">
                <Setter.Value>
                    <Style TargetType="{x:Type Border}">
                        <Setter Property="Background" Value="Transparent" />
                    </Style>
                </Setter.Value>
            </Setter>
            <Setter Property="YesButtonContent" Value="Yesですよ" />
            <Setter Property="NoButtonContent" Value="Noです" />
            <Setter Property="OkButtonStyle" Value="{StaticResource CustomButtonStyle}" />
            <Setter Property="YesButtonStyle" Value="{StaticResource CustomButtonStyle}" />
            <Setter Property="NoButtonStyle" Value="{StaticResource CustomButtonStyle}" />
            <Setter Property="CancelButtonStyle" Value="{StaticResource CustomButtonStyle}" />
        </Style>
    </Application.Resources>
</Application>

まだまだ直さないといけないところもあるけど、今日はここまで。