繼承 FrameworkElement 的元素自行繪製;但繼承 Control 的控制項應將外觀委派給 ControlTemplate。這樣才能像內建控制項一樣支援樣式替換。所有內建 WPF 控制項都是「無外觀」的。
模板可以替換視覺樹,但必須提供控制項所需的特定特徵。協定形式隨控制項而異:
控制項定義公用相依性屬性,模板決定哪些屬性在視覺樹中可見。控制項不關心視覺樹長什麼樣,只暴露屬性與命令。事件可透過路由機制從視覺樹冒泡到控制項。
<ControlTemplate TargetType="{x:Type local:MyCustomControl}">
<Grid>
<Rectangle Fill="{TemplateBinding Foo}" />
</Grid>
</ControlTemplate>
所有相依性屬性自動支援屬性別名。
ContentControl 時,模板中預期有一個 ContentPresenter 作為內容佔位符。不強制要求存在,但缺少則內容不顯示。ItemsControl 使用,若模板不想指定特定 Panel 型別時使用。如 ItemsControl 尋找 Panel.IsItemsHost="true" 的元素作為項目容器。自訂控制項可定義專屬附屬相依性屬性(如 IsMyPlaceholder),並在屬性變更回呼中尋找被標記的元素:
public class ControlWithPlaceholder : Control {
public static readonly DependencyProperty IsMyPlaceholderProperty =
DependencyProperty.RegisterAttached("IsMyPlaceholder", typeof(bool),
typeof(ControlWithPlaceholder),
new PropertyMetadata(false, OnIsMyPlaceholderChanged));
private static void OnIsMyPlaceholderChanged(DependencyObject target) {
FrameworkElement fe = target as FrameworkElement;
if (fe != null && GetIsMyPlaceholder(fe)) {
ControlWithPlaceholder ctrl = fe.TemplatedParent as ControlWithPlaceholder;
if (ctrl != null) ctrl.placeholder = fe;
}
}
private FrameworkElement placeholder;
}
模板中使用:
<ControlTemplate TargetType="{x:Type local:ControlWithPlaceholder}">
<Grid local:ControlWithPlaceholder.IsMyPlaceholder="true" />
</ControlTemplate>
複雜控制項(如 Slider)需多個特定元素(Thumb、Track、按鈕),可定義多個附屬屬性分別標記。控制項通常對缺少的佔位符寬容處理,僅啟用對應功能。
ContentPresenter 或自訂附屬屬性標記佔位符。