<Window>,改為基類 <src:BaseWindow> 時,若基類本身有 XAML 會報錯「無法作為 XAML 根元素」。解決:基類僅用 C# 定義,不提供 BaseWindow.xaml。this.Content,子視窗 XAML 內容會取代之。解決:將標題列、按鈕移入 ControlTemplate,內容由 ContentPresenter 呈現。<ControlTemplate x:Key="BaseWindowTemplate" TargetType="{x:Type Window}">
<DockPanel LastChildFill="True">
<Border DockPanel.Dock="Top" Background="#FF7097D0" CornerRadius="4,4,0,0">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button x:Name="BtnMin" Content="Min" />
<Button x:Name="BtnMax" Content="Max" />
<Button x:Name="BtnClose" Content="Close" />
</StackPanel>
</Border>
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="0,0,4,4">
<AdornerDecorator><ContentPresenter /></AdornerDecorator>
</Border>
</DockPanel>
</ControlTemplate>
<Style x:Key="BaseWindowStyle" TargetType="{x:Type Window}">
<Setter Property="Template" Value="{StaticResource BaseWindowTemplate}" />
<Setter Property="WindowStyle" Value="None" />
<Setter Property="AllowsTransparency" Value="True" />
<Setter Property="BorderBrush" Value="#FF7097D0" />
<Setter Property="BorderThickness" Value="4,0,4,4" />
</Style>
public class BaseWindow : Window
{
public BaseWindow()
{
Loaded += (s, e) => InitializeChrome();
}
private void InitializeChrome()
{
var template = (ControlTemplate)Application.Current.Resources["BaseWindowTemplate"];
var minBtn = (Button)template.FindName("BtnMin", this);
var maxBtn = (Button)template.FindName("BtnMax", this);
var closeBtn = (Button)template.FindName("BtnClose", this);
minBtn.Click += (s, e) => WindowState = WindowState.Minimized;
maxBtn.Click += (s, e) => WindowState = WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized;
closeBtn.Click += (s, e) => Close();
}
}
關鍵:必須在 Loaded 後呼叫 FindName,此時 Template 已套用完成。
<src:BaseWindow xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:MyProject"
x:Class="MyProject.ChildWindow"
Style="{StaticResource BaseWindowStyle}">
<Grid>
<TextBlock Text="這是子視窗內容" />
</Grid>
</src:BaseWindow>
所有彈出視窗統一外觀與行為,維護成本大幅降低。