现在的位置: 首页 > 综合 > 正文

(WPF)查找数据模板中的子元素

2012年05月05日 ⁄ 综合 ⁄ 共 2049字 ⁄ 字号 评论关闭

说明:严重抱歉,文章发表后,发现文中贴的代码有问题,现在进行修改。

 

最近,常与火星人交流心得,侃谈代码的艺术、学用之道,总之一句话,谈笑有鸿儒,往来无白丁。

但毕竟与火星人沟通,由于两个星球的文明差异,会遇到一些波折。

前两天在写一个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】。如下图所示。

接着会弹出一个很好玩的窗口,在这个窗口中,你可以把WPF控件的结构看得清清楚楚,还可以看到各元素的属性的实时值。

 

最后的结果。

 

怎么样,这个很实用吧。

 

抱歉!评论已关闭.