模板遗留问题
五、寻找失落的控件
在寻找之前我们要先了解一些知识。在ControlTemplate里面有个Target,明确指出了它的树根。那么DataTemplate里面通常有很多控件,他的根目标是不是也是具有DataTemplate的控件呢?总的来说是,但实际是一个ContentPresenter或者ItemsPresenter。我们利用WPF Inspector工具来查看一下我们第一个使用DataTemplate例子中使用可视树,查询结果如图11:
图11
这个给我们了很大的方便,如果我们想找模板中的控件的话,我们就可以先找到这个类的控件,然后在去遍历此控件的子控件。由于WPF数据驱动的缘故,如果我们去获得逻辑数据的话,通常情况可以从绑定的源去取,不必去目标的属性去取。 如TextBox1的数据为源,TextBox2为目标,把第二个TextBox的值绑定在第一个TextBox上面,我们不需要去找TextBox2,可以直接找TextBox1的依赖性属性的数据的来源去取。但是如果要获取TextBox的Width还还必须要从TextBox去取的。好了下面就看一下怎么查找DataTemplate和ControlTemplate里面的控件。先看一个ControlTemplate的例子:
<Window x:Class="Template.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="200" Width="300"> <Window.Resources> <ControlTemplate x:Key="cTmp"> <StackPanel Background="Orange"> <TextBox x:Name="textBox1" Margin="6"></TextBox> <TextBox x:Name="textBox2" Margin="6"></TextBox> <TextBox x:Name="textBox3" Margin="6"></TextBox> </StackPanel> </ControlTemplate> </Window.Resources> <StackPanel> <UserControl x:Name="uc" Template="{StaticResource cTmp}" Margin="5"/> <Button Content="找控件" Width="120" Height="30" Click="Button_Click"/> </StackPanel> </Window>
其对应的可视树为图12;
图12
我们可以通过目标控件的 TextBox tb = this.uc.Template.FindName("textBox1", this.uc) as TextBox;找到其中的一个TextBox控件,然后找其父类控件,最后再找其他的子控件。下面给出button按钮单击事件的代码:
private void Button_Click(object sender, RoutedEventArgs e)
{
TextBox tb = this.uc.Template.FindName("textBox1", this.uc) as TextBox;
tb.Text = "hello wpf";
StackPanel sp = tb.Parent as StackPanel;
(sp.Children[1] as TextBox).Text = "111";
(sp.Children[2] as TextBox).Text = "222";
}
下面接着演示一个DataTemplate里面的控件。给出一个ListView控件里面放的有GridView,在GridView里面通过CellTemplate(里面嵌套的是TextBox或者是其他控件)来绑定网格的内容。当点击TextBox时,获取相同行的其他控件的值。下面先给出代码,然后用工具给出可视树的图,然后用代码获取:
<Window x:Class="WpfControlTemlate.FindControls" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:c="clr-namespace:System.Collections;assembly=mscorlib" xmlns:local="clr-namespace:WpfControlTemlate" Title="FindControls" Height="300" Width="300" Background="Orange"> <Window.Resources> <c:ArrayList x:Key="stuList"> <local:Student ID="1" Name="haiziguo" Skill="WPF" HasJob="False"/> <local:Student ID="2" Name="shanjige" Skill="C++" HasJob="True"/> <local:Student ID="3" Name="xiaozhang" Skill="C#" HasJob="False"/> <local:Student ID="3" Name="chenwenzi" Skill="Test" HasJob="True"/> </c:ArrayList> <DataTemplate x:Key="nameDT"> <TextBox x:Name="textBoxName" Text="{Binding Name}" GotFocus="textBoxName_GotFocus"/> </DataTemplate> <DataTemplate x:Key="skillDT"> <TextBox x:Name="textBoxSkill" Text="{Binding Skill}"/> </DataTemplate> <DataTemplate x:Key="hjDT"> <CheckBox x:Name="checkBoxJob" IsChecked="{Binding HasJob}"/> </DataTemplate> </Window.Resources> <Grid Margin="5"> <ListView x:Name="listViewStudent" ItemsSource="{StaticResource stuList}"> <ListView.View> <GridView> <GridViewColumn Header="ID" DisplayMemberBinding="{Binding ID}"/> <GridViewColumn Header="姓名" CellTemplate="{StaticResource nameDT}"/> <GridViewColumn Header="技术" CellTemplate="{StaticResource skillDT}"/> <GridViewColumn Header="已工作" CellTemplate="{StaticResource hjDT}"/> </GridView> </ListView.View> </ListView> </Grid> </Window>
可视树为图13:
图13
后台代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; namespace WpfControlTemlate { /// <summary> /// FindControls.xaml 的交互逻辑 /// </summary> public partial class FindControls : Window { public FindControls() { InitializeComponent(); } private void textBoxName_GotFocus(object sender, RoutedEventArgs e) { TextBox tb = e.OriginalSource as TextBox; ContentPresenter cp = tb.TemplatedParent as ContentPresenter; Student stu = cp.Content as Student; this.listViewStudent.SelectedItem = stu; ListViewItem lvi = this.listViewStudent.ItemContainerGenerator.ContainerFromItem(stu) as ListViewItem; CheckBox chb = this.FindVisualChild<CheckBox>(lvi); MessageBox.Show(chb.IsChecked.ToString()); } //下面是一个递归算法 private ChildType FindVisualChild<ChildType>(DependencyObject obj) where ChildType : DependencyObject //关于where可以参考 http://technet.microsoft.com/zh-cn/bb384067.aspx { for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) { DependencyObject child = VisualTreeHelper.GetChild(obj, i); if (child != null && child is ChildType) return child as ChildType; else { ChildType childOfChild = FindVisualChild<ChildType>(child); if (childOfChild != null) return childOfChild; } } return null; } } #region Student——学生类 public class Student { public string Name { get; set; } public int ID { get; set; } public string Skill { get; set; } public bool HasJob{ get; set; } } #endregion }
有个不理解的是ContentPresenter的Content