WPF中如何查找数据模板中的子元素2013-08-10前两天在写一个WPF程序时,突然发现DataGrid控件没有筛选功能,但我不急,因为我知道,WPF的好处在于UI与逻辑的分离,要给数据网格控件加上输入筛选功能并不复杂,也不用去找第六方控件了,直接从DataGrid类派生一个类,并重定义它的控件模板,在DataGrid原有的控件模板上加一个StackPanel就可以了,方向为水平排列,放在列标头的下方。然后在代码中根据各个列的情况,向那个StackPanel添加N个文本框就可以输入筛选信息了。呵呵,虽不算完美,解决燃眉之急还是可以的。这样我就想到另一个问题,如何修改DataTemplate里面的元素呢。或者说如何获取指定的元素。于是,我在心里产生了两个方案:1、在定义DataTemplate时,比如我里面用了一个TextBlock控件,我给它命名为tbText,然后我就循环访问集合控件的各个项,并得到每个项容器的ContentTemplate对象,希望用FindName方法直接把TextBlock取出来,但发生异常,这种方法不行。注意,如果DataTemplate定义在资源中,不能直接修改资源中的内容,因为这一改,会导致所有项都变了,因为集合控件(如ListBox)中每个项引用的都是同一个资源,而我现在要的是每个项里面的TextBlock的前景色都不一样。2、我想到VisualTreeHelper类,对,就直接从项容器(ListBoxItem)的层次入手,一层一层往下找,直到找到TextBlock为止,由于我的模板中只有一个TextBlock,我就不必管它叫什么名字,只要是TextBlock类型就可以。但因为里面元素层次较多,不可能第一轮就能找到TextBlock,于是我写了一个递归方法,把元素的所有子元素都翻了个遍。
// 已修正private void FindChildByType(DependencyObject relate, Type type, ref FrameworkElement resElement){for (int i = 0; i < VisualTreeHelper.GetChildrenCount(relate); i++){var el = VisualTreeHelper.GetChild(relate, i) as FrameworkElement;if (el.GetType() == type){resElement = el;return;}else{FindChildByType(el, type, ref resElement);}}}
嘿嘿,这方法还果然骤效,为了使每个项的文本颜色不同,我使用了用随机数来创建颜色。
private Color BuildColor(){Array.Clear(colorBs, 0, colorBs.Length);rand.NextBytes(colorBs);return Color.FromRgb(colorBs[0], colorBs[1], colorBs[2]);}
好了,现在可以动手去改变DataTemplate中的元素的属性了。/* 已修正 */
private void Button_Click_1(object sender, RoutedEventArgs e){foreach (var item in lb.Items){var el = lb.ItemContainerGenerator.ContainerFromItem(item) as FrameworkElement;if (el != null && el is ListBoxItem){ListBoxItem lbItem = el as ListBoxItem;FrameworkElement efind = default(FrameworkElement);FindChildByType(lbItem, typeof(TextBlock), ref efind);if (efind is TextBlock){TextBlock textblock = efind as TextBlock;textblock.Foreground = new SolidColorBrush(BuildColor());}}}}
把每个项的TextBlock找出来,修改它的Foreground属性。我们可以在找到TextBlock后的代码上下一个断点,然后调试运行。代码执行到断点处停下,把鼠标移到TextBlock变量上,点击名字右边的放大镜图标,从弹出的菜单中选择【WPF Tree Visualizer】。如下图所示。