WPF 基础 - 在模板中找元素
阅读原文时间:2023年07月09日阅读:2

1. 在 ControlTemplate 中寻找元素

<Window.Resources>
    <ControlTemplate x:Key="cTmp">
        <StackPanel>
            <StackPanel.Resources>
                <Style TargetType="TextBlock">
                    <Setter Property="Margin" Value="6"/>
                </Style>
            </StackPanel.Resources>
            <TextBlock x:Name="tb1" Text="tb1"/>
            <TextBlock x:Name="tb2" Text="tb2"/>
            <TextBlock x:Name="tb3" Text="tb3"/>
        </StackPanel>
    </ControlTemplate>
</Window.Resources>

<StackPanel>
    <UserControl x:Name="uc" Template="{StaticResource cTmp}" />
    <Button Click="Button_Click" Content="FindName" Width="90" Height="25"/>
</StackPanel>

private void Button_Click(object sender, RoutedEventArgs e)
{
    TextBlock tb = this.uc.Template.FindName("tb1", this.uc) as TextBlock;
    tb.Text = "I Find the first one";

    StackPanel sp = tb.Parent as StackPanel;
    (sp.Children[1] as TextBlock).Text = "I am the second one";
    (sp.Children[2] as TextBlock).Text = "I am the third one";
}

this.xx.Template.FindName((string)elementName, this.xx) as typeof(elementType);

2. 在 DateTemplate 中寻找元素

类似于 ControlTemplate,可以使用

this.xx.ContentTemplate.FindName(string(elementName), this.xx) as typeof(elementType) 寻找元素;

但我们一般是为了获取控件的长度、宽度,而不是为了获取内容的某个属性;

要获取内容的某个属性,不必绕到表层控件上来。

<Window.Resources>
    <coll:ArrayList x:Key="stuList">
        <local:Student Id="1" Name="Mao1" LoveBasketball="True"></local:Student>
        <local:Student Id="2" Name="Mao2" LoveBasketball="False"></local:Student>
        <local:Student Id="3" Name="Mao3" LoveBasketball="True"></local:Student>
        <local:Student Id="4" Name="Mao4" LoveBasketball="False"></local:Student>
        <local:Student Id="5" Name="Mao5" LoveBasketball="True"></local:Student>
        <local:Student Id="6" Name="Mao6" LoveBasketball="False"></local:Student>
    </coll:ArrayList> 

    <DataTemplate x:Key="dtId">
        <TextBlock Text="{Binding Id}"/>
    </DataTemplate>

    <DataTemplate x:Key="dtName">
        <TextBox x:Name="textBoxName" Text="{Binding Name}" GotFocus="TextBoxName_GotFocus" />
    </DataTemplate>

    <DataTemplate x:Key="dtLoveBasketball">
        <CheckBox x:Name="checkBoxLove" IsChecked="{Binding LoveBasketball}" IsEnabled="False"/>
    </DataTemplate>

</Window.Resources>

<StackPanel MenuItem.Click="StackPanel_Click">
    <ListView x:Name="list_stu" ItemsSource="{StaticResource stuList}">
        <ListView.View>
            <GridView x:Name="ddr">
                <GridViewColumn Header="Id" CellTemplate="{Binding Source={StaticResource dtId}}"></GridViewColumn>
                <GridViewColumn x:Name="dd" Header="Name" CellTemplate="{Binding Source={StaticResource dtName}}"></GridViewColumn>
                <GridViewColumn Header="LoveBasketball" CellTemplate="{Binding Source={StaticResource dtLoveBasketball}}"></GridViewColumn>
            </GridView>
        </ListView.View>
    </ListView>
</StackPanel>


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; // 获取业务逻辑数据,[DataTemplate 落实在 ContentPresenter 上,ContentPresenter 是 ControlTemplate 的一个结点]
    this.list_stu.SelectedItem = stu; // 设置 ListView 的选中项

    ListViewItem lvi = this.list_stu.ItemContainerGenerator.ContainerFromItem(stu) as ListViewItem;
    CheckBox cb = this.FindVisualChild<CheckBox>(lvi);

    Console.WriteLine(cb.Name);
}

private ChildType FindVisualChild<ChildType>(DependencyObject obj) where ChildType : DependencyObject
{
    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;
}
  • 当使用 GridView 作为 ListView 的 View 属性值,如果某一列使用 TextBox 作为 CellTemplate,那么即使这列中的 TextBox 被鼠标单击并获得焦点,ListView 也不会把此项作为自己的 SelectedItem;
  • 在 ListBox 中 ItemTemplate 下的 TextBox 也是如此;
  • 因此 TextBoxName_GotFocus 先获取事件的最初源头,然后沿着 UI 元素树上溯到 DataTemplate 的目标控件(ContentPresenter)并获取它的内容,必是一个 Student 实例;
  • 使用 ItemsControl 的 ItemContainerGenerator.ContainerFromItem 方法获得包装这指定条目数据的容器 ListViewItem;
  • ListViewItem 的 Content 也是 Student 实例。

前面 ListViewItem 的可视化树

graph LR
Border1-->ListViewItem
Border2-->Border1
Grid-->Border2
Rectangle-->Grid
GridViewRowPresenter-->Grid

ContentPresenter1-->GridViewRowPresenter
TextBlock-->ContentPresenter1

ContentPresenter2-->GridViewRowPresenter
TextBox-->ContentPresenter2

ContentPresenter3-->GridViewRowPresenter
CheckBox-->ContentPresenter3

graph LR
Border3-->TextBox
ScrollViewer-->Border3
Grid2-->ScrollViewer
Rectangle2-->Grid2
ScrollContentPresenter-->Grid2
TextBoxView-->ScrollContentPresenter
TextBoxLineDrawingVisual-->TextBoxView
AdornerLayer-->ScrollContentPresenter
ScrollBar-->Grid2
ScrollBar2-->Grid2

<StackPanel MenuItem.Click="StackPanel_Click">
    <StackPanel.Resources>
        <DataTemplate x:Key="it">
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Id}"/>
                <TextBox x:Name="textBoxName" Text="{Binding Name}" GotFocus="TextBoxName_GotFocus" Margin="15 0"/>
                <CheckBox x:Name="checkBoxLove" IsChecked="{Binding LoveBasketball}"/>
            </StackPanel>
        </DataTemplate>

        <Style TargetType="ListBoxItem">
            <Setter Property="ContentTemplate" Value="{StaticResource it}"/>
            <Style.Triggers>
                <MultiDataTrigger>
                    <MultiDataTrigger.Conditions>
                        <Condition Binding="{Binding Id}" Value="3"/>
                        <Condition Binding="{Binding Name}" Value="Mao3"/>
                    </MultiDataTrigger.Conditions>
                    <Setter Property="Background" Value="Red"></Setter>
                </MultiDataTrigger>
            </Style.Triggers>
        </Style>
    </StackPanel.Resources>

    <ListBox x:Name="list_stu2" ItemsSource="{StaticResource stuList}" >
    </ListBox>
</StackPanel>

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器