接上一篇的通用窗體模板,這個模板是重寫窗體樣式的自定義模板,與上篇不一樣的是它將窗體的一些基本功能所有清空(放大、縮小、關閉按鈕,窗體雙擊放大縮小,窗體可拖拉大小,標題和logo等)。咱們須要重寫函數來實現這些功能。express
首先是新建窗體文件window.xaml,爲什麼不用資源字典文件呢?由於咱們須要寫函數因此須要cs文件,而窗體文件新建後會自動生成window.xaml.cs,只需將window標籤修改成ResourceDictionary便可。app
cs文件中修改:函數
而後開始寫窗體樣式,在xaml中添加:ui
<!-- 菜單按鈕組模板 --> <Style x:Key="WindowCenterMenuBtn" TargetType="Button"> <Setter Property="Foreground" Value="White"></Setter> <Setter Property="Opacity" Value="0.2"></Setter> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Button"> <TextBlock FontSize="25" Text="{TemplateBinding Content}" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock> <ControlTemplate.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Opacity" Value="1.0"></Setter> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> <!-- 通用窗口模板-標題居中 --> <ControlTemplate x:Key="WindowCenterTemplate" TargetType="Window"> <Border x:Name="WindowCenterFrame" Margin="0" CornerRadius="0" BorderThickness="1" BorderBrush="DodgerBlue" Background="#FFFFFF" MouseLeftButtonDown="CustomWindow_MouseLeftButtonDown" > <Border.Effect> <DropShadowEffect BlurRadius="3" RenderingBias="Performance" ShadowDepth="0" Opacity="1"/> </Border.Effect> <Grid > <Grid.RowDefinitions> <RowDefinition Height="30"></RowDefinition> <RowDefinition Height="*"></RowDefinition> </Grid.RowDefinitions> <Border Grid.Row="0"> <Border.Background> <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0" Opacity="0.6"> <GradientStop Color="#FFFBFBFC" Offset="1"/> <GradientStop Color="#FFF3F7FB" Offset="0.021"/> </LinearGradientBrush> </Border.Background> <Grid MouseDown="CustomWindow_MouseDoubleDown"> <Grid.ColumnDefinitions> <ColumnDefinition Width="1*"/> <ColumnDefinition Width="120"/> </Grid.ColumnDefinitions> <StackPanel Grid.Column="0" Orientation="Horizontal" VerticalAlignment="Bottom" HorizontalAlignment="Center" Margin="60,0,0,0"> <Button Width="22" HorizontalAlignment="Center" Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}" WindowChrome.IsHitTestVisibleInChrome="True" Command="{x:Static SystemCommands.ShowSystemMenuCommand}" CommandParameter="{Binding ElementName=_MainWindow}"> <!-- Make sure there is a resource with name Icon in MainWindow --> <Image Source="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Icon}" WindowChrome.IsHitTestVisibleInChrome="True"/> </Button> <TextBlock VerticalAlignment="Center" Text="{Binding Title, RelativeSource={RelativeSource TemplatedParent}}" FontSize="13"/> </StackPanel> <StackPanel Orientation="Horizontal" Grid.Column="1" HorizontalAlignment="Right" Margin="0,0,20,0" VerticalAlignment="Center"> <Button x:Name="MinBtn" Height="20" Width="30" Content="-" Style="{StaticResource ResourceKey=WindowCenterMenuBtn}" Click="CustomWindowBtnMinimized_Click" /> <Button x:Name="MaxBtn" Height="20" Width="30" Content="□" Style="{StaticResource ResourceKey=WindowCenterMenuBtn}" Click="CustomWindowBtnMaxNormal_Click" /> <Button x:Name="CloseBtn" Height="20" Width="30" Content="×" Style="{StaticResource ResourceKey=WindowCenterMenuBtn}" Click="CustomWindowBtnClose_Click" /> </StackPanel> </Grid> </Border> <Grid Grid.Row="1"> <AdornerDecorator> <ContentPresenter></ContentPresenter> </AdornerDecorator> </Grid> </Grid> </Border> </ControlTemplate> <!-- 通用窗口樣式-標題居中 --> <Style x:Key="WindowCenterChrome" TargetType="Window"> <Setter Property="AllowsTransparency" Value="True"></Setter> <Setter Property="Background" Value="Transparent"></Setter> <Setter Property="WindowStyle" Value="None"></Setter> <Setter Property="ResizeMode" Value="NoResize"></Setter> <Setter Property="Template" Value="{StaticResource WindowCenterTemplate}"></Setter> </Style>
接下來是功能函數,在cs文件中添加代碼,實現窗口的放大、縮小、關閉、雙擊放大縮小、鼠標拖動窗口等。this
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Windows.Input; using System.Windows; using System.Windows.Controls; using System.Windows.Interop; using System.Windows.Media; using System.Windows.Shapes; using System.Windows.Threading; namespace Resource.Styles.Base { public partial class CustomWindow : ResourceDictionary { public class Tools { //雙擊事件定時器 private static DispatcherTimer _timer; //是否單擊過一次 private static bool _isFirst; static Tools() { _timer = new DispatcherTimer(); _timer.Interval = new TimeSpan(0, 0, 0, 0, 400); _timer.Tick += new EventHandler(_timer_Tick); } /// <summary> /// 判斷是否雙擊 /// </summary> /// <returns></returns> public static bool IsDoubleClick() { if (!_isFirst) { _isFirst = true; _timer.Start(); return false; } else { return true; } } //間隔時間 private static void _timer_Tick(object sender, EventArgs e) { _isFirst = false; _timer.Stop(); } } // 拖動 private void CustomWindow_MouseLeftButtonDown(object sender, MouseEventArgs e) { Window win = (Window) ((FrameworkElement) sender).TemplatedParent; if (e.LeftButton == MouseButtonState.Pressed) { win.DragMove(); } } //雙擊放大縮小 private void CustomWindow_MouseDoubleDown(object sender, MouseEventArgs e) { Window win = (Window) ((FrameworkElement) sender).TemplatedParent; if (Tools.IsDoubleClick()) { win.WindowState = win.WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized; } } // 關閉 private void CustomWindowBtnClose_Click(object sender, RoutedEventArgs e) { Window win = (Window) ((FrameworkElement) sender).TemplatedParent; //提示是否要關閉 if (MessageBox.Show("肯定要退出系統?", "提示", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes) { System.Windows.Application.Current.Shutdown(); } } // 最小化 private void CustomWindowBtnMinimized_Click(object sender, RoutedEventArgs e) { Window win = (Window) ((FrameworkElement) sender).TemplatedParent; win.WindowState = WindowState.Minimized; } // 最大化、還原 private void CustomWindowBtnMaxNormal_Click(object sender, RoutedEventArgs e) { Window win = (Window) ((FrameworkElement) sender).TemplatedParent; if (win.WindowState == WindowState.Maximized) { win.WindowState = WindowState.Normal; } else { // 不覆蓋任務欄 win.MaxWidth = SystemParameters.WorkArea.Width; win.MaxHeight = SystemParameters.WorkArea.Height; win.WindowState = WindowState.Maximized; } } } }
最後在你要應用自定義樣式的window窗體中添加引用:spa
<Window x:Class="EGIS.Main.UI.OPWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" Style="{DynamicResource WindowCenterChrome}" mc:Ignorable="d" WindowStartupLocation="CenterScreen" Loaded="OPWindow_Loaded" > <Window.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="pack://application:,,,/EGIS.Resource;component/Resource/Generic.xaml" /> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Window.Resources>
在這個窗體的cs文件中的OPWindow_Loaded函數中添加以下語句(實現頁面加載全屏不遮擋任務欄):code
private void OPWindow_Loaded(object sender, RoutedEventArgs e) { try { //使頁面全屏化但不遮擋任務欄 this.MaxWidth = SystemParameters.WorkArea.Width; this.MaxHeight = SystemParameters.WorkArea.Height; this.WindowState = WindowState.Maximized; } catch (Exception ex) { LogService.WriteExceptionLog(ex, ""); } }
還有一個窗體事件爲鼠標拖動改變窗口大小,這個查詢了網上的一些方法,最終用下面的代碼實現:component
#region 重寫頁面,鼠標左鍵拖動改變窗口大小 #region 這一部分是四個邊加上四個角 public enum ResizeDirection { Left = 1, Right = 2, Top = 3, TopLeft = 4, TopRight = 5, Bottom = 6, BottomLeft = 7, BottomRight = 8, } #endregion #region 用於改變窗體大小 [DllImport("user32.dll", CharSet = CharSet.Auto)] private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); private void ResizeWindow(ResizeDirection direction) { SendMessage(hs.Handle, WM_SYSCOMMAND, (IntPtr)(61440 + direction), IntPtr.Zero); } #endregion #region 爲元素註冊事件 private void InitializeEvent() { //獲取當前窗口調用的style樣式 Style temStyle = this.Resources["CustomWindowChrome"] as Style; if (temStyle!=null) { Setter temSetter = null; foreach (Setter item in temStyle.Setters) { if (item.Value == Template) temSetter = item; } ControlTemplate baseWindowTemplate = (ControlTemplate)temSetter.Value; Border borderClip = (Border)baseWindowTemplate.FindName("CustomWindowFrame", this); borderClip.MouseMove += delegate { DisplayResizeCursor(null, null); }; borderClip.PreviewMouseDown += delegate { Resize(null, null); }; borderClip.MouseLeftButtonDown += delegate { DragMove(); }; this.PreviewMouseMove += delegate { ResetCursor(null, null); }; } } #endregion #region 重寫的DragMove,以便解決利用系統自帶的DragMove出現Exception的狀況 public new void DragMove() { if (this.WindowState == WindowState.Normal) { SendMessage(hs.Handle, WM_SYSCOMMAND, (IntPtr)0xf012, IntPtr.Zero); SendMessage(hs.Handle, WM_LBUTTONUP, IntPtr.Zero, IntPtr.Zero); } } #endregion #region 顯示拖拉鼠標形狀 private void DisplayResizeCursor(object sender, MouseEventArgs e) { Point pos = Mouse.GetPosition(this); double x = pos.X; double y = pos.Y; double w = this.ActualWidth; //注意這個地方使用ActualWidth,纔可以實時顯示寬度變化 double h = this.ActualHeight; if (x <= relativeClip & y <= relativeClip) // left top { this.Cursor = Cursors.SizeNWSE; } if (x >= w - relativeClip & y <= relativeClip) //right top { this.Cursor = Cursors.SizeNESW; } if (x >= w - relativeClip & y >= h - relativeClip) //bottom right { this.Cursor = Cursors.SizeNWSE; } if (x <= relativeClip & y >= h - relativeClip) // bottom left { this.Cursor = Cursors.SizeNESW; } if ((x >= relativeClip & x <= w - relativeClip) & y <= relativeClip) //top { this.Cursor = Cursors.SizeNS; } if (x >= w - relativeClip & (y >= relativeClip & y <= h - relativeClip)) //right { this.Cursor = Cursors.SizeWE; } if ((x >= relativeClip & x <= w - relativeClip) & y > h - relativeClip) //bottom { this.Cursor = Cursors.SizeNS; } if (x <= relativeClip & (y <= h - relativeClip & y >= relativeClip)) //left { this.Cursor = Cursors.SizeWE; } } #endregion #region 還原鼠標形狀 private void ResetCursor(object sender, MouseEventArgs e) { if (Mouse.LeftButton != MouseButtonState.Pressed) { this.Cursor = Cursors.Arrow; } } #endregion #region 判斷區域,改變窗體大小 private void Resize(object sender, MouseButtonEventArgs e) { Point pos = Mouse.GetPosition(this); double x = pos.X; double y = pos.Y; double w = this.ActualWidth; double h = this.ActualHeight; if (x <= relativeClip & y <= relativeClip) // left top { this.Cursor = Cursors.SizeNWSE; ResizeWindow(ResizeDirection.TopLeft); } if (x >= w - relativeClip & y <= relativeClip) //right top { this.Cursor = Cursors.SizeNESW; ResizeWindow(ResizeDirection.TopRight); } if (x >= w - relativeClip & y >= h - relativeClip) //bottom right { this.Cursor = Cursors.SizeNWSE; ResizeWindow(ResizeDirection.BottomRight); } if (x <= relativeClip & y >= h - relativeClip) // bottom left { this.Cursor = Cursors.SizeNESW; ResizeWindow(ResizeDirection.BottomLeft); } if ((x >= relativeClip & x <= w - relativeClip) & y <= relativeClip) //top { this.Cursor = Cursors.SizeNS; ResizeWindow(ResizeDirection.Top); } if (x >= w - relativeClip & (y >= relativeClip & y <= h - relativeClip)) //right { this.Cursor = Cursors.SizeWE; ResizeWindow(ResizeDirection.Right); } if ((x >= relativeClip & x <= w - relativeClip) & y > h - relativeClip) //bottom { this.Cursor = Cursors.SizeNS; ResizeWindow(ResizeDirection.Bottom); } if (x <= relativeClip & (y <= h - relativeClip & y >= relativeClip)) //left { this.Cursor = Cursors.SizeWE; ResizeWindow(ResizeDirection.Left); } } #endregion #endregion
代碼中注意修改一下,將CustomWindowChrome改成通用窗體的名稱WindowCenterChrome。CustomWindowFrame修改成WindowCenterFrame。orm
到此,一個徹底重寫的自定義樣式就完成了,還有一點和上篇的通用窗體樣式同樣,會與WindowsFormsHost控件有衝突。xml
注意代碼中的標籤或樣式名稱問題,可能會有沒有統一的部分,使用時本身排查一下。