1. 前言
WPF有一个灵活的UI框架,用户可以轻松地使用代码控制控件的外观。例设我需要一个控件在鼠标进入的时候背景变成蓝色,我可以用下面这段代码实现:
protected override void OnMouseEnter(MouseEventArgs e)
{
base.OnMouseEnter(e);
Background = new SolidColorBrush(Colors.Blue);
}
但一般没人会这么做,因为这样做代码和UI过于耦合,难以扩展。正确的做法应该是使用代码告诉ControlTemplate去改变外观,或者控制ControlTemplate中可用的元素进入某个状态。
这篇文章介绍自定义控件的代码如何和ControlTemplate交互,涉及的知识包括RelativeSource、Trigger、TemplatePart和VisualState。
2. 简单的Expander
本文使用一个简单的Expander介绍UI和ControlTemplate交互的几种技术,它的代码如下:
public class MyExpander : HeaderedContentControl
{
public MyExpander()
{
DefaultStyleKey = typeof(MyExpander);
}
public bool IsExpanded
{
get => (bool)GetValue(IsExpandedProperty);
set => SetValue(IsExpandedProperty, value);
}
public static readonly DependencyProperty IsExpandedProperty =
DependencyProperty.Register(nameof(IsExpanded), typeof(bool), typeof(MyExpander), new PropertyMetadata(default(bool), OnIsExpandedChanged));
private static void OnIsExpandedChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
var oldValue = (bool)args.OldValue;
var newValue = (bool)args.NewValue;
if (oldValue == newValue)
return;
var target = obj as MyExpander;
target?.OnIsExpandedChanged(oldValue, newValue);
}
protected virtual void OnIsExpandedChanged(bool oldValue, bool newValue)
{
if (newValue)
OnExpanded();
else
OnCollapsed();
}
protected virtual void OnCollapsed()
{
}
protected virtual void OnExpanded()
{
}
}
<Style TargetType="{x:Type local:MyExpander}">
<Setter Property="HorizontalContentAlignment"
Value="Stretch" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MyExpander}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<StackPanel>
<ToggleButton x:Name="ExpanderToggleButton"
Content="{TemplateBinding Header}"
IsChecked="{Binding IsExpanded,RelativeSource={RelativeSource Mode=TemplatedParent},Mode=TwoWay}" />
<ContentPresenter Grid.Row="1"
x:Name="ContentPresenter"
Horizonta