本文中着重讲述通过继承Control来实现自定义控件。
通过本文的学习,我们将学到以下知识点:
1). 如何自定义控件
2). 如何自定义DataTemplate
3). 如何自定义数据源
4). 代码中如何根据数据源以及DataTemplate创建Child控件,并且添加到自定义控件的Visual tree中。
自定义控件具体实现步骤如下:
1. 定义类,继承自Control,代码如下:
Code:public class DevDivTemplatedCtrl : Control
2. 定义数据源
我们定义了CLR和DP两种,为了代码中可以直接赋值,同时支持绑定;代码如下:
代码并不复杂,我们就是想为类保存一个IEnumerable对象作为数据源Code:public static DependencyProperty DevDivItemsSourceProperty = DependencyProperty.Register("DevDivItemsSource", typeof(IEnumerable), typeof(DevDivTemplatedCtrl), new PropertyMetadata(DevDivItemsSourceChanged)); public IEnumerable DevDivItemsSource { get { return (IEnumerable)GetValue(DevDivItemsSourceProperty); } set { SetValue(DevDivItemsSourceProperty, value); } }
3. 自定义一个模板,这样控件使用者可以通过这个模板为数据源提供显示样式,代码如下:
4. 继续看类实现前,我们先看看这个控件的默认ControlTemplate,代码在Themes/Generic.xaml中Code:public DataTemplate DevDivItemTemplate { get; set; }
代码并不复杂,其中最重要的是这个自定义控件以x:Name为RootPanel的StackPanel作为数据源显示的Parent。即这个控件的使用者,是把子控件添加到RootPanel下的。Code:<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:DevDivTemplatedSample"> <Style TargetType="local:DevDivTemplatedCtrl"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="local:DevDivTemplatedCtrl"> <StackPanel x:Name="RootPanel" /> </ControlTemplate> </Setter.Value> </Setter> </Style> </ResourceDictionary>
5. 我们返回控件类文件DevDivTemplatedCtrl.cs
我们会看到,它定义了一个private成员
_pnl指向的就是RootPanel,_pnl初始化代码在OnApplyTemplate中,代码如下:Code:// RootPanel private Panel _pnl;
6. 控件代码中最重要部分就是如何读取DevDivItemsSource以及如何利用用户提供的DevDivItemTemplate把Child控件加入到DevDivTemplatedCtrl这个自定义控件中。Code:_pnl = this.GetTemplateChild("RootPanel") as Panel;
上面代码中就是遍历DevDivItemsSource中每一个Item,然后把它赋值给模板控件的DataContext;并且把模板控件添加到RootPanel得Children中。Code:private void ReconstructItemTree(IEnumerable items) { if (_pnl == null) return; // Iterate DevDivItemsSource // And assign each item to the DataContext of FrameworkElement which is loaded from DevDivItemTemplate foreach (var item in items) { FrameworkElement obj = DevDivItemTemplate.LoadContent() as FrameworkElement; obj.DataContext = item; _pnl.Children.Add(obj); } }
7. 接下来我们来看看如何使用这个自定义控件:
1). 在MainPage.xaml中添加如下代码:
实际上我们就是给DevDivTemplatedCtrl设置了名字,并且设置了一下DevDivItemTemplate,Code:<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <TemplatedCtrl:DevDivTemplatedCtrl x:Name="TemplatedCtrl1"> <TemplatedCtrl:DevDivTemplatedCtrl.DevDivItemTemplate> <DataTemplate> <TextBlock Text="{Binding Name}" /> </DataTemplate> </TemplatedCtrl:DevDivTemplatedCtrl.DevDivItemTemplate> </TemplatedCtrl:DevDivTemplatedCtrl> </Grid>
控件的名字为TemplatedCtrl1,DevDivItemTemplate中指定每一个item只有一个TextBlock,并且绑定到数据源的Name中。
2). MainPage.xaml.cs中设置数据源,数据源为一个City列表,City作为一个Item,City有一个Name属性;具体代码如下:
通过TemplatedCtrl1.DevDivItemsSource = cities;设置,自定义控件的DevDivItemsSource属性为cities,也就是第2节中提到的IEnumerable变成了cities(City列表)。Code:ObservableCollection<City> cities = new ObservableCollection<City>(); cities.Add(new City("Beijing")); cities.Add(new City("Shanghai")); TemplatedCtrl1.DevDivItemsSource = cities;
有了这个列表,第6小节中,就会遍历这个cities,取出每一个City,然后把City赋值给模板控件的DataContext,这样TextBlock就会赋值为City.Name,第6步最终把Text=City.Name的每一个TextBlock添加到RootPanel中。
Regards
Vincent
http://weibo.com/xueyw

Reply With Quote

