註意: 本文方法基礎是WindowChrome,而WindowChrome在 之後才集成發佈的。見: "WindowChrome Class" 在 中使用WindowChrome,需要安裝 "Ribbon" 來支持 "WindowChrome" ) 目前官方文檔的內容較為陳舊(但仍有參考價值),其中 ...
註意:
- 本文方法基礎是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。
相關下載
解:奇葩史
落後要挨打,個人以前一直局限於老舊方式實現窗體處理,沒有跟進WPF技術改進,在此,非常感謝@vbfool的提醒。【之前說周末搞出來,自己這幾周浪了,沒研究。今天趁著別人雙11買買買,咱就擼擼代碼】