• 10-1.WPF模板


    10-1.WPF模板

    控件由“算法内容”和“数据内容”决定

    • 算法内容:指控件能展示哪些数据、具有哪些方法、能激发什么事件等,简而言之是控件的功能,一组相关逻辑
    • 数据内容:控件所展示的具体数据是什么

    在WPF中,模板将数据和算法的内容和形式进行了解耦,模板分为两大类:

    • ControlTemplate:是算法内容的表现形式,决定了控件长什么样
    • DataTemplate:是数据内容的表现形式,也就是数据显示成什么样

    DataTemplate

    DataTemplate常用的地方有3处:

    1. ContentControl的ContentTemplate属性,相当于给ContentControl的内容穿上外衣
    2. ItemsControl的ItemTemplate属性,相当于给ItemTemplate的内容穿上外衣
    3. GridViewColumn的CellTemplate属性,相当于给GridViewColumn单元格里的数据穿上外衣

    传统的事件驱动模式是控件和控件之间沟通,模型如下图

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b3BiGY74-1668484396161)(10.WPF模板.assets/image-20221114100004170.png)]

    数据驱动则是数据与控件之间沟通,使用DataTemplate可以很方便的将事件驱动模式改为数据驱动模式

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W7GIc1Qv-1668484396162)(10.WPF模板.assets/image-20221114100340111.png)]

    案例:有一列汽车的数据显示在ListBox里面,点击某条数据,详细数据显示在窗体左侧细节展示框中。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f1B2cC1L-1668484396162)(10.WPF模板.assets/image-20221114100825704.png)]

    将ListBox和细节展示框的窗体设计分别放进标签内包装,再放到主窗体的资源词典中。最重要的是为里的每个控件设置Binding,告诉各个控件应该关注数据的哪个属性。除此之外,有些属性不能直接使用,需要自定义Converter,在XAML中有两种方法使用Converter:

    • 把Converter以资源的形式放在资源词典里
    • 为Converter准备一个静态属性,在XAML中使用{x:Static}标签扩展来访问

    定义Converter

    //厂商名称转换为Logo路径
    public class AutoMarkToLogoPathConverter:IValueConverter
    {
        /// 正向转
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return new BitmapImage(new Uri(string.Format(@"Resource/Image/{0}.png",(string)value),UriKind.Relative));
        }
        /// 逆向转未用到
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
    //汽车名称转换为照片路径
    public class NameToPhotoPathConverter:IValueConverter
    {
        /// 正向转
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return new BitmapImage(new Uri(string.Format(@"Resource/Image/{0}.jpg", (string)value), UriKind.Relative));
        }
        /// 逆向转未用到
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    XAML代码如下:

    <Window x:Class="WpfApplication1.Window36"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:WpfApplication1.Model"
            Title="Window36" Height="350" Width="623">
        <Window.Resources>
            
            <local:AutoMarkToLogoPathConverter x:Key="amp"/>
            <local:NameToPhotoPathConverter x:Key="npp"/>
            
            <DataTemplate x:Key="DatialViewTemplate">
                <Border BorderBrush="Black" BorderThickness="1" CornerRadius="6">
                    <StackPanel>
                        <Image x:Name="imgPhoto" Width="400" Height="250" Source="{Binding AutoMark,Converter={StaticResource npp}}">Image>
                        <StackPanel Orientation="Horizontal" Margin="5,0">
                            <TextBlock Text="Name:" FontSize="20" FontWeight="Bold">TextBlock>
                            <TextBlock FontSize="20" Margin="5,0" Text="{Binding Name}">TextBlock>
                        StackPanel>
                        <StackPanel Orientation="Horizontal" Margin="5,0">
                            <TextBlock Text="AutoMark:" FontWeight="Bold">TextBlock>
                            <TextBlock  Margin="5,0" Text="{Binding AutoMark}">TextBlock>
                            <TextBlock Text="Year:" FontWeight="Bold">
    
                            TextBlock>
                            <TextBlock Text="{Binding Year}" Margin="5,0">
    
                            TextBlock>
                            <TextBlock Text="Top Speed:" FontWeight="Bold">
    
                            TextBlock>
                            <TextBlock Text="{Binding TopSpeed}" Margin="5,0">
    
                            TextBlock>
                        StackPanel>
                    StackPanel>
                Border>
            DataTemplate>
            
            <DataTemplate x:Key="ItemView">
                <Grid Margin="2">
                    <StackPanel Orientation="Horizontal">
                        <Image x:Name="igLogo" Grid.RowSpan="3" Width="64" Height="64" Source="{Binding Name,Converter={StaticResource amp}}">Image>
                        <StackPanel Margin="5,10">
                            <TextBlock Text="{Binding Name}" FontSize="16" FontWeight="Bold">TextBlock>
                            <TextBlock Text="{Binding Year}" FontSize="14">TextBlock>
                        StackPanel>
                    StackPanel>
                Grid>
            DataTemplate>
        Window.Resources>
       
        <StackPanel Orientation="Horizontal">
            <UserControl ContentTemplate="{StaticResource DatialViewTemplate}" Content="{Binding Path=SelectedItem,ElementName=lbInfos}">UserControl>
            <ListBox x:Name="lbInfos" ItemTemplate="{StaticResource ItemView}">ListBox>
        StackPanel>
    Window>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    初始化数据

    private void InitialCarList()
    {
        List<Car> infos = new List<Car>() { 
        new Car(){ AutoMark="Aodi", Name="Aodi", TopSpeed="200", Year="1990"},
        new Car(){ AutoMark="Aodi", Name="Aodi", TopSpeed="250", Year="1998"},
        new Car(){ AutoMark="Aodi", Name="Aodi", TopSpeed="300", Year="2002"},
        new Car(){ AutoMark="Aodi", Name="Aodi", TopSpeed="350", Year="2011"},
        new Car(){ AutoMark="Aodi", Name="Aodi", TopSpeed="500", Year="2020"}
        };
        this.lbInfos.ItemsSource = infos;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    ControlTemplate

    ControlTemplae可以控制控件的外表样式,在实际项目中ControlTemplate主要用来:

    1. 通过更换ControlTemplate来改变控件外观
    2. 借助ControlTemplate,程序员和UI设计可以并行工作,程序员可以使用WPF标准控件来编程,等设计师工作完成后,只需把新的ControlTemplate应用到程序就可以

    在Blend中的Application资源词典中增加一个TextBoxStyle

    <Application.Resources>
            <Style x:Key="TextBoxStyle" TargetType="{x:Type TextBox}">
                "Template">
                    
                        "{x:Type TextBox}">
                            x:Name="border" 
                                    BorderBrush="{TemplateBinding BorderBrush}" 
                                    BorderThickness="{TemplateBinding BorderThickness}" 
                                    Background="{TemplateBinding Background}" 
                                    CornerRadius="10">
                                x:Name="PART_ContentHost"/>
                            
                        
                    
                
            Style>
    Application.Resources>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    注意点:

    1. 作为资源不是单纯的ControlTemplate,而是Style。使用style时,如果value值比较简单,则直接使用Attribute就可以,如果比较复杂,则只能使用XAML的属性对象语法,如TextBox的Template属性是一个ControlTemplate对象。
    2. TemplateBinding,ControlTemplate被应用到一个控件上,我们称之为目标控件,TemplateBinding的意思是将自己的属性关联到目标控件上的某个属性值上,如Background="{TemplateBinding Background}" 意思是让Border的Background与目标控件保持一致。

    TextBox应用上面的style

    <TextBox  Style="{DynamicResource TextBoxStyle}"/>
    <TextBox  Style="{DynamicResource TextBoxStyle}"/>
    
    • 1
    • 2

    其实每个控件本身就是一颗UI元素树,WPF可以看做两颗树,LogicalTree和VisualTree树,这两棵树的交点就是ControlTemplate。

    ItemsControl的PanelTemplate

    ItemsControl具有一个名为ItemsPanel的属性,其数据类型为ItemsPanelTemplate,它的作用是控制ItemsControl的条目容器。比如,ListBox条目都是纵向排列,现在改为横向排列。

    <Grid Margin="2">
        <ListBox>
            <TextBlock Text="A"/>
            <TextBlock Text="B"/>
            <TextBlock Text="C"/>
            <TextBlock Text="D"/>
            <TextBlock Text="E"/>
        ListBox>
    Grid>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v3Ft1iwg-1668484396163)(10.WPF模板.assets/image-20221114105432328.png)]

    <Grid Margin="2">
        <ListBox>
            <ListBox.ItemsPanel>
                <ItemsPanelTemplate>
                    <StackPanel Orientation="Horizontal"/>
                ItemsPanelTemplate>
            ListBox.ItemsPanel>
            <TextBlock Text="A"/>
            <TextBlock Text="B"/>
            <TextBlock Text="C"/>
            <TextBlock Text="D"/>
            <TextBlock Text="E"/>
        ListBox>
    Grid>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-15atntjv-1668484396163)(10.WPF模板.assets/image-20221114105501185.png)]

    DataTemplate和ControlTemplate的关系

    控件只是个数据和行为的载体,它的外观由ControlTemplate决定,数据外观由DataTemplate决定,他们正对应着Control类的Template和ContentTemplate两个属性。

    凡是Template都要作用在控件上,这个控件叫做Template的目标控件,也叫做模板化控件。DataTemplate同样也是,它施加在数据对象上,但是展示数据对象(一组展示数据的控件)也要有一个载体,该载体一般落实在一个ContentPresenter对象上,该对象只有一个ContentTemplate属性,表明它的功能就是承载由DataTemplate生成的一组控件。因为ContentPresenter是ControlTemplate控件树的一个节点,所以DataTemplate是ControlTemplate一颗子树。

    由Template生成的控件树都有根,每个控件都有个TemplateParent属性,如果值不为NULL,说明这个控件是由Template自动生成的,而属性值就是应用了该模板的控件。如果由Template生成的控件使用了TemplateBinding获取属性值,则TemplateBinding的数据源就是应用了这个模板的目标控件。
    在这里插入图片描述

    应用

    为Template设置应用目标有两种方法

    1. 逐个设置控件的Template/ContentTemplate/ItemsTemplate/CellTemplate等属性
    2. 整体应用,借助Style来实现,但是Style不能标记x:Key,如果不想应用则设置控件的Style为{x:Null}
    <Window x:Class="WpfApp5.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WpfApp5"
            mc:Ignorable="d"
            Title="MainWindow" Height="150" Width="300">
        <Window.Resources>
            <Style TargetType="{x:Type TextBox}">
                "Template">
                    
                        "{x:Type TextBox}">
                            "Orange"/>
                        
                    
                
                "Margin" Value="5"/>
                "BorderBrush" Value="Black"/>
                "Height" Value="25"/>
            Style>
        Window.Resources>
        <StackPanel>
            <TextBox/>
            <TextBox/>
            <TextBox Style="{x:Null}" Margin="5"/>
        StackPanel>
    Window>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LzBTYkZi-1668484396164)(10.WPF模板.assets/image-20221114195728350.png)]

    把DataTemplate应用在某个数据类型上的方法时设置DataTemplate的DataType属性,并且DataTemplate作为资源时不能带x:Key标记。

    <Window x:Class="WpfApp5.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:c="clr-namespace:System.Collections;assembly=mscorlib"
            xmlns:local="clr-namespace:WpfApp5"
            mc:Ignorable="d"
            Title="MainWindow" Height="200" Width="300">
        <Window.Resources>
            <DataTemplate DataType="{x:Type local:Unit}">
                <Grid>
                    <StackPanel Orientation="Horizontal">
                        <Grid>
                            <Rectangle Stroke="Yellow" Fill="Orange" Width="{Binding Price}"/>
                            <TextBlock Text="{Binding Year}"/>
                        Grid>
                    StackPanel>
                Grid>
            DataTemplate>
            <c:ArrayList x:Key="ds">
                <local:Unit Year="2001" Price="100"/>
                <local:Unit Year="2002" Price="120"/>
                <local:Unit Year="2003" Price="130"/>
                <local:Unit Year="2004" Price="150"/>
                <local:Unit Year="2005" Price="160"/>
                <local:Unit Year="2006" Price="180"/>
                <local:Unit Year="2007" Price="190"/>
            c:ArrayList>
        Window.Resources>
        <StackPanel>
            <ListBox ItemsSource="{StaticResource ds}"/>
            <ComboBox ItemsSource="{StaticResource ds}" Margin="5"/>
        StackPanel>
    Window>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    public class Unit
    {
        public int Price { set; get; }
        public string Year { set; get; }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MHTpxunK-1668484396164)(10.WPF模板.assets/image-20221115091306919.png)]

    XML数据源

    DataTemplate可以把XML数据中的元素名作为DataType,元素子节点和Attribute可以使用xpath来访问。

    上面的案例改造

     <Window.Resources>
         <DataTemplate DataType="Unit">
             <Grid>
                 <StackPanel Orientation="Horizontal">
                     <Grid>
                         <Rectangle Stroke="Yellow" Fill="Orange" Width="{Binding XPath=@Price}"/>
                         <TextBlock Text="{Binding XPath=@Year}"/>
                     Grid>
                 StackPanel>
             Grid>
         DataTemplate>
         <XmlDataProvider x:Key="ds" XPath="Units/Unit">
             <x:XData>
                 <Units xmlns="">
                     <Unit Year ="2001" Price="100"/>
                     <Unit Year ="2002" Price="110"/>
                     <Unit Year ="2003" Price="120"/>
                     <Unit Year ="2004" Price="130"/>
                     <Unit Year ="2005" Price="140"/>
                     <Unit Year ="2006" Price="150"/>
                 Units>
             x:XData>
         XmlDataProvider>
     Window.Resources>
     <StackPanel>
         <ListBox ItemsSource="{Binding Source={StaticResource ds}}"/>
         <ComboBox ItemsSource="{Binding Source={StaticResource ds}}" Margin="5"/>
     StackPanel>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    层级结构

    WPF中TreeView和MenuItem控件用来显示层级,能够帮助层级控件显示层级数据的模板是HierarchicalDataTemplate

    案例1:Data.xml文件

     
    <Data xmlns="">
      <Grade Name="一年级">
        <Class Name="甲班">
          <Group Name="A组"/>
          <Group Name="B组"/>
          <Group Name="C组"/>
        Class>
      <Class Name="乙班">
          <Group Name="A组"/>
          <Group Name="B组"/>
          <Group Name="C组"/>
        Class>
      Grade>
      <Grade Name="二年级">
        <Class Name="甲班">
          <Group Name="A组"/>
          <Group Name="B组"/>
          <Group Name="C组"/>
        Class>
      <Class Name="乙班">
          <Group Name="A组"/>
          <Group Name="B组"/>
          <Group Name="C组"/>
        Class>
      Grade>
    Data>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    程序的XAML:

    <Window x:Class="WpfApp5.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:c="clr-namespace:System.Collections;assembly=mscorlib"
            xmlns:local="clr-namespace:WpfApp5"
            mc:Ignorable="d"
            Title="MainWindow" Height="200" Width="300">
        <Window.Resources>
            <XmlDataProvider x:Key="ds" Source="Data.xml" XPath="Data/Grade"/>
            
            <HierarchicalDataTemplate DataType="Grade" ItemsSource="{Binding XPath=Class}">
                <TextBlock Text="{Binding XPath=@Name}"/>
            HierarchicalDataTemplate>
            
            <HierarchicalDataTemplate DataType="Class" ItemsSource="{Binding XPath=Group}">
                <RadioButton Content="{Binding XPath=@Name}" GroupName="gn"/>
            HierarchicalDataTemplate>
            
            <HierarchicalDataTemplate DataType="Group" ItemsSource="{Binding XPath=Student}">
                <CheckBox Content="{Binding XPath=@Name}" />
            HierarchicalDataTemplate>
        Window.Resources>
        <Grid>
            <TreeView Margin="5" ItemsSource="{Binding Source={StaticResource ds}}"/>
        Grid>
    Window>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    在这里插入图片描述
    DataType指定了HierarchicalDataTemplate模板用哪种数据类型
    ItemsSource指定下一层显示哪些数据

    案例2:同一种数据类型的嵌套,只需设置一个HierarchicalDataTemplate,他会自动迭代

    data.xml

    
    <Data xmlns="">
      <Operation Name="文件" Gesture="F">
        <Operation Name="新建" Gesture="N">
          <Operation Name="项目" Gesture="N"/>
          <Operation Name="网站" Gesture="N"/>
          <Operation Name="文档" Gesture="N"/>
        Operation>
        <Operation Name="保存" Gesture="N"/>
        <Operation Name="打印" Gesture="N"/>
        <Operation Name="退出" Gesture="N"/>
      Operation>
      <Operation Name="编辑" Gesture="E">
        <Operation Name="拷贝" Gesture="N"/>
        <Operation Name="剪切" Gesture="N"/>
        <Operation Name="粘贴" Gesture="N"/>
      Operation>
    Data>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    XAML:

    <Window x:Class="WpfApp5.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:c="clr-namespace:System.Collections;assembly=mscorlib"
            xmlns:local="clr-namespace:WpfApp5"
            mc:Ignorable="d"
            Title="MainWindow" Height="200" Width="300">
        <Window.Resources>
            <XmlDataProvider x:Key="ds" Source="Data1.xml" XPath="Data/Operation"/>
            <HierarchicalDataTemplate DataType="Operation" ItemsSource="{Binding XPath=Operation}">
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="{Binding XPath=@Name}" Margin="10,0"/>
                    <TextBlock Text="{Binding XPath=@Gesture}"/>
                StackPanel>
            HierarchicalDataTemplate>
        Window.Resources>
        <StackPanel>
            <Menu ItemsSource="{Binding Source={StaticResource ds}}"/>
        StackPanel>
    Window>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    在这里插入图片描述

    HierarchicalDataTemplate的作用目标不是MenuItem的内容,而是它的Header。如果对MenuItem的单击事件进行侦听,可以从单击MenuItem的Header中提取XML数据。

    <StackPanel MenuItem.Click="StackPanel_Checked">
        <Menu ItemsSource="{Binding Source={StaticResource ds}}"/>
    StackPanel>
    
    • 1
    • 2
    • 3
    private void StackPanel_Checked(object sender, RoutedEventArgs e)
    {
        MenuItem mi = e.OriginalSource as MenuItem;
        XmlElement xe = mi.Header as XmlElement;
        MessageBox.Show(xe.Attributes["Name"].Value);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2R6lOGte-1668484396166)(10.WPF模板.assets/image-20221115100908738.png)]

    控件内部的树形结构

    如果UI元素树上有个x:Name="txt"的控件,而某个控件内部有个由Template生成的x:Name="txt"的控件,他们不会产生冲突,因为LogicTree不会看到控件内部的细节。由ControlTemplate和DataTemplate有一个FindName的方法供我们检索其中的内部控件,也就是说,只要我们拿到Template就能找到内部的控件。

    获取ControlTemplate对象

    想拿到ControlTemplate对象,直接访问目标对象的Template就可以。

    <Window x:Class="WpfApp5.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WpfApp5"
            mc:Ignorable="d"
            Title="MainWindow" Height="172" Width="300">
        <Window.Resources>
            <ControlTemplate x:Key="cTmp">
                <StackPanel Background="Orange">
                    <TextBox x:Name="txt1" Margin="6"/>
                    <TextBox x:Name="txt2" Margin="6"/>
                    <TextBox x:Name="txt3" Margin="6"/>
                StackPanel>
            ControlTemplate>
        Window.Resources>
        <StackPanel Background="Yellow">
            <UserControl x:Name="uc" Template="{StaticResource cTmp}" Margin="5"/>
            <Button Content="Find by Name" Width="120" Height="30" Click="Button_Click"/>
        StackPanel>
    Window>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    private void Button_Click(object sender, RoutedEventArgs e)
    {
        //通过目标对象的Template就可以获得ControlTemplate
        TextBox tb = this.uc.Template.FindName("txt1", this.uc) as TextBox;
        tb.Text = "Hello";
        StackPanel sp= tb.Parent as StackPanel;
        (sp.Children[1] as TextBox).Text = "Hi";
        (sp.Children[2] as TextBox).Text = "XXX";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n6E0WOXZ-1668514330794)(C:\Users\54302\Desktop\wpf教程#\使用LogicalTree控件.assets\image-20221115191011155.png)]

    获取DataTemplate对象

    如果找到DataTemplate生成的控件后,如果想获得与控件相关的数据,如长宽高,这种做法是正确的。而如果想获得数据,那一般是逻辑出现了问题,因为WPF采用数据驱动的方式,获取数据一般在底层就可以实现。

    <Window x:Class="WpfApp5.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WpfApp5"
            mc:Ignorable="d"
            Title="MainWindow" Height="175" Width="220">
        <Window.Resources>
            
            <local:Student x:Key="stu" Id="1" Name="Tom" Skill="WPF" HasJob="True"/>
            
            <DataTemplate x:Key="stuDT">
                <Border BorderBrush="Orange" BorderThickness="2" CornerRadius="5">
                    <StackPanel>
                        <TextBlock Text="{Binding Id}" Margin="5"/>
                        <TextBlock x:Name="txtName" Text="{Binding Name}" Margin="5"/>
                        <TextBlock Text="{Binding Skill}" Margin="5"/>
                    StackPanel>
                Border>
            DataTemplate>
        Window.Resources>
        <StackPanel Background="Yellow">
            <ContentPresenter x:Name="cp" Content="{StaticResource stu}" ContentTemplate="{StaticResource stuDT}" Margin="5"/>
            <Button Content="Find" Margin="5" Click="Button_Click_1"/>
        StackPanel>
    Window>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    public class Student
    {
        public int Id { set; get; }
        public string Name { set; get; }
        public string Skill { get; set; }
        public bool HasJob { set; get; }
    }
    private void Button_Click_1(object sender, RoutedEventArgs e)
    {
        TextBlock tb = this.cp.ContentTemplate.FindName("txtName", this.cp) as TextBlock;
        MessageBox.Show(tb.Text);
    
        //如果只是使用数据,最好这样
        //Student st = this.cp.Content as Student;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SzQAqwXy-1668514330795)(C:\Users\54302\Desktop\wpf教程#\使用LogicalTree控件.assets\image-20221115192756579.png)][外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-azTZJfQs-1668514330796)(C:\Users\54302\Desktop\wpf教程#\使用LogicalTree控件.assets\image-20221115192806347.png)]

    GridView使用Template

    DataTemplate的常用之处是GridViewColumn的CellTemplate属性。GridViewColumn默认的CellTemplate属性使用了一个TextBlock,如果想使用CheckBox呢?

    案例:

    <Window x:Class="WpfApp5.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:c="clr-namespace:System.Collections;assembly=mscorlib"
            xmlns:local="clr-namespace:WpfApp5"
            mc:Ignorable="d"
            Title="MainWindow" Height="175" Width="220">
        <Window.Resources>
            
            <c:ArrayList x:Key="stuList">
                <local:Student Id="1" Name="A" Skill="C" HasJob="True"/>
                <local:Student Id="2" Name="B" Skill="C++" HasJob="True"/>
                <local:Student Id="3" Name="C" Skill="C#" HasJob="True"/>
                <local:Student Id="4" Name="D" Skill="jAVA" HasJob="False"/>
                <local:Student Id="5" Name="E" Skill="PYTHON" HasJob="True"/>
                <local:Student Id="6" Name="F" Skill="SQL" HasJob="True"/>
            c:ArrayList>
            
            <DataTemplate x:Key="nameDT">
                <TextBox x:Name="txtName" Text="{Binding Name}"/>
            DataTemplate>
            <DataTemplate x:Key="skillDT">
                <TextBox x:Name="txtSkill" GotFocus="TxtSkill_GotFocus" Text="{Binding Skill}"/>
            DataTemplate>
            <DataTemplate x:Key="hjDT">
                <CheckBox x:Name="chkJob" IsChecked="{Binding HasJob}"/>
            DataTemplate>
        Window.Resources>
        <Grid Margin="5">
            <ListView x:Name="listView" 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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    private void TxtSkill_GotFocus(object sender, RoutedEventArgs e)
    {
        //访问数据
        TextBox tb = e.OriginalSource as TextBox;//事件发起源头
        ContentPresenter cp = tb.TemplatedParent as ContentPresenter;//获取模板
        Student su = cp.Content as Student;//获取业务数据
        this.listView.SelectedItem = su;
    
        //访问界面元素
        ListViewItem vi = this.listView.ItemContainerGenerator.ContainerFromItem(su) as ListViewItem;
        CheckBox crb = FindVisualChild<CheckBox>(vi);
        MessageBox.Show(crb.Name);
    }
    private T FindVisualChild<T>(DependencyObject obj) where T:DependencyObject
    {
        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
        {
            DependencyObject child = VisualTreeHelper.GetChild(obj, i);
            if (child!=null && child is T)
            {
                return child as T;
            }
            else
            {
                T childOfChild = FindVisualChild<T>(child);
                if (childOfChild !=null)
                {
                    return childOfChild;
                }
            }
        }
        return null;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zkcX0HoD-1668514330797)(C:\Users\54302\Desktop\wpf教程#\使用LogicalTree控件.assets\image-20221115195825110.png)][外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1Gi12oWI-1668514330798)(C:\Users\54302\Desktop\wpf教程#\使用LogicalTree控件.assets\image-20221115195834126.png)]

    • 案例中为模板中显示姓名的TextBox增加了GetFocus事件,界面上任何一个姓名TextBox获得焦点后都会调用该事件。

    • 每个ItemsControl派生类(ListBox、ListView)都有自己的条目容器,使用ItemContainerGenerator.ContainerFromItem方法能获得包装着指定条目数据的容器。

    • VisualTreeHelper类可以遍历控件内部的各个节点。

  • 相关阅读:
    标准I/O和系统I/O的本质区别
    SAP入门到放弃系列之QM物料主数据&检验类型设置
    Linux Shell入门常用命令使用
    kafka消息的序列化与反序列化
    【Oculus Interaction SDK】(十)在 VR 中使用手势识别
    nlp入门(三)基于贝叶斯算法的拼写错误检测器
    如何针对数据中心进行安全疏散和消防应急管理
    Ubuntu部署nginx
    HOperatorSet.Connection 有内存泄漏或缓存
    cnpm安装步骤
  • 原文地址:https://blog.csdn.net/weixin_44064908/article/details/127863288