欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

WPF自定义Window样式(1)

程序员文章站 2022-07-05 08:43:12
目录: WPF自定义Window样式(1) WPF自定义Window样式(2) 1. 引言 WPF是制作界面的一大利器。最近在做一个项目,用的就是WPF。既然使用了WPF了,那么理所当然的,需要自定义窗体样式。所使用的代码是在网上查到的,遗憾的是,整理完毕后,再找那篇帖子却怎么也找不到了,仅仅在下载 ......

目录:

WPF自定义Window样式(1)

WPF自定义Window样式(2)

 

1. 引言

    WPF是制作界面的一大利器。最近在做一个项目,用的就是WPF。既然使用了WPF了,那么理所当然的,需要自定义窗体样式。所使用的代码是在网上查到的:原文链接

      首先上原始源码

2. 创建项目

       创建空白项目stonemqy.CustomWindow,添加WPF项目stonemqy.CustomWindow.Main。在stonemqy.CustomWindow.Main中添加文件夹Themes,并在其中添加资源字典Generic.xaml,注意这里的Themes文件夹和Generic.xaml资源字典的名字不可更改。并在项目下依次添加类VisualStates、TransitioningContentControl和CustomWindow。

3. VisualStates

using System.Diagnostics;
using System.Linq;
using System.Windows;
using System.Windows.Media;

namespace stonemqy.CustomWindow.Main
{
    public static class VisualStates
    {  /// <summary>
        /// This method tries to get the named VisualStateGroup for the 
        /// dependency object. The provided object's ImplementationRoot will be 
        /// looked up in this call.
        /// </summary>
        /// <param name="dependencyObject">The dependency object.</param>
        /// <param name="groupName">The visual state group's name.</param>
        /// <returns>Returns null or the VisualStateGroup object.</returns>
        public static VisualStateGroup TryGetVisualStateGroup(DependencyObject dependencyObject, string groupName)
        {
            FrameworkElement root = GetImplementationRoot(dependencyObject);
            if (root == null)
            {
                return null;
            }

            return VisualStateManager.GetVisualStateGroups(root)
                .OfType<VisualStateGroup>()
                .Where(group => string.CompareOrdinal(groupName, group.Name) == 0)
                .FirstOrDefault();
        }

        /// <summary>
        /// Gets the implementation root of the Control.
        /// </summary>
        /// <param name="dependencyObject">The DependencyObject.</param>
        /// <remarks>
        /// Implements Silverlight's corresponding internal property on Control.
        /// </remarks>
        /// <returns>Returns the implementation root or null.</returns>
        public static FrameworkElement GetImplementationRoot(DependencyObject dependencyObject)
        {
            Debug.Assert(dependencyObject != null, "DependencyObject should not be null.");
            return (1 == VisualTreeHelper.GetChildrenCount(dependencyObject)) ?
                VisualTreeHelper.GetChild(dependencyObject, 0) as FrameworkElement :
                null;
        }
    }
}

4. TransitioningContentControl

using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Animation;

namespace stonemqy.CustomWindow.Main
{
    /// <summary>
    /// Represents a control with a single piece of content and when that content 
    /// changes performs a transition animation. 
    /// </summary>
    /// <QualityBand>Experimental</QualityBand>
    /// <remarks>The API for this control will change considerably in the future.</remarks>
    [TemplateVisualState(GroupName = PresentationGroup, Name = NormalState)]
    [TemplateVisualState(GroupName = PresentationGroup, Name = DefaultTransitionState)]
    [TemplatePart(Name = PreviousContentPresentationSitePartName, Type = typeof(ContentControl))]
    [TemplatePart(Name = CurrentContentPresentationSitePartName, Type = typeof(ContentControl))]
    public class TransitioningContentControl : ContentControl
    {
        #region Visual state names
        /// <summary>
        /// The name of the group that holds the presentation states.
        /// </summary>
        private const string PresentationGroup = "PresentationStates";

        /// <summary>
        /// The name of the state that represents a normal situation where no
        /// transition is currently being used.
        /// </summary>
        private const string NormalState = "Normal";

        /// <summary>
        /// The name of the state that represents the default transition.
        /// </summary>
        public const string DefaultTransitionState = "DefaultTransition";
        #endregion Visual state names

        #region Template part names
        /// <summary>
        /// The name of the control that will display the previous content.
        /// </summary>
        internal const string PreviousContentPresentationSitePartName = "PreviousContentPresentationSite";

        /// <summary>
        /// The name of the control that will display the current content.
        /// </summary>
        internal const string CurrentContentPresentationSitePartName = "CurrentContentPresentationSite";

        #endregion Template part names

        #region TemplateParts
        /// <summary>
        /// Gets or sets the current content presentation site.
        /// </summary>
        /// <value>The current content presentation site.</value>
        private ContentPresenter CurrentContentPresentationSite { get; set; }

        /// <summary>
        /// Gets or sets the previous content presentation site.
        /// </summary>
        /// <value>The previous content presentation site.</value>
        private ContentPresenter PreviousContentPresentationSite { get; set; }
        #endregion TemplateParts

        #region public bool IsTransitioning

        /// <summary>
        /// Indicates whether the control allows writing IsTransitioning.
        /// </summary>
        private bool _allowIsTransitioningWrite;

        /// <summary>
        /// Gets a value indicating whether this instance is currently performing
        /// a transition.
        /// </summary>
        public bool IsTransitioning
        {
            get { return (bool)GetValue(IsTransitioningProperty); }
            private set
            {
                _allowIsTransitioningWrite = true;
                SetValue(IsTransitioningProperty, value);
                _allowIsTransitioningWrite = false;
            }
        }

        /// <summary>
        /// Identifies the IsTransitioning dependency property.
        /// </summary>
        public static readonly DependencyProperty IsTransitioningProperty =
            DependencyProperty.Register(
                "IsTransitioning",
                typeof(bool),
                typeof(TransitioningContentControl),
                new PropertyMetadata(OnIsTransitioningPropertyChanged));

        /// <summary>
        /// IsTransitioningProperty property changed handler.
        /// </summary>
        /// <param name="d">TransitioningContentControl that changed its IsTransitioning.</param>
        /// <param name="e">Event arguments.</param>
        private static void OnIsTransitioningPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TransitioningContentControl source = (TransitioningContentControl)d;

            if (!source._allowIsTransitioningWrite)
            {
                source.IsTransitioning = (bool)e.OldValue;
                throw new InvalidOperationException("TransitiotioningContentControl_IsTransitioningReadOnly");
            }
        }
        #endregion public bool IsTransitioning

        /// <summary>
        /// The storyboard that is used to transition old and new content.
        /// </summary>
        private Storyboard _currentTransition;

        /// <summary>
        /// Gets or sets the storyboard that is used to transition old and new content.
        /// </summary>
        private Storyboard CurrentTransition
        {
            get { return _currentTransition; }
            set
            {
                // decouple event
                if (_currentTransition != null)
                {
                    _currentTransition.Completed -= OnTransitionCompleted;
                }

                _currentTransition = value;

                if (_currentTransition != null)
                {
                    _currentTransition.Completed += OnTransitionCompleted;
                }
            }
        }

        #region public string Transition
        /// <summary>
        /// Gets or sets the name of the transition to use. These correspond
        /// directly to the VisualStates inside the PresentationStates group.
        /// </summary>
        public string Transition
        {
            get { return GetValue(TransitionProperty) as string; }
            set { SetValue(TransitionProperty, value); }
        }

        /// <summary>
        /// Identifies the Transition dependency property.
        /// </summary>
        public static readonly DependencyProperty TransitionProperty =
            DependencyProperty.Register(
                "Transition",
                typeof(string),
                typeof(TransitioningContentControl),
                new PropertyMetadata(DefaultTransitionState, OnTransitionPropertyChanged));

        /// <summary>
        /// TransitionProperty property changed handler.
        /// </summary>
        /// <param name="d">TransitioningContentControl that changed its Transition.</param>
        /// <param name="e">Event arguments.</param>
        private static void OnTransitionPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TransitioningContentControl source = (TransitioningContentControl)d;
            string oldTransition = e.OldValue as string;
            string newTransition = e.NewValue as string;

            if (source.IsTransitioning)
            {
                source.AbortTransition();
            }

            // find new transition
            Storyboard newStoryboard = source.GetStoryboard(newTransition);

            // unable to find the transition.
            if (newStoryboard == null)
            {
                // could be during initialization of xaml that presentationgroups was not yet defined
                if (VisualStates.TryGetVisualStateGroup(source, PresentationGroup) == null)
                {
                    // will delay check
                    source.CurrentTransition = null;
                }
                else
                {
                    // revert to old value
                    source.SetValue(TransitionProperty, oldTransition);

                    throw new ArgumentException(
                       "TransitioningContentControl_TransitionNotFound");
                }
            }
            else
            {
                source.CurrentTransition = newStoryboard;
            }
        }
        #endregion public string Transition

        #region public bool RestartTransitionOnContentChange
        /// <summary>
        /// Gets or sets a value indicating whether the current transition
        /// will be aborted when setting new content during a transition.
        /// </summary>
        public bool RestartTransitionOnContentChange
        {
            get { return (bool)GetValue(RestartTransitionOnContentChangeProperty); }
            set { SetValue(RestartTransitionOnContentChangeProperty, value); }
        }

        /// <summary>
        /// Identifies the RestartTransitionOnContentChange dependency property.
        /// </summary>
        public static readonly DependencyProperty RestartTransitionOnContentChangeProperty =
            DependencyProperty.Register(
                "RestartTransitionOnContentChange",
                typeof(bool),
                typeof(TransitioningContentControl),
                new PropertyMetadata(false, OnRestartTransitionOnContentChangePropertyChanged));

        /// <summary>
        /// RestartTransitionOnContentChangeProperty property changed handler.
        /// </summary>
        /// <param name="d">TransitioningContentControl that changed its RestartTransitionOnContentChange.</param>
        /// <param name="e">Event arguments.</param>
        private static void OnRestartTransitionOnContentChangePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((TransitioningContentControl)d).OnRestartTransitionOnContentChangeChanged((bool)e.OldValue, (bool)e.NewValue);
        }

        /// <summary>
        /// Called when the RestartTransitionOnContentChangeProperty changes.
        /// </summary>
        /// <param name="oldValue">The old value of RestartTransitionOnContentChange.</param>
        /// <param name="newValue">The new value of RestartTransitionOnContentChange.</param>
        protected virtual void OnRestartTransitionOnContentChangeChanged(bool oldValue, bool newValue)
        {
        }
        #endregion public bool RestartTransitionOnContentChange

        #region Events
        /// <summary>
        /// Occurs when the current transition has completed.
        /// </summary>
        public event RoutedEventHandler TransitionCompleted;
        #endregion Events

        /// <summary>
        /// Initializes a new instance of the <see cref="TransitioningContentControl"/> class.
        /// </summary>
        public TransitioningContentControl()
        {
            DefaultStyleKey = typeof(TransitioningContentControl);
        }

        /// <summary>
        /// Builds the visual tree for the TransitioningContentControl control 
        /// when a new template is applied.
        /// </summary>
        public override void OnApplyTemplate()
        {
            if (IsTransitioning)
            {
                AbortTransition();
            }

            base.OnApplyTemplate();

            PreviousContentPresentationSite = GetTemplateChild(PreviousContentPresentationSitePartName) as ContentPresenter;
            CurrentContentPresentationSite = GetTemplateChild(CurrentContentPresentationSitePartName) as ContentPresenter;

            if (CurrentContentPresentationSite != null)
            {
                CurrentContentPresentationSite.Content = Content;
            }

            // hookup currenttransition
            Storyboard transition = GetStoryboard(Transition);
            CurrentTransition = transition;
            if (transition == null)
            {
                string invalidTransition = Transition;
                // revert to default
                Transition = DefaultTransitionState;

                throw new ArgumentException(
                    "TransitioningContentControl_TransitionNotFound");
            }

            VisualStateManager.GoToState(this, NormalState, false);
            VisualStateManager.GoToState(this, Transition, true);
        }

        /// <summary>
        /// Called when the value of the <see cref="P:System.Windows.Controls.ContentControl.Content"/> property changes.
        /// </summary>
        /// <param name="oldContent">The old value of the <see cref="P:System.Windows.Controls.ContentControl.Content"/> property.</param>
        /// <param name="newContent">The new value of the <see cref="P:System.Windows.Controls.ContentControl.Content"/> property.</param>
        protected override void OnContentChanged(object oldContent, object newContent)
        {
            base.OnContentChanged(oldContent, newContent);

            StartTransition(oldContent, newContent);
        }

        /// <summary>
        /// Starts the transition.
        /// </summary>
        /// <param name="oldContent">The old content.</param>
        /// <param name="newContent">The new content.</param>
        [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "newContent", Justification = "Should be used in the future.")]
        private void StartTransition(object oldContent, object newContent)
        {
            // both presenters must be available, otherwise a transition is useless.
            if (CurrentContentPresentationSite != null && PreviousContentPresentationSite != null)
            {
                CurrentContentPresentationSite.Content = newContent;

                PreviousContentPresentationSite.Content = oldContent;

                // and start a new transition
                if (!IsTransitioning || RestartTransitionOnContentChange)
                {
                    IsTransitioning = true;
                    VisualStateManager.GoToState(this, NormalState, false);
                    VisualStateManager.GoToState(this, Transition, true);
                }
            }
        }

        /// <summary>
        /// Handles the Completed event of the transition storyboard.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void OnTransitionCompleted(object sender, EventArgs e)
        {
            AbortTransition();

            RoutedEventHandler handler = TransitionCompleted;
            if (handler != null)
            {
                handler(this, new RoutedEventArgs());
            }
        }

        /// <summary>
        /// Aborts the transition and releases the previous content.
        /// </summary>
        public void AbortTransition()
        {
            // go to normal state and release our hold on the old content.
            VisualStateManager.GoToState(this, NormalState, false);
            IsTransitioning = false;
            if (PreviousContentPresentationSite != null)
            {
                PreviousContentPresentationSite.Content = null;
            }
        }

        /// <summary>
        /// Attempts to find a storyboard that matches the newTransition name.
        /// </summary>
        /// <param name="newTransition">The new transition.</param>
        /// <returns>A storyboard or null, if no storyboard was found.</returns>
        private Storyboard GetStoryboard(string newTransition)
        {
            VisualStateGroup presentationGroup = VisualStates.TryGetVisualStateGroup(this, PresentationGroup);
            Storyboard newStoryboard = null;
            if (presentationGroup != null)
            {
                newStoryboard = presentationGroup.States
                    .OfType<VisualState>()
                    .Where(state => state.Name == newTransition)
                    .Select(state => state.Storyboard)
                    .FirstOrDefault();
            }
            return newStoryboard;
        }


    }
}

 

 5. CustomWindow

     此处原来有关于CustomWindow的一些注释,但是经验证,注释有误,故贴代码时将注释删除了。

using System; 
using System.Windows; 
using System.Windows.Input; 

namespace stonemqy.CustomWindow.Main
{
    public class CustomWindow : Window
    {
        public CustomWindow()
        {
            DefaultStyleKey = typeof(CustomWindow);
            CommandBindings.Add(new CommandBinding(SystemCommands.CloseWindowCommand, CloseWindow));
            CommandBindings.Add(new CommandBinding(SystemCommands.MaximizeWindowCommand, MaximizeWindow, CanResizeWindow));
            CommandBindings.Add(new CommandBinding(SystemCommands.MinimizeWindowCommand, MinimizeWindow, CanMinimizeWindow));
            CommandBindings.Add(new CommandBinding(SystemCommands.RestoreWindowCommand, RestoreWindow, CanResizeWindow));
            CommandBindings.Add(new CommandBinding(SystemCommands.ShowSystemMenuCommand, ShowSystemMenu));
        }

        protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
        {
            base.OnMouseLeftButtonDown(e);
            if (e.ButtonState == MouseButtonState.Pressed)
                DragMove();
        }

        protected override void OnContentRendered(EventArgs e)
        {
            base.OnContentRendered(e);
            if (SizeToContent == SizeToContent.WidthAndHeight)
                InvalidateMeasure();
        }

        #region Window Commands

        private void CanResizeWindow(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = ResizeMode == ResizeMode.CanResize || ResizeMode == ResizeMode.CanResizeWithGrip;
        }

        private void CanMinimizeWindow(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = ResizeMode != ResizeMode.NoResize;
        }

        private void CloseWindow(object sender, ExecutedRoutedEventArgs e)
        {
            this.Close();
            //SystemCommands.CloseWindow(this);
        }

        private void MaximizeWindow(object sender, ExecutedRoutedEventArgs e)
        {
            SystemCommands.MaximizeWindow(this);
        }

        private void MinimizeWindow(object sender, ExecutedRoutedEventArgs e)
        {
            SystemCommands.MinimizeWindow(this);
        }

        private void RestoreWindow(object sender, ExecutedRoutedEventArgs e)
        {
            SystemCommands.RestoreWindow(this);
        }


        private void ShowSystemMenu(object sender, ExecutedRoutedEventArgs e)
        {
            var element = e.OriginalSource as FrameworkElement;
            if (element == null)
                return;

            var point = WindowState == WindowState.Maximized ? new Point(0, element.ActualHeight)
                : new Point(Left + BorderThickness.Left, element.ActualHeight + Top + BorderThickness.Top);
            point = element.TransformToAncestor(this).Transform(point);
            SystemCommands.ShowSystemMenu(this, point);
        }

        #endregion
    }
}

6. 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:stonemqy.CustomWindow.Main"
                    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity">


    <DataTemplate x:Key="RestoreWhite">
        <Grid UseLayoutRounding="True"
              RenderTransform="1,0,0,1,.5,.5">
            <Path Data="M2,0 L8,0 L8,6 M0,3 L6,3 M0,2 L6,2 L6,8 L0,8 Z"
                  Width="8"
                  Height="8"
                  VerticalAlignment="Center"
                  HorizontalAlignment="Center"
                  Stroke="White"
                  StrokeThickness="1" />
        </Grid>
    </DataTemplate>

    <DataTemplate x:Key="CloseWhite">
        <Grid Margin="1,0,0,0">

            <Rectangle Stroke="White"
                       Height="2"
                       RenderTransformOrigin="0.5,0.5"
                       Width="11"
                       UseLayoutRounding="True">
                <Rectangle.RenderTransform>
                    <TransformGroup>
                        <ScaleTransform />
                        <SkewTransform />
                        <RotateTransform Angle="45" />
                        <TranslateTransform />
                    </TransformGroup>
                </Rectangle.RenderTransform>
            </Rectangle>
            <Rectangle Stroke="White"
                       Height="2"
                       RenderTransformOrigin="0.5,0.5"
                       Width="11"
                       UseLayoutRounding="True">
                <Rectangle.RenderTransform>
                    <TransformGroup>
                        <ScaleTransform />
                        <SkewTransform />
                        <RotateTransform Angle="-45" />
                        <TranslateTransform />
                    </TransformGroup>
                </Rectangle.RenderTransform>
            </Rectangle>
        </Grid>
    </DataTemplate>

    <DataTemplate x:Key="MaximizeWhite">
        <Grid>
            <Path Data="M0,1 L9,1 L9,8 L0,8 Z"
                  Width="9"
                  Height="8"
                  Margin="0,2,0,0"
                  VerticalAlignment="Center"
                  HorizontalAlignment="Center"
                  Stroke="White"
                  StrokeThickness="2" />
        </Grid>
    </DataTemplate>

    <DataTemplate x:Key="MinimizeWhite">
        <Grid>
            <Path Data="M0,6 L8,6 Z"
                  Width="8"
                  Height="7"
                  VerticalAlignment="Center"
                  HorizontalAlignment="Center"
                  Stroke="White"
                  StrokeThickness="2" />
        </Grid>
    </DataTemplate>



    <Style x:Key="TitleBarButtonFocusVisual">
        <Setter Property="Control.Template">
            <Setter.Value>
                <ControlTemplate>
                    <Rectangle Margin="2"
                               SnapsToDevicePixels="True"
                               Stroke="Transparent"
                               StrokeDashArray="1 2"
                               StrokeThickness="1" />
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <Style x:Key="TitleBarButtonStyle"
           TargetType="{x:Type Button}">
        <Setter Property="Focusable"
                Value="False" />
        <Setter Property="Background"
                Value="Transparent" />
        <Setter Property="BorderBrush"
                Value="Transparent" />
        <Setter Property="BorderThickness"
                Value="1" />
        <Setter Property="HorizontalContentAlignment"
                Value="Center" />
        <Setter Property="VerticalContentAlignment"
                Value="Center" />
        <Setter Property="Padding"
                Value="1" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Button}">
                    <Grid x:Name="LayoutRoot">
                        <Rectangle x:Name="ButtonBackground"
                                   Width="28"
                                   Height="28"
                                   Fill="#FFFFFFFF"
                                   Opacity="0" />
                        <Border x:Name="ButtonBorder"
                                BorderBrush="{TemplateBinding BorderBrush}"
                                BorderThickness="{TemplateBinding BorderThickness}"
                                Background="{TemplateBinding Background}"
                                SnapsToDevicePixels="true">
                            <ContentPresenter x:Name="TitleBarButtonContentPresenter"
                                              Focusable="False"
                                              HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                              Margin="{TemplateBinding Padding}"
                                              RecognizesAccessKey="True"
                                              SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                                              VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
                        </Border>
                    </Grid>

                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver"
                                 Value="true">
                            <Setter Property="Opacity"
                                    Value="0.2"
                                    TargetName="ButtonBackground" />
                        </Trigger>
                        <Trigger Property="IsPressed"
                                 Value="True">
                            <Setter Property="Opacity"
                                    Value="0.4"
                                    TargetName="ButtonBackground" />
                        </Trigger>
                        <Trigger Property="IsEnabled"
                                 Value="false">
                            <Setter TargetName="TitleBarButtonContentPresenter"
                                    Property="Opacity"
                                    Value=".5" />
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    <!--<Style TargetType="{x:Type local:OmecWindow}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:OmecWindow}">
                    <Border Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}">
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>-->



    <Style TargetType="{x:Type local:CustomWindow}">
        <Setter Property="Foreground"
                Value="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}" />
        <Setter Property="Background"
                Value="#FFF1F1F1" />
        <Setter Property="BorderBrush"
                Value="#FF0874AA" />
        <Setter Property="BorderThickness"
                Value="1" />
        <Setter Property="IsTabStop"
                Value="False" />
        <Setter Property="ResizeMode"
                Value="CanResizeWithGrip" />
        <Setter Property="UseLayoutRounding"
                Value="True" />
        <Setter Property="TextOptions.TextFormattingMode"
                Value="Display" />
        <Setter Property="WindowStyle"
                Value="SingleBorderWindow" />
        <Setter Property="FontFamily"
                Value="Segoe UI" />
        <Setter Property="WindowChrome.WindowChrome">
            <Setter.Value>
                <WindowChrome CornerRadius="0"
                              GlassFrameThickness="1"
                              UseAeroCaptionButtons="False"
                              NonClientFrameEdges="None" />
            </Setter.Value>
        </Setter>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:CustomWindow}">


                    <Border BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}"
                            x:Name="WindowBorder">
                        <Grid x:Name="LayoutRoot"
                              Background="{TemplateBinding Background}">
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto" />
                                <RowDefinition Height="*" />
                            </Grid.RowDefinitions>

                            <Grid x:Name="PART_WindowTitleGrid"
                                  Grid.Row="0"
                                  Height="26.4"
                                  Background="{TemplateBinding BorderBrush}">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="*" />
                                    <ColumnDefinition Width="Auto" />
                                </Grid.ColumnDefinitions>
                                <StackPanel Orientation="Horizontal">
                                    <!--<Image Source="{TemplateBinding Icon}"
                                           Margin="7,0,5,0"
                                           VerticalAlignment="Center"
                                          />-->
                                    <Button VerticalAlignment="Center"
                                            Margin="7,0,5,0"
                                            Content="{TemplateBinding Icon}"
                                            Height="{x:Static SystemParameters.SmallIconHeight}"
                                            Width="{x:Static SystemParameters.SmallIconWidth}"
                                            WindowChrome.IsHitTestVisibleInChrome="True"
                                            IsTabStop="False">
                                        <Button.Template>
                                            <ControlTemplate TargetType="{x:Type Button}">
                                                <Image Source="{TemplateBinding Content}" />
                                            </ControlTemplate>
                                        </Button.Template>
                                        <i:Interaction.Triggers>
                                            <i:EventTrigger EventName="Click">
                                                <i:InvokeCommandAction Command="{x:Static SystemCommands.ShowSystemMenuCommand}" />
                                            </i:EventTrigger>
                                            <i:EventTrigger EventName="MouseDoubleClick">
                                                <i:InvokeCommandAction Command="{x:Static SystemCommands.CloseWindowCommand}" />
                                            </i:EventTrigger>
                                        </i:Interaction.Triggers>
                                    </Button>
                                    <ContentControl IsTabStop="False"
                                                    Foreground="White"
                                                    HorizontalAlignment="Center"
                                                    VerticalAlignment="Center"
                                                    FontSize="{DynamicResource {x:Static SystemFonts.CaptionFontSize}}"
                                                    Content="{TemplateBinding Title}" />
                                </StackPanel>
                                <StackPanel x:Name="WindowCommandButtonsStackPanel"
                                            Grid.Column="1"
                                            HorizontalAlignment="Right"
                                            VerticalAlignment="Top"
                                            Background="Transparent"
                                            Orientation="Horizontal"
                                            WindowChrome.IsHitTestVisibleInChrome="True">
                                    <Button x:Name="Minimize"
                                            ToolTip="Minimize"
                                            WindowChrome.IsHitTestVisibleInChrome="True"
                                            Command="{Binding Source={x:Static SystemCommands.MinimizeWindowCommand}}"
                                            ContentTemplate="{StaticResource MinimizeWhite}"
                                            Style="{StaticResource TitleBarButtonStyle}"
                                            IsTabStop="False" />
                                    <Grid Margin="1,0,1,0">
                                        <Button x:Name="Restore"
                                                ToolTip="Restore"
                                                WindowChrome.IsHitTestVisibleInChrome="True"
                                                Command="{Binding Source={x:Static SystemCommands.RestoreWindowCommand}}"
                                                ContentTemplate="{StaticResource RestoreWhite}"
                                                Style="{StaticResource TitleBarButtonStyle}"
                                                Visibility="Collapsed"
                                                IsTabStop="False" />
                                        <Button x:Name="Maximize"
                                                ToolTip="Maximize"
                                                WindowChrome.IsHitTestVisibleInChrome="True"
                                                Command="{Binding Source={x:Static SystemCommands.MaximizeWindowCommand}}"
                                                ContentTemplate="{StaticResource MaximizeWhite}"
                                                Style="{StaticResource TitleBarButtonStyle}"
                                                IsTabStop="False" />
                                    </Grid>
                                    <Button x:Name="Close"
                                            ToolTip="Close"
                                            WindowChrome.IsHitTestVisibleInChrome="True"
                                            Command="{Binding Source={x:Static SystemCommands.CloseWindowCommand}}"
                                            ContentTemplate="{StaticResource CloseWhite}"
                                            Style="{StaticResource TitleBarButtonStyle}"
                                            IsTabStop="False" />
                                </StackPanel>
                            </Grid>
                            <AdornerDecorator Grid.Row="1" KeyboardNavigation.IsTabStop="False">
                                <!--<ContentPresenter x:Name="MainContentPresenter"
                                                  KeyboardNavigation.TabNavigation="Cycle" />-->
                                <local:TransitioningContentControl Content="{TemplateBinding Content}"
                                                                   x:Name="MainContentPresenter"
                                                                
                                                                   KeyboardNavigation.TabNavigation="Cycle" />
                            </AdornerDecorator>
                            <ResizeGrip x:Name="ResizeGrip"
                                        HorizontalAlignment="Right"
                                        VerticalAlignment="Bottom"
                                        Grid.Row="1"
                                        IsTabStop="False"
                                        Visibility="Hidden"
                                        WindowChrome.ResizeGripDirection="BottomRight" />
                        </Grid>
                    </Border>


                    <ControlTemplate.Triggers>
                        <Trigger Property="IsActive"
                                 Value="False">
                            <Setter Property="BorderBrush"
                                    Value="#FF6F7785" />
                        </Trigger>
                        <Trigger Property="WindowState"
                                 Value="Maximized">
                            <Setter TargetName="Maximize"
                                    Property="Visibility"
                                    Value="Collapsed" />
                            <Setter TargetName="Restore"
                                    Property="Visibility"
                                    Value="Visible" />
                            <Setter TargetName="LayoutRoot"
                                    Property="Margin"
                                    Value="7" />
                        </Trigger>
                        <Trigger Property="WindowState"
                                 Value="Normal">
                            <Setter TargetName="Maximize"
                                    Property="Visibility"
                                    Value="Visible" />
                            <Setter TargetName="Restore"
                                    Property="Visibility"
                                    Value="Collapsed" />
                        </Trigger>
                        <Trigger Property="ResizeMode"
                                 Value="NoResize">
                            <Setter TargetName="Minimize"
                                    Property="Visibility"
                                    Value="Collapsed" />
                            <Setter TargetName="Maximize"
                                    Property="Visibility"
                                    Value="Collapsed" />
                            <Setter TargetName="Restore"
                                    Property="Visibility"
                                    Value="Collapsed" />
                        </Trigger>

                        <MultiTrigger>
                            <MultiTrigger.Conditions>
                                <Condition Property="ResizeMode"
                                           Value="CanResizeWithGrip" />
                                <Condition Property="WindowState"
                                           Value="Normal" />
                            </MultiTrigger.Conditions>
                            <Setter TargetName="ResizeGrip"
                                    Property="Visibility"
                                    Value="Visible" />
                        </MultiTrigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>

    </Style>



    <Style TargetType="local:TransitioningContentControl">
        <Setter Property="IsTabStop"
                Value="False" />
        <Setter Property="HorizontalContentAlignment"
                Value="Stretch" />
        <Setter Property="VerticalContentAlignment"
                Value="Stretch" />
        <Setter Property="Transition"
                Value="DefaultTransition" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:TransitioningContentControl">
                    <Border Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}"
                            CornerRadius="2">
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="PresentationStates">
                                <VisualState x:Name="DefaultTransition">
                                    <Storyboard>
                                        <!--<DoubleAnimationUsingKeyFrames 
                                            BeginTime="00:00:00" 
                                            Storyboard.TargetName="CurrentContentPresentationSite"
                                            Storyboard.TargetProperty="(UIElement.Opacity)">
                                            <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
                                            <SplineDoubleKeyFrame KeyTime="00:00:00.300" Value="1"/>
                                        </DoubleAnimationUsingKeyFrames>
                                        <DoubleAnimationUsingKeyFrames
                                            BeginTime="00:00:00" 
                                            Storyboard.TargetName="PreviousContentPresentationSite" 
                                            Storyboard.TargetProperty="(UIElement.Opacity)">
                                            <SplineDoubleKeyFrame KeyTime="00:00:00" Value="1"/>
                                            <SplineDoubleKeyFrame KeyTime="00:00:00.300" Value="0"/>
                                        </DoubleAnimationUsingKeyFrames>-->
                                        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
                                                                       Storyboard.TargetName="CurrentContentPresentationSite"
                                                                       Storyboard.TargetProperty="(UIElement.Opacity)">
                                            <SplineDoubleKeyFrame KeyTime="00:00:00"
                                                                  Value="0" />
                                            <SplineDoubleKeyFrame KeyTime="00:00:00.400"
                                                                  Value="1" />
                                        </DoubleAnimationUsingKeyFrames>

                                        <DoubleAnimation BeginTime="00:00:00"
                                                         Duration="00:00:00.500"
                                                         Storyboard.TargetName="CurrentContentPresentationSite"
                                                         Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)"
                                                         From="50"
                                                         To="0">
                                            <DoubleAnimation.EasingFunction>
                                                <CubicEase EasingMode="EaseOut" />
                                            </DoubleAnimation.EasingFunction>
                                        </DoubleAnimation>

                                        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
                                                                       Storyboard.TargetName="PreviousContentPresentationSite"
                                                                       Storyboard.TargetProperty="(UIElement.Opacity)">
                                            <SplineDoubleKeyFrame KeyTime="00:00:00"
                                                                  Value="1" />
                                            <SplineDoubleKeyFrame KeyTime="00:00:00.100"
                                                                  Value="0" />
                                        </DoubleAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="Normal">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames BeginTime="00:00:00"
                                                                       Storyboard.TargetName="PreviousContentPresentationSite"
                                                                       Storyboard.TargetProperty="(UIElement.Visibility)">
                                            <DiscreteObjectKeyFrame KeyTime="00:00:00">
                                                <DiscreteObjectKeyFrame.Value>
                                                    <Visibility>Collapsed</Visibility>
                                                </DiscreteObjectKeyFrame.Value>
                                            </DiscreteObjectKeyFrame>
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>

                                <VisualState x:Name="UpTransition">
                                    <Storyboard>
                                        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
                                                                       Storyboard.TargetName="CurrentContentPresentationSite"
                                                                       Storyboard.TargetProperty="(UIElement.Opacity)">
                                            <SplineDoubleKeyFrame KeyTime="00:00:00"
                                                                  Value="0" />
                                            <SplineDoubleKeyFrame KeyTime="00:00:00.300"
                                                                  Value="1" />
                                        </DoubleAnimationUsingKeyFrames>

                                        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
                                                                       Storyboard.TargetName="CurrentContentPresentationSite"
                                                                       Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)">
                                            <SplineDoubleKeyFrame KeyTime="00:00:00"
                                                                  Value="30" />
                                            <SplineDoubleKeyFrame KeyTime="00:00:00.300"
                                                                  Value="0" />
                                        </DoubleAnimationUsingKeyFrames>

                                        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
                                                                       Storyboard.TargetName="PreviousContentPresentationSite"
                                                                       Storyboard.TargetProperty="(UIElement.Opacity)">
                                            <SplineDoubleKeyFrame KeyTime="00:00:00"
                                                                  Value="1" />
                                            <SplineDoubleKeyFrame KeyTime="00:00:00.300"
                                                                  Value="0" />
                                        </DoubleAnimationUsingKeyFrames>

                                        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
                                                                       Storyboard.TargetName="PreviousContentPresentationSite"
                                                                       Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)">
                                            <SplineDoubleKeyFrame KeyTime="00:00:00"
                                                                  Value="0" />
                                            <SplineDoubleKeyFrame KeyTime="00:00:00.300"
                                                                  Value="-30" />
                                        </DoubleAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>

                                <VisualState x:Name="DownTransition">
                                    <Storyboard>
                                        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
                                                                       Storyboard.TargetName="CurrentContentPresentationSite"
                                                                       Storyboard.TargetProperty="(UIElement.Opacity)">
                                            <SplineDoubleKeyFrame KeyTime="00:00:00"
                                                                  Value="0" />
                                            <SplineDoubleKeyFrame KeyTime="00:00:00.300"
                                                                  Value="1" />
                                        </DoubleAnimationUsingKeyFrames>

                                        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
                                                                       Storyboard.TargetName="CurrentContentPresentationSite"
                                                                       Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)">
                                            <SplineDoubleKeyFrame KeyTime="00:00:00"
                                                                  Value="-40" />
                                            <SplineDoubleKeyFrame KeyTime="00:00:00.300"
                                                                  Value="0" />
                                        </DoubleAnimationUsingKeyFrames>

                                        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
                                                                       Storyboard.TargetName="PreviousContentPresentationSite"
                                                                       Storyboard.TargetProperty="(UIElement.Opacity)">
                                            <SplineDoubleKeyFrame KeyTime="00:00:00"
                                                                  Value="1" />
                                            <SplineDoubleKeyFrame KeyTime="00:00:00.300"
                                                                  Value="0" />
                                        </DoubleAnimationUsingKeyFrames>

                                        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
                                                                       Storyboard.TargetName="PreviousContentPresentationSite"
                                                                       Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)">
                                            <SplineDoubleKeyFrame KeyTime="00:00:00"
                                                                  Value="0" />
                                            <SplineDoubleKeyFrame KeyTime="00:00:00.300"
                                                                  Value="40" />
                                        </DoubleAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>

                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>

                        <Grid>
                            <ContentPresenter x:Name="PreviousContentPresentationSite"
                                              Content="{x:Null}"
                                              ContentTemplate="{TemplateBinding ContentTemplate}"
                                              HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                              VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
                                <ContentPresenter.RenderTransform>
                                    <TransformGroup>
                                        <ScaleTransform />
                                        <SkewTransform />
                                        <RotateTransform />
                                        <TranslateTransform />
                                    </TransformGroup>
                                </ContentPresenter.RenderTransform>
                            </ContentPresenter>

                            <ContentPresenter x:Name="CurrentContentPresentationSite"
                                              Content="{x:Null}"
                                              ContentTemplate="{TemplateBinding ContentTemplate}"
                                              HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                              VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
                                <ContentPresenter.RenderTransform>
                                    <TransformGroup>
                                        <ScaleTransform />
                                        <SkewTransform />
                                        <RotateTransform />
                                        <TranslateTransform />
                                    </TransformGroup>
                                </ContentPresenter.RenderTransform>
                            </ContentPresenter>
                        </Grid>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

7. MainWindow.xaml

<local:CustomWindow x:Class="stonemqy.CustomWindow.Main.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:stonemqy.CustomWindow.Main"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        
    </Grid>
</local:CustomWindow>

8. MainWindow.cs

namespace stonemqy.CustomWindow.Main
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : CustomWindow
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }
}

9. 运行效果

WPF自定义Window样式(1)

10. 项目代码

源码