1、测量和安排布局:MeasureOverride, ArrangeOverride
//测量(传入控件的可用大小) protected override Size MeasureOverride(Size availableSize) { //遍历所有子空间 foreach (UIElement child in InternalChildren) { //计算控件可用大小,调用子控件的MeasureOverride方法 child.Measure(availableSize); //获得控件大小 //child.DesiredSize } //返回给ArrangeOverride调用 return availableSize; } //安排布局 protected override Size ArrangeOverride(Size finalSize) { double x = 0; foreach (UIElement child in InternalChildren) { //安排控件实际渲染位置,通过 DesiredSize获得控件需要的大小 child.Arrange(new Rect(new Point(x, 0), child.DesiredSize)); x += child.DesiredSize.Width; } //返回控件实际布局大小(在父控件通过DesiredSize获得) return finalSize; }
2、依赖属性
//注册依赖属性 public static DependencyProperty TextProperty = DependencyProperty.Register("MyText", typeof(string), typeof(MySilverButton), new PropertyMetadata("默认属性值", OnTextPropertyChanged)); //依赖属性对应的控件属性 public string MyText { get { return (string) GetValue(TextProperty); } set { SetValue(TextProperty, value); } } //依赖属性回掉函数(static) private static void OnTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { //这里MySilverButton是控件类名 var mySilverButton = d as MySilverButton; if (mySilverButton!=null) mySilverButton.OnTextPropertyChanged(e); } //属性变化回掉(通过静态函数调用,也可以在静态函数里写) private void OnTextPropertyChanged(DependencyPropertyChangedEventArgs e) { var bgTextBlock = GetTemplateChild("ButtonCaption") as TextBlock; if (bgTextBlock != null) bgTextBlock.Text = e.NewValue as string; }
在xaml中定义Template时,可以通过{TemplateBinding MyText}绑定依赖属性
3、在模板加载完成后注册事件和一些初始化操作
public override void OnApplyTemplate()
4、获取子控件和资源(定义在Generic.xaml)
//获取根控件 var root = (FrameworkElement)GetTemplateChild("RootElement"); //获取控件资源 var enter = (Storyboard)root.Resources[MouseEnterAnimation];
5、元数据
在控件的定义加上TemplatePart特性,该特性不是必须的,只是一种契约,推荐这样设计控件
意思是告诉要来写ControlTemplate的用户,你的ControlTemplate中需要有一个x:Name为“LayoutRoot” , 类型为 Panel 的元素 , 因为逻辑部分对这些东西进行了引用,它们将对控件的默认行为起着关键作用, 可以理解为这个控件的最基本元素,是实现默认行为的最小集合,主要是给用户看的
[TemplatePart(Name = "LayoutRoot", Type = typeof (Panel))] [TemplateVisualState(GroupName = "HoverStates", Name = "MouseOver")] [TemplateVisualState(GroupName = "HoverStates", Name = "Normal")] public class MyControl : Control
6、视图状态
//一般在ControlTemplate中的Border控件内部定义
<VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="HoverStates"> <VisualState x:Name="MouseOver"> <Storyboard> <ColorAnimation Storyboard.TargetName="BackgroundElement" Storyboard.TargetProperty="(Rectangle.Fill).(SolidColorBrush.Color)" To="Yellow" Duration="0:0:.5" /> </Storyboard> </VisualState> <VisualState x:Name="Normal"> <Storyboard> <ColorAnimation Storyboard.TargetName="BackgroundElement" Storyboard.TargetProperty="(Rectangle.Fill).(SolidColorBrush.Color)" To="Transparent" Duration="0:0:.5" /> </Storyboard> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups>
//在视图中定义不同视图状态的过度动画逻辑
在cs中改变视图状态(this表示该控件)
VisualStateManager.GoToState(this, "MouseOver", useTransitions);
参考链接
1、WPF MeasureOverride And ArrangeOverride
http://www.cnblogs.com/dingli/archive/2011/04/22/2024786.html
2、用户自定义控件详解
http://blog.csdn.net/mr_raptor/article/details/7251942