WPF中自定義標題欄時窗體最大化處理之WindowChrome
部落格分類: ofollow,noindex" target="_blank">FAQ
注意:
-
本文方法基礎是WindowChrome,而WindowChrome在
.NET Framework 4.5
之後才整合釋出的。見:WindowChrome Class -
在
.NET Framework 4.0
中使用WindowChrome,需要安裝Ribbon 來支援WindowChrome -
目前官方文件的內容較為陳舊(但仍有參考價值),其中提到了
SystemParameters2
,這個應該是Ribbon裡的東西,4.5
想用可以安裝Xceed.Wpf.AvalonDock
庫,這裡面有現成的Microsoft.Windows.Shell.SystemParameters2
實現——當然,自己不怕麻煩,可以自己實現來獲取要用的系統變數。(一般用不著,4.5
中SystemParameters
添加了許多系統變數的實現)
實現步驟
第一步:基本實現【保留系統基礎操作按鈕】
- 新增Window的Style定義,並設定WindowChrome.WindowChrome屬性;
-
設定WindowChrome標題欄:
- CaptionHeight——主要用於拖動有效區;
- GlassFrameThickness——影響標題欄系統按鈕顯示,0表示不使用系統按鈕【後面介紹】,-1表示用的系統預設值,如下示例則表示標題欄高度30;
- 自定義窗體Title
注意:控制元件模板中的定義:
- 1、最外層Border背景無顏色,否則會覆蓋標題欄,看不到系統按鈕。
- 2、內部佈局定義,使用Grid隔出30的標題欄高度,也可以直接對ContentPresenter設定Margin【主要是為了讓頂部顯示出標題欄】。
<Style x:Key="WindowStyle1" TargetType="{x:Type Window}"> <Setter Property="WindowChrome.WindowChrome"> <Setter.Value> <WindowChrome CaptionHeight="30" GlassFrameThickness="0,30,0,0"/> </Setter.Value> </Setter> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type Window}"> <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" > <AdornerDecorator> <Grid> <Grid.RowDefinitions> <RowDefinition Height="30"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <ContentPresenter Grid.Row="1"/> <TextBlock Text="{TemplateBinding Title}" HorizontalAlignment="Left" VerticalAlignment="Center" /> </Grid> </AdornerDecorator> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style>
存在的問題: 最大化邊框問題 —— 會有部分溢位。
根據觀察,這個溢位寬度應該是8
,此值的來源:(SystemParameters.MaximizedPrimaryScreenWidth - SystemParameters.WorkArea.Width)/2
,高度也可以這樣計算。
第二步:優化邊界處理
- 方法1:模板新增最大化觸發器,設定最大化時,內部佈局Margin設為8
- 方法2:模板新增最大化觸發器,設定最大化時,限制佈局最大化的寬高最大值
不管使用哪種方法,最大化時,系統的標題欄高度都發生了變化,故,需要重新設定模板中定義的標題欄高度。
<ControlTemplate TargetType="{x:Type Window}"> <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" > <AdornerDecorator> <Grid Name="win_content"> <Grid.RowDefinitions> <RowDefinition Height="30" x:Name="row_title"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <ContentPresenter Grid.Row="1"/> <TextBlock Text="{TemplateBinding Title}" HorizontalAlignment="Left" VerticalAlignment="Center" /> </Grid> </AdornerDecorator> </Border> <ControlTemplate.Triggers> <Trigger Property="WindowState" Value="Maximized"> <Setter Property="Margin" TargetName="win_content" Value="8"/> <!--二選一--> <Setter Property="MaxWidth" TargetName="win_content" Value="{Binding Source={x:Static SystemParameters.WorkArea},Path=Width}" /> <Setter Property="MaxHeight" TargetName="win_content" Value="{Binding Source={x:Static SystemParameters.WorkArea},Path=Height}"/> <Setter Property="Height" TargetName="row_title" Value="22" /> </Trigger> </ControlTemplate.Triggers> </ControlTemplate>
至此,都是系統標題欄和我們自定義內容組合使用,但這樣總有些邊邊角角要修正,下面就完全自定義標題欄
第三步:完全自定義標題欄【即,不使用系統的操作按鈕】
-
初步操作類似第一步,其中將
GlassFrameThickness
設定為0
-
在內容定義部分新增自定義的標題欄,新增操作按鈕,並設定按鈕屬性
WindowChrome.IsHitTestVisibleInChrome="True"
如果不設定WindowChrome.IsHitTestVisibleInChrome
,則由於我們之前設定CaptionHeight
,則這個區域內,按鈕將失效。
但是,也不能將整個標題欄佈局設定這個屬性,那樣會完全覆蓋系統標題欄的操作,如拖動效果,即CaptionHeight
設定的那個區域。
<!--樣式定義--> <Style x:Key="WindowStyle2" TargetType="{x:Type Window}"> <Setter Property="WindowChrome.WindowChrome"> <Setter.Value> <WindowChrome UseAeroCaptionButtons="False" GlassFrameThickness="0" CaptionHeight="30"/> </Setter.Value> </Setter> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type Window}"> <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}"> <AdornerDecorator > <ContentPresenter x:Name="win_content" /> </AdornerDecorator> </Border> <ControlTemplate.Triggers> <Trigger Property="WindowState" Value="Maximized"> <Setter Property="Margin" TargetName="win_content" Value="8"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style>
<!--定義標題欄--> <Grid Background="Red" > <Grid Height="30" HorizontalAlignment="Stretch" VerticalAlignment="Top" Background="Blue"> <StackPanel Orientation="Horizontal" WindowChrome.IsHitTestVisibleInChrome="True" HorizontalAlignment="Right" > <Button Name="btn_Min" Content="—" ></Button> <Button Name="btn_Max" Content="☐" ></Button> <Button Name="btn_Close" Content="✕" ></Button> </StackPanel> </Grid> </Grid>
//標題欄按鈕功能實現 public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); this.btn_Min.Click += Btn_Min_Click; this.btn_Max.Click += Btn_Max_Click; this.btn_Close.Click += Btn_Close_Click; } private void Btn_Close_Click(object sender, RoutedEventArgs e) { this.Close(); } private void Btn_Max_Click(object sender, RoutedEventArgs e) { this.WindowState = WindowState.Maximized == this.WindowState ? WindowState.Normal : WindowState.Maximized; } private void Btn_Min_Click(object sender, RoutedEventArgs e) { this.WindowState = WindowState.Minimized; } }
關聯瞭解:
如果你平時也使用Visual Studio Code【VSCode】,文中的第一步和第三步,在vscode的設定中有對應效果:
- 第一步 —— 將vscode使用者設定中Title Bar Style值設為native;
- 第三步 —— 將vscode使用者設定中Title Bar Style值設為custom。