WPF支持样式、内容控件和模板,这些特性提供了多种方式来完善和扩展标准的控件,因此不再强调自定义控件。
- 样式 - 方便地重用控件属性的组合。甚至可使用触发器应用效果。
- 内容控件 - 所有继承自 ContentControl 类的控件都支持嵌套的内容。
- 控件模板 - 所有控件都是无外观的,意味着都具有硬编码的功能,外观是通过控件模板单独定义的。
- 数据模板 - 所有派生自 ItemsControl 的类都支持数据模板,可创建某些数据对象类型的富列表表示。
因此当希望微调元素的外观时,自定义元素并非最佳选择。如果希望改变底层的功能时,自定义元素就十分有用了。一般在专门的类库程序集(DLL)中放置自定义元素,可在多个程序之间共享自定义元素。
构建自定义控件
创建自定义控件的第一步是选择正确的基类进行继承:
基类 | 说明 |
---|---|
FrameworkElement | 最低级的基类,希望重写 OnRender() 方法 |
Control | 最常用的基类,是所有用户交互小组件的基类。Control添加了背景,前景,字体和内容对齐,Tab顺序,鼠标双击,Template属性 |
ContentControl | 能够显示任意单一内容的控件的基类 |
UserControl | 可使用设计视图进行配置的内容控件 |
ItemsControl 或 Selector | ItemsControl是封装项列表的控件的基类,但不支持选择 |
Panel | 具有布局逻辑控件的基类 |
Decorator | 封装其他元素的元素的基类 |
定义依赖项属性
// 1:定义静态字段,以Property结尾
public static DependencyProperty ColorProperty;
// 2:静态的构造函数
static ColorPicker()
{
// 依赖属性
ColorProperty = DependencyProperty.Register("Color", typeof(Color), typeof(ColorPicker),
new FrameworkPropertyMetadata(Colors.Black, new PropertyChangedCallback(OnColorChanged)));
// 路由事件
// RoutedEventHandler委托用于不带额外信息的路由事件
// RoutedPropertyChangedEventHandler用于提供属性发生之后的旧值和新值的路由事件
ColorChangedEvent = EventManager.RegisterRoutedEvent("ColorChanged", RoutingStrategy.Bubble,
typeof(RoutedPropertyChangedEventHandler<Color>), typeof(ColorPicker));
}
// 属性变化的回调函数
private static void OnColorChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
// 当 Color 值改变时修改 RGB的值
Color newColor = (Color)e.NewValue;
ColorPicker colorPicker = (ColorPicker)sender;
colorPicker.Red = newColor.R;
colorPicker.Green = newColor.G;
colorPicker.Blue = newColor.B;
// 引发路由事件
Color oldColor = (Color)e.OldValue;
RoutedPropertyChangedEventArgs<Color> args = new RoutedPropertyChangedEventArgs<Color>(oldColor, newColor);
args.RoutedEvent = ColorPicker.ColorChangedEvent;
colorPicker.RaiseEvent(args);
}
// 3:添加属性封装器,使访问更加容易,并可在XAML中使用它们
public Color Color
{
get { return (Color)GetValue(ColorProperty); }
// 当属性变化时 SetValue 引发回调函数
set { SetValue(ColorProperty, value); }
}
// 4: 路由事件,当发生一些事情时用于通知控件的使用者
public static readonly RoutedEvent ColorChangedEvent;
// 路由事件委托 - 不一定要为事件签名创建新的委托
// RoutedEventHandler委托用于不带额外信息的路由事件
// RoutedPropertyChangedEventHandler用于提供属性发生之后的旧值和新值的路由事件
public event RoutedPropertyChangedEventHandler<Color> ColorChanged
{
add { AddHandler(ColorChangedEvent, value); }
remove { RemoveHandler(ColorChangedEvent, value); }
}
命令支持
- 添加将控件链接到特定命令的命令绑定
- 为命令创建新的 RoutedUICommand 对象,作为自定义控件的静态字段。
// 第一次创建控件时是创建命令绑定的最佳时机
public ColorPicker()
{
InitializeComponent();
SetUpCommands();
}
private void SetUpCommands()
{
CommandBinding binding = new CommandBinding(ApplicationCommands.Undo, UndoCommand_Executed, UndoCommand_CanExecute);
this.CommandBindings.Add(binding);
}
private void UndoCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = previousColor.HasValue;
}
private void UndoCommand_Executed(object sender, ExecuteRoutedEventArgs e)
{
this.Color = (Color)previousColor;
}
<Button Command="Undo" CommandTarget="{Binding ElementName=colorPicker}">
Undo
</Button>
上面代码中的命令比较脆弱,因为可以自由地修改CommandBindings集合。更可靠的技术是使用 CommandManager.RegisterClassCommandBinding 方法在静态构造函数中创建命令。