WPF之模板样式
以前学WPF时弄的个自定义窗体,关于截断系统消息的一部分代码来源于网上。
源文件在这里。
说起来也容易,整个窗体就是个border,渲染下,然后在上面加层DockPanel,放那些标题和按钮。
下面是自定义窗体的样式,将XAML分开,这样方便看。
窗体部分:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<Style x:Key="WindowStyle" TargetType="Window"> <Setter Property="AllowsTransparency" Value="True"/> <Setter Property="Background" Value="Transparent"/> <Setter Property="WindowStyle" Value="None"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate x:Name="WindowTemplate" TargetType="Window"> <!--若不设置margin,阴影无法显示(被挡住了)--> <Border x:Name="FussWindowBorder" CornerRadius="5" Margin="8" Background="White" BorderBrush="#6A6A6A" BorderThickness="1" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" > <Border.Effect> <DropShadowEffect BlurRadius="8" ShadowDepth="0" Color="#00000000"/> </Border.Effect> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <Border Name="TitleBar" Height="50" CornerRadius="5"> <Border.Background> <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1"> <GradientStop Color="#FF2161D6" Offset="0.02"/> <GradientStop Color="#00FFFFFF" Offset="1"/> </LinearGradientBrush> </Border.Background> <DockPanel Margin="0"> <TextBlock DockPanel.Dock="Left" Name="TitleText" VerticalAlignment="Top" Margin="10 5 0 0" FontSize="16" FontWeight="Bold" Foreground="#FF101010"/> <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" DockPanel.Dock="Right" Height="25" VerticalAlignment="Top"> <Rectangle Style="{DynamicResource Splitter}"/> <Button Name="MiniButton" Width="30" Template="{DynamicResource MiniButton}"/> <Rectangle Style="{DynamicResource Splitter}"/> <Button Name="MaxButton" Width="30" Style="{DynamicResource WinNormalButton}"/> <Rectangle Name="MaxSplitter" Style="{DynamicResource Splitter}"/> <Button Name="CloseButton" Width="35" Style="{DynamicResource CloseButton}"/> </StackPanel> </DockPanel> </Border> <AdornerDecorator Grid.Row="1" Height="Auto" Width="Auto"> <ContentPresenter/> </AdornerDecorator> </Grid> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style>
最大化、最小化、关闭按钮间的切分条:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<Style x:Key="Splitter" TargetType="Rectangle"> <Setter Property="Width" Value="1"/> <Setter Property="Stroke" Value="Transparent"/> <Setter Property="StrokeThickness" Value="0"/> <Setter Property="Fill"> <Setter.Value> <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1"> <LinearGradientBrush.GradientStops> <GradientStop Color="#FF000000" Offset="0"/> <GradientStop Color="#80000000" Offset="0.6"/> <GradientStop Color="#00000000" Offset="1.0"/> </LinearGradientBrush.GradientStops> </LinearGradientBrush> </Setter.Value> </Setter> </Style>
几个按钮的颜色样式:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<LinearGradientBrush x:Key="MinMaxBrush" StartPoint="0.5,0" EndPoint="0.5,1"> <LinearGradientBrush.GradientStops> <GradientStop Color="#F0FFFFFF" Offset="0"/> <GradientStop Color="#00FFFFFF" Offset="1"/> </LinearGradientBrush.GradientStops> </LinearGradientBrush> <LinearGradientBrush x:Key="MinMaxPressedBrush" StartPoint="0.5,0" EndPoint="0.5,1"> <LinearGradientBrush.GradientStops> <GradientStop Color="#E0BBBBBB" Offset="0"/> <GradientStop Color="#00BBBBBB" Offset="1"/> </LinearGradientBrush.GradientStops> </LinearGradientBrush> <DrawingBrush x:Key="CloseButtonBrush"> <DrawingBrush.Drawing> <GeometryDrawing Geometry="M 36.0396,7.62939e-006L -6.10352e-005,7.62939e-006L -6.10352e-005,25L 39.9999,25L 39.9999,3.90626C 39.9999,1.74885 38.2269,7.62939e-006 36.0396,7.62939e-006 Z "> <GeometryDrawing.Brush> <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1"> <LinearGradientBrush.GradientStops> <GradientStop Color="#FFFF0000" Offset="0"/> <GradientStop Color="#00FF0000" Offset="1"/> </LinearGradientBrush.GradientStops> </LinearGradientBrush> </GeometryDrawing.Brush> </GeometryDrawing> </DrawingBrush.Drawing> </DrawingBrush> <DrawingBrush x:Key="CloseButtonPressBrush"> <DrawingBrush.Drawing> <GeometryDrawing Geometry="M 36.0396,7.62939e-006L -6.10352e-005,7.62939e-006L -6.10352e-005,25L 39.9999,25L 39.9999,3.90626C 39.9999,1.74885 38.2269,7.62939e-006 36.0396,7.62939e-006 Z "> <GeometryDrawing.Brush> <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1"> <LinearGradientBrush.GradientStops> <GradientStop Color="#FF5A022F" Offset="0"/> <GradientStop Color="#005A022F" Offset="1"/> </LinearGradientBrush.GradientStops> </LinearGradientBrush> </GeometryDrawing.Brush> </GeometryDrawing> </DrawingBrush.Drawing> </DrawingBrush>
最小化、最大化(窗体最大化和正常两种情况)、关闭按钮的样式:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<ControlTemplate x:Key="MiniButton" TargetType="Button"> <!--一定要初始化Grid的Background属性--> <Grid x:Name="MiniGrid" Background="Transparent"> <Path Data="F1 M 14.7587,4.59057L 0.5,4.59057L 0.5,0.499992L 14.7587,0.499992L 14.7587,4.59057 Z " Fill="#FFFCFCFD" StrokeLineJoin="Round" Stroke="#FF4A4B4D" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0 5 0 0" /> </Grid> <ControlTemplate.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Background" TargetName="MiniGrid" Value="{StaticResource MinMaxBrush}"/> </Trigger> <Trigger Property="IsPressed" Value="True"> <Setter Property="Background" TargetName="MiniGrid" Value="{StaticResource MinMaxPressedBrush}"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> <Style x:Key="WinMaxButton" TargetType="Button"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Button"> <Grid x:Name="MaxButton" Background="Transparent"> <Canvas Width="15" Height="15" VerticalAlignment="Center" HorizontalAlignment="Center" > <Canvas.Background> <DrawingBrush> <DrawingBrush.Drawing> <DrawingGroup> <DrawingGroup.Children> <GeometryDrawing Brush="#FFFCFCFD" Geometry="M 3.06549,0.500031L 15.5,0.500031L 15.5,12.9346L 12.4866,12.9346L 12.4866,3.6337L 3.06549,3.6337L 3.06549,0.500031 Z "> <GeometryDrawing.Pen> <Pen LineJoin="Round" Brush="#FF040404"/> </GeometryDrawing.Pen> </GeometryDrawing> <GeometryDrawing Brush="#FFFCFCFD" Geometry="M 0.5,3.06546L 12.9345,3.06546L 12.9345,15.5L 0.5,15.5L 0.5,3.06546 Z M 3.60864,6.1741L 3.60864,12.3914L 9.82587,12.3914L 9.82587,6.1741L 3.60864,6.1741 Z "> <GeometryDrawing.Pen> <Pen LineJoin="Round" Brush="#FF040404"/> </GeometryDrawing.Pen> </GeometryDrawing> </DrawingGroup.Children> </DrawingGroup> </DrawingBrush.Drawing> </DrawingBrush> </Canvas.Background> </Canvas> </Grid> <ControlTemplate.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Background" TargetName="MaxButton" Value="{StaticResource MinMaxBrush}"/> </Trigger> <Trigger Property="IsPressed" Value="True"> <Setter Property="Background" TargetName="MaxButton" Value="{StaticResource MinMaxPressedBrush}"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> <Style x:Key="WinNormalButton" TargetType="Button"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Button"> <Grid x:Name="MaxButton" Background="Transparent"> <Canvas Width="15" Height="15" VerticalAlignment="Center" HorizontalAlignment="Center" > <Canvas.Background> <DrawingBrush> <DrawingBrush.Drawing> <DrawingGroup> <DrawingGroup.Children> <GeometryDrawing Brush="#FFFCFCFD" Geometry="M 0.5,3.06546L 12.9345,3.06546L 12.9345,15.5L 0.5,15.5L 0.5,3.06546 Z M 3.60864,6.1741L 3.60864,12.3914L 9.82587,12.3914L 9.82587,6.1741L 3.60864,6.1741 Z "> <GeometryDrawing.Pen> <Pen LineJoin="Round" Brush="#FF040404"/> </GeometryDrawing.Pen> </GeometryDrawing> </DrawingGroup.Children> </DrawingGroup> </DrawingBrush.Drawing> </DrawingBrush> </Canvas.Background> </Canvas> </Grid> <ControlTemplate.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Background" TargetName="MaxButton" Value="{StaticResource MinMaxBrush}"/> </Trigger> <Trigger Property="IsPressed" Value="True"> <Setter Property="Background" TargetName="MaxButton" Value="{StaticResource MinMaxPressedBrush}"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> <Style x:Key="CloseButton" TargetType="Button"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Button"> <Grid x:Name="CloseGrid" Background="Transparent"> <Path Data="F1 M 8,3.21448L 6.02069,0.5L 0.507629,0.5L 5.24347,6.99478L 0.5,13.5L 6.013,13.5L 8,10.7751L 9.987,13.5L 15.5,13.5L 10.7565,6.99478L 15.4924,0.5L 9.97937,0.5L 8,3.21448 Z " Fill="#FFFCFCFD" StrokeLineJoin="Round" Stroke="#FF4A4B4D" HorizontalAlignment="Center" VerticalAlignment="Center"/> </Grid> <ControlTemplate.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Background" TargetName="CloseGrid" Value="{StaticResource CloseButtonBrush}"/> </Trigger> <Trigger Property="IsPressed" Value="True"> <Setter Property="Background" TargetName="CloseGrid" Value="{StaticResource CloseButtonPressBrush}"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style>
然后写个基类,读取下样式资源并应用。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
namespace Fuss.Wpf.Themes { public class WindowBase : Window { private ResourceDictionary myWindowResouce = new ResourceDictionary ( ); private ControlTemplate myTemplate; private const int WM_NCHITTEST = 0x0084; private const int WM_GETMINMAXINFO = 0x0024; private readonly int agWidth = 12; //拐角宽度 private readonly int bThickness = 4; // 边框宽度 private Point mousePoint = new Point ( ); //鼠标坐标 private Button maxButton; private bool _showMax=true; public bool ShowMax { get { return _showMax; } set { _showMax = value; } } public WindowBase ( ) { myWindowResouce.Source = new Uri ( "Fuss.Wpf.Themes;component/WindowTemplate.xaml", UriKind.Relative ); this.Resources.MergedDictionaries.Add ( myWindowResouce ); this.Style = (Style)myWindowResouce ["WindowStyle"]; myTemplate = (ControlTemplate)(this.Style.Setters [3] as Setter).Value; this.Loaded += new RoutedEventHandler ( WindowBase_Loaded ); this.MaxWidth = SystemParameters.MaximizedPrimaryScreenWidth; this.MaxHeight = SystemParameters.MaximizedPrimaryScreenHeight; } private void WindowBase_Loaded ( object sender, RoutedEventArgs e ) { maxButton = (Button)myTemplate.FindName ( "MaxButton", this ); //((Border)myTemplate.FindName ( "TitleBar", this )).MouseLeftButtonDown += ( s1, e1 ) => //{ // this.DragMove ( ); //}; ((TextBlock)myTemplate.FindName("TitleText", this)).Text = this.Title; if (!ShowMax) { maxButton.Visibility = Visibility.Collapsed; (myTemplate.FindName("MaxSplitter", this) as Rectangle).Visibility = Visibility.Collapsed; } else maxButton.Visibility = Visibility.Visible; this.SizeChanged += ( s1, e1 ) => { if (this.WindowState == WindowState.Normal) { maxButton.Style = (Style)myWindowResouce ["WinNormalButton"]; } else if (maxButton.Style != (Style)myWindowResouce ["WinMaxButton"] &&this.WindowState == WindowState.Maximized) { maxButton.Style = (Style)myWindowResouce ["WinMaxButton"]; } }; ( (Button)myTemplate.FindName ( "MiniButton", this ) ).Click += ( s2, e2 ) => { this.WindowState = WindowState.Minimized; }; maxButton.Click += ( s3, e3 ) => { this.WindowState = (this.WindowState == WindowState.Normal) ? WindowState.Maximized : WindowState.Normal; }; ( (Button)myTemplate.FindName ( "CloseButton", this ) ).Click += ( s4, e4 ) => { this.Close ( ); }; HwndSource hwndSource = PresentationSource.FromVisual ( this ) as HwndSource; if (hwndSource != null) { hwndSource.AddHook ( new HwndSourceHook ( WndProc ) ); } } protected virtual IntPtr WndProc ( IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled ) { switch (msg) { case WM_NCHITTEST: mousePoint.X = (lParam.ToInt32 ( ) & 0xFFFF); mousePoint.Y = (lParam.ToInt32 ( ) >> 16); handled = true; //窗体为最大化时(如果最大化,Left、Top属性都会造成影响) if (this.WindowState == WindowState.Normal) { #region 拖拽改变窗体大小 //左上角 if ((mousePoint.Y - Top <= agWidth) && (mousePoint.X - Left <= agWidth)) { return new IntPtr ( (int)HitTest.HTTOPLEFT ); } //左下角 if ((this.ActualHeight + this.Top - this.mousePoint.Y <= this.agWidth) && (this.mousePoint.X - this.Left <= this.agWidth)) { return new IntPtr ( (int)HitTest.HTBOTTOMLEFT ); } //右上角 if ((this.mousePoint.Y - this.Top <= this.agWidth) && (this.ActualWidth + this.Left - this.mousePoint.X <= this.agWidth)) { return new IntPtr ( (int)HitTest.HTTOPRIGHT ); } //右下角 if ((this.ActualWidth + this.Left - this.mousePoint.X <= this.agWidth) && (this.ActualHeight + this.Top - this.mousePoint.Y <= this.agWidth)) { return new IntPtr ( (int)HitTest.HTBOTTOMRIGHT ); } //左侧 if (this.mousePoint.X - (this.Left + 4) <= this.bThickness) { return new IntPtr ( (int)HitTest.HTLEFT ); } //右侧 if (this.ActualWidth + this.Left - 4 - this.mousePoint.X <= this.bThickness) { return new IntPtr ( (int)HitTest.HTRIGHT ); } //上侧 if (this.mousePoint.Y - (this.Top + 4) <= this.bThickness) { return new IntPtr ( (int)HitTest.HTTOP ); } //下侧 if (this.ActualHeight + this.Top - 4 - this.mousePoint.Y <= this.bThickness) { return new IntPtr ( (int)HitTest.HTBOTTOM ); } #endregion //正常情况下移动窗体 if (this.mousePoint.Y - this.Top > 0 && this.mousePoint.Y - this.Top < 40 && this.Left + this.ActualWidth - mousePoint.X > 110) { return new IntPtr ( (int)HitTest.HTCAPTION ); } } //最大化时移动窗体,让窗体正常化 if (this.WindowState == WindowState.Maximized) { if(this.mousePoint.Y< 40 && this.ActualWidth - mousePoint.X > 110) { return new IntPtr ( (int)HitTest.HTCAPTION ); } } ////将q其他区域设置为客户端,避免鼠标事件被屏蔽 return new IntPtr ( (int)HitTest.HTCLIENT ); } return IntPtr.Zero; } } /// <summary> /// 包含了鼠标的各种消息 /// </summary> public enum HitTest : int { HTERROR = -2, HTTRANSPARENT = -1, HTNOWHERE = 0, HTCLIENT = 1, HTCAPTION = 2, HTSYSMENU = 3, HTGROWBOX = 4, HTSIZE = HTGROWBOX, HTMENU = 5, HTHSCROLL = 6, HTVSCROLL = 7, HTMINBUTTON = 8, HTMAXBUTTON = 9, HTLEFT = 10, HTRIGHT = 11, HTTOP = 12, HTTOPLEFT = 13, HTTOPRIGHT = 14, HTBOTTOM = 15, HTBOTTOMLEFT = 16, HTBOTTOMRIGHT = 17, HTBORDER = 18, HTREDUCE = HTMINBUTTON, HTZOOM = HTMAXBUTTON, HTSIZEFIRST = HTLEFT, HTSIZELAST = HTBOTTOMRIGHT, HTOBJECT = 19, HTCLOSE = 20, HTHELP = 21 } }
自定义窗体完成了,现在这样使用,注意后台代码中的窗体需继承自自定义的窗体类。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<fuss:WindowBase x:Class="Shell.LoginForm" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:fuss="clr-namespace:Fuss.Wpf.Themes;assembly=Fuss.Wpf.Themes" Title="登录" Height="320" Width="480" ResizeMode="NoResize" ShowMax="False"> <Grid> <Grid Width="300" Height="120" Margin="0 30 0 0"> <Grid.RowDefinitions> <RowDefinition/> <RowDefinition/> <RowDefinition/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="100"/> <ColumnDefinition /> </Grid.ColumnDefinitions> <TextBlock Grid.Row="0" Grid.Column="0" Text="用户名:" VerticalAlignment="Center" HorizontalAlignment="Right" FontSize="18" Margin="0 0 10 0"/> <TextBlock Grid.Row="1" Grid.Column="0" Text="密码:" VerticalAlignment="Center" HorizontalAlignment="Right" FontSize="18" Margin="0 0 10 0"/> <TextBox Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" FontSize="18"/> <TextBox Grid.Row="1" Grid.Column="1" VerticalAlignment="Center" FontSize="18"/> <StackPanel Grid.Row="2" Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right"> <Button Content="登陆" Margin="5" Padding="8 3" Click="LoginButton_Click"/> <Button Content="取消" Margin="5 5 10 5" Padding="8 3"/> </StackPanel> </Grid> </Grid> </fuss:WindowBase>