在WPF中,概念上来说用户自己制作的控件有两种:用户控件和自定义控件。但是这两种控件之间有什么区别?这篇文章中列出了他们各自的特点,以便在项目中选择选择合适的控件类型。
用户控件(组合)
- 将多个现有的控件组合成一个可重用的“组”。
- 由一个XAML文件和一个后台代码文件。
- 不能使用样式和模板。
- 继承自UserControl类。
自定义控件(扩展)
- 在现有的控件上进行扩展,增加一些新的属性方法等。
- 包括一个代码文件和一个默认的主题文件。
- 可以使用样式和模板。
- 构建控件库的好方法。
UserControl主要是现有控件的组合。组合是好组合啊,我随便拖几个控件往页面里面一放,比如我弄个TextBox和一个button往页面里面一方,起个名字就是UserControl了,问题是这个UserControl光能看不能用啊。比如说我在WPF窗体里面要对这个UserControl里面的TextBox赋值或者获取它的值,咋获取?我想点击UserControl里的Button来触发这个UserControl所在的WPF窗体后台代码文件里面(暂不提MVVM模式)的某个事件,怎么触发?这两个问题才是创建UserControl的关键问题。
第一个问题:获取或设置属性。
建立一个wpf用户控件项目,在UserControl1.xaml里添加一个Button和TextBox。用户控件默认继承自UserControl类,你也可以修改他的所继承的类。若修改为其他类,UserControl则将拥有这个类的相应的方法和属性。这里先不修改,保持其默认的继承。
现在的主要任务是当这个用户控件放到WPF窗体里面后,在窗体里能获取或设置里面的 TextBox的值。
关键的一步是为这个用户控件添加一个依赖属性。
比如我要给这个用户控件添加一个Text属性,即当我将这个用户控件放到WPF窗口里要获取或者设置它的Text属性。
添加这个Text依赖属性的代码如下:
这样就为这个用户控件
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string),
typeof(UserControl1),
new PropertyMetadata("TextBox", new PropertyChangedCallback(OnTextChanged)));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
static void OnTextChanged(object sender, DependencyPropertyChangedEventArgs args)
{
UserControl1 source = (UserControl1)sender;
source.tb.Text = (string)args.NewValue;
}
增加了一个名字为Text的属性。若你刚好正在做这方面或者学习这方面的东西,你通过搜索看到了这篇文章估计有人会直接把上面的代码复制一下看实现了,心里很高兴。这样是很快,但是这是哪走了鱼而不是渔。这里面主要的句代码是
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string),
typeof(UserControl1),
new PropertyMetadata("TextBox", new PropertyChangedCallback(OnTextChanged)));
然后主要是DependencyProperty.Register方法。
第一个参数,是你要为这个用户控件增加的属性的名字,即你在第一个参数里面填写什么字符串将来你的用户控件将会增加以这个字符串为名字的属性。
第二个参数是指这个属性对应的数据类型。
第三个参数这个属性所有者的类型。
第四个参数属性改变时触发的回调事件。
这个方法及其参数弄懂后,就很容易来为用户控件增加属性了。
下面第二个大问题,事件传阅。
比如我们想让这个用户控件暴露给窗体一个MyButtonClick事件。代码如下
public static readonly RoutedEvent MyButtonClickEvent =
EventManager.RegisterRoutedEvent("MyButtonClick", RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler<object>), typeof(UserControl1));
public event RoutedPropertyChangedEventHandler<object> MyButtonClick
{
add
{
this.AddHandler(MyButtonClickEvent, value);
}
remove
{
this.RemoveHandler(MyButtonClickEvent, value);
}
}
public void OnMyButtonClick(object oldValue, object newValue)
{
RoutedPropertyChangedEventArgs<object> arg =
new RoutedPropertyChangedEventArgs<object>(oldValue, newValue, MyButtonClickEvent);
this.RaiseEvent(arg);
}
这样通过这两段代码你的用户控件就得到了一个Text属性和一个MyButtonClick方法。
请注意以上两段代码中,特别是第二段注册事件的代码中要特别注意,当你的用户控件继承的基类不同时,注册事件时可能所用的参数和事件的类型会有所不同,比如msdn上有个例子是继承自Button的,其中的事件类型和参数就不同:
public class MyButtonSimple: Button
{
// Create a custom routed event by first registering a RoutedEventID
// This event uses the bubbling routing strategy
public static readonly RoutedEvent TapEvent = EventManager.RegisterRoutedEvent(
"Tap", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyButtonSimple));
// Provide CLR accessors for the event
public event RoutedEventHandler Tap
{
add { AddHandler(TapEvent, value); }
remove { RemoveHandler(TapEvent, value); }
}
// This method raises the Tap event
void RaiseTapEvent()
{
RoutedEventArgs newEventArgs = new RoutedEventArgs(MyButtonSimple.TapEvent);
RaiseEvent(newEventArgs);
}
// For demonstration purposes we raise the event when the MyButtonSimple is clicked
protected override void OnClick()
{
RaiseTapEvent();
}
}