WPF自定义Window样式(1)
目录:
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. 运行效果
10. 项目代码
学到的就要教人