(感谢Microsoft公司的Ocean Ju提供本文章的思路)。
创建CustomControl的步骤我就不再累述,不清楚的请参考综合应用WPF/WCF/WF/LINQ之三:采用用代码创建的方式实现CheckListBox的CustomControl
。
为了方便大家学习,请单击此处下载该程序的代码。注意:代码中的GetProperty、GetControl等为扩展方法,具体代码请参考源代码。
这次Themes\CheckListBox.xaml的内容为:
4 xmlns:local="clr-namespace:Eallies.OA.UI.Controls.Common">
6 <local:CheckListBoxItemConverter x:Key="CheckListBoxItemConverter" />
8 <Style TargetType="{x:Type local:CheckListBox}" BasedOn="{StaticResource {x:Type ListBox}}">
9 <Setter Property="ItemTemplate">
12 <CheckBox Name="chkCheckBox">
14 <MultiBinding Converter="{StaticResource CheckListBoxItemConverter}">
15 <MultiBinding.Bindings>
17 <Binding Path="ItemValuePath" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type local:CheckListBox}}" />
18 </MultiBinding.Bindings>
22 <MultiBinding Converter="{StaticResource CheckListBoxItemConverter}">
23 <MultiBinding.Bindings>
25 <Binding Path="ItemContentPath" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type local:CheckListBox}}" />
26 </MultiBinding.Bindings>
我们的思路是:具体Binding哪个字段可以用ItemValuePath和ItemContentPath这两个字段来配置,而根据字段名来取值则采用反射的方法。为了实现这个思路,我们需要使用Binding的Converter技术。这里需要传入两个参数,一个是当前Item,一个是字段名,因此我们采用MultiBinding技术。该Converter类的代码如下:
1 public class CheckListBoxItemConverter : IMultiValueConverter
3 #region IMultiValueConverter Members
5 public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
9 object item = values[0];
10 string name = values[1] as string;
12 return item.GetProperty(name).GetValue(item, null);
20 public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
22 throw new NotSupportedException("Not supported.");
而这两个字段名,则可以采用DependencyProperty技术来实现:
1 public string ItemValuePath
3 get { return (string)GetValue(ItemValuePathProperty); }
4 set { SetValue(ItemValuePathProperty, value); }
7 public static readonly DependencyProperty ItemValuePathProperty =
8 DependencyProperty.Register("ItemValuePath", typeof(string), typeof(CheckListBox));
10 public string ItemContentPath
12 get { return (string)GetValue(ItemContentPathProperty); }
13 set { SetValue(ItemContentPathProperty, value); }
16 public static readonly DependencyProperty ItemContentPathProperty =
17 DependencyProperty.Register("ItemContentPath", typeof(string), typeof(CheckListBox));
至此,我们已经能够实现对CheckListBox控件的数据绑定了。
但还不够,我们需要处理CheckBox的Checked和Unchecked事件。为了处理这个问题,我们采用EventManager.RegisterClassHandler方法来注册CheckBox的CheckedEvent和UncheckedEvent。这里需要注意的是:如果界面上有两个以上的CheckListBox控件,则两个控件中的OnChecked和OnUnchecked事件都会触发,因此,我们需要做一个判断:if (sender.Equals(this) == false) return;
3 EventManager.RegisterClassHandler(typeof(CheckListBox), CheckBox.CheckedEvent, new RoutedEventHandler(OnChecked));
4 EventManager.RegisterClassHandler(typeof(CheckListBox), CheckBox.UncheckedEvent, new RoutedEventHandler(OnUnchecked));
7 private void OnChecked(object sender, RoutedEventArgs e)
11 if (sender.Equals(this) == false) return;
13 this._CheckedValues.Add((e.OriginalSource as CheckBox).Tag.ToType<int>());
21 private void OnUnchecked(object sender, RoutedEventArgs e)
25 if (sender.Equals(this) == false) return;
27 this._CheckedValues.Remove((e.OriginalSource as CheckBox).Tag.ToType<int>());
至此,剩下的问题就只有解决为CheckBox控件赋值的问题了。
1 public void SetIsChecked(int index, bool value)
5 (this.GetControl("chkCheckBox", this.ItemValuePath, index) as CheckBox).IsChecked = value;