WPF 深入浅出 模板(DataTemplate 数据外衣、ControlTemplate 控件外衣、ItemsPanelTemplate 项布局

一、模板内函

模板就是“具有一定规格的样板”,有了模板,就可以依据它制造出很多一样的实例。

模板分为三大类:

DataTemplate 数据外衣

ControlTemplate 控件外衣

ItemsPanelTemplate 项布局(如:ListBox的item)


<Application.Resources>

<Style>

<Setter Property="Template">

<Setter.Value>

<ControlTemplate></ControlTemplate>

</Setter.Value>

</Setter>

</Style>

<DataTemplate></DataTemplate>

</Application.Resources>

二、DataTemplate 数据外衣(使用数据驱动)

效果:

代码:

    public class Car
    {
        public string Automake { get; set; }
        public string Name { get; set; }
        public string Year { get; set; }
        public string TopSpeed { get; set; }
    }

    //厂商名称转换成Logo图片路径
    public class AutomakerTologoPathConverter:IValueConverter
    {

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            string uriStr = string.Format(@"/Resources/Images/{0}.jpg", (string)value);
            return new BitmapImage(new Uri(uriStr, UriKind.Relative));
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

窗体代码:

<Window x:Class="WpfApplication.Window15"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication"
        Title="Window15" Height="400" Width="700">
    
    <Window.Resources>
        <local:AutomakerTologoPathConverter x:Key="n2p"/>
        <!--详细页外衣-->
        <DataTemplate x:Key="carDetailViewTemplate">
            <Border BorderBrush="Black" BorderThickness="1" CornerRadius="6">
                <StackPanel Margin="5">
                    <Image Width="400" Height="250"
                           Source="{Binding Automake,Converter={StaticResource n2p}}"/>
                    <StackPanel Orientation="Horizontal" Margin="5,0">
                        <TextBlock Text="Name:" FontWeight="Bold" FontSize="20"/>
                        <TextBlock Text="{Binding Name}" FontSize="20" Margin="5,0"/>
                    </StackPanel>
                    <StackPanel Orientation="Horizontal" Margin="5,0">
                        <TextBlock Text="Automake:" FontWeight="Bold"/>
                        <TextBlock Text="{Binding Automaker}" Margin="5,0"/>
                        <TextBlock Text="Year:" FontWeight="Bold" />
                        <TextBlock Text="{Binding Year}" Margin="5,0"/>
                        <TextBlock Text="TopSpeed:" FontWeight="Bold" />
                        <TextBlock Text="{Binding TopSeed}" Margin="5,0"/>
                    </StackPanel>
                </StackPanel>
            </Border>
        </DataTemplate>
        <!--列表外衣-->
       <DataTemplate x:Key="carListItemViewTemplate">
            <Grid Margin="2">
                <StackPanel Orientation="Horizontal">
                    <Image Source="{Binding Automake, Converter={StaticResource n2p}}" Grid.RowSpan="3" Width="64" Height="64"/>
                    <StackPanel Margin="5,0">
                        <TextBlock Text="{Binding Name}" FontSize="16" FontWeight="Bold"/>
                        <TextBlock Text="{Binding Year}" FontSize="14"/>
                    </StackPanel>
                </StackPanel>
            </Grid>
        </DataTemplate>
    </Window.Resources>
    
    <!--窗体内容-->
    <StackPanel Orientation="Horizontal" Margin="5">
        <UserControl ContentTemplate="{StaticResource carDetailViewTemplate}"
                     Content="{Binding SelectedItem,ElementName=listboxCars}"/>
        
        <ListBox x:Name="listboxCars" Width="180" Margin="5,0" 
                 ItemTemplate="{StaticResource carListItemViewTemplate}"/>
    </StackPanel>
</Window>

ContentTemplate="{StaticResource carDetailViewTemplate}"   给用户控件添加外衣

ItemTemplate="{StaticResource carListItemViewTemplate}"     给ListBox每个Item项添加外衣



    public partial class Window15 : Window
    {
        public Window15()
        {
            InitializeComponent();
            InitialCarList();
        }

        private void InitialCarList()
        {
            List<Car> carList = new List<Car>()
            {
               new Car(){Automake="4",Name="name1",Year="1999",TopSpeed="300"},
               new Car(){Automake="5",Name="name2",Year="1990",TopSpeed="150"},
               new Car(){Automake="4",Name="name3",Year="1991",TopSpeed="350"},
               new Car(){Automake="5",Name="name4",Year="1995",TopSpeed="400"},
            };
            listboxCars.ItemsSource = carList;
        }
    }


三、ControlTemplate 控件外衣

    <Application.Resources>
        <Style x:Key="键名" BasedOn="{x:Null}" TargetType="{x:Type TextBox}">
              <!---属性简单直接value,属性复杂就Setter.Value->
                 <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
                 <Setter Property="Template">
                                    <Setter.Value>
                                                         <ControlTemplate TargetType="{x:Type TextBox}">
                                                                   外衣
                                                                  <ControlTemplate.Triggers>
                                                                        <Trigger>
                                                                           触发器
                                                                        </Trigger>
                                                                </ControlTemplate.Triggers>
                                                       </ControlTemplate>
                                     </Setter.Value>
                </Setter>
        </Style>
       <Application.Resources>


实例:输入文本框,边圆角

第一步:控件模板

	<Application.Resources>
		<LinearGradientBrush x:Key="TextBoxBorder" EndPoint="0,20" MappingMode="Absolute" StartPoint="0,0">
			<GradientStop Color="#ABADB3" Offset="0.05"/>
			<GradientStop Color="#E2E3EA" Offset="0.07"/>
			<GradientStop Color="#E3E9EF" Offset="1"/>
		</LinearGradientBrush>
		<Style x:Key="RoundCornerTextBoxStyle" BasedOn="{x:Null}" TargetType="{x:Type TextBox}">
			<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
			<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
			<Setter Property="BorderBrush" Value="{StaticResource TextBoxBorder}"/>
			<Setter Property="BorderThickness" Value="1"/>
			<Setter Property="Padding" Value="1"/>
			<Setter Property="AllowDrop" Value="true"/>
			<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
			<Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
			<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
			<Setter Property="Template">
				<Setter.Value>
					<ControlTemplate TargetType="{x:Type TextBox}">
                       <Border x:Name="Bd" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="5">
						<ScrollViewer x:Name="PART_ContentHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
					   </Border>

						<ControlTemplate.Triggers>
							<Trigger Property="IsEnabled" Value="false">
								<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
								<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
							</Trigger>
						</ControlTemplate.Triggers>
					</ControlTemplate>
				</Setter.Value>
			</Setter>
		</Style>
	<!-- 应该在此定义应用程序级的资源。-->
	</Application.Resources>

注意:TemplateBinding 将自己的属性关联到目标控件的某个属性上。


第二步:样式绑定模板

<TextBox Style="{DynamicResource RoundCornerTextBoxStyle}"/>


四、ItemsControl的PanelTemplate

      控制 ItemControl条目容器

        <ListBox>


            <ListBox.ItemsPanel>
                <ItemsPanelTemplate>
                    <StackPanel Orientation="Horizontal"/>    条目会包装放到一个水平排列的StackPanel中
                </ItemsPanelTemplate>
            </ListBox.ItemsPanel>


            <!--条目-->
            <TextBlock Text="A"/>
            <TextBlock Text="B"/>
            <TextBlock Text="C"/>
            <TextBlock Text="D"/>


        </ListBox>



五、DataTemplate 与 ControlTemplate 的关系与应用

决定控件外观的是 ControlTemplate,  是控件                  Template 属性的值
决定数据外观的是 DataTemplate    ,   是控件   ContentTemplate 属性的值





       ContentPresenter(内容content,内容位置,内容模板 contentTemplate) 控件是ControlTemplate控件树的一个结点。
       DataTemplate 控件树是 ControlTemplae控件树的一颗子树。



1、所有目标样式

<Window>
  <Style TargetType="{x:Type TextBox}">
    <Setter Property="Template">
         <Setter.Value>
            <ControlTemplate TargetType="{x:Type TextBox}">
                控件模板
            </ControlTemplate>
        </Setter.Value>
    </Setter>
  </Style>

  <StackPanel>
  <TextBox/>
  <TextBox Style="{x:null}" text="不应用样式"/>
  </StackPanel>
</Window>


2、设置 DataTemplate 的 DataType 属性,可以把 DataTemplate 应用在那种数据类型上。

效果:


代码:

创建数据类型

    public class Unit
    {
        public int Price { get; set; }
        public string Year { get; set; }
    }

数据类型模板、数据类型源

<Window x:Class="WpfApplication.Window17"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication"
        xmlns:c="clr-namespace:System.Collections;assembly=mscorlib"

        Title="Window17" Height="300" 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>
                    <TextBlock Text="{Binding Price}" Margin="5,0"/>
                </StackPanel>
            </Grid>
        </DataTemplate>
        <!--数据类型 (源)-->
        <c:ArrayList x:Key="ds">
            <local:Unit Year="2001年" Price="100"/>
            <local:Unit Year="2002年" Price="150"/>
            <local:Unit Year="2003年" Price="180"/>
        </c:ArrayList>
    </Window.Resources>
    <StackPanel>
        <!--绑定数据类型源-->
        <ListBox ItemsSource="{StaticResource ds}"/>
        <ComboBox ItemsSource="{StaticResource ds}"/>
    </StackPanel>
</Window>



3、XmlDataProvider 做为数据源

<Window x:Class="WpfApplication.Window17"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication"
        xmlns:c="clr-namespace:System.Collections;assembly=mscorlib">
    <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>
                    <TextBlock Text="{Binding XPath=@Price}" Margin="5,0"/>
                </StackPanel>
            </Grid>
        </DataTemplate>
        <XmlDataProvider x:Key="ds" XPath="Units/Unit">
            <x:XData>
                <Units xmlns="">
                    <Unit Year="2001" Price="150"/>
                    <Unit Year="2002" Price="180"/>
                    <Unit Year="2003" Price="200"/>
                </Units>
            </x:XData>
        </XmlDataProvider>
        
    </Window.Resources>
    <StackPanel>
        <!--绑定数据类型源-->
        <ListBox ItemsSource="{Binding Source={StaticResource ds}}"/>
        <ComboBox ItemsSource="{Binding Source={StaticResource ds}}"/>
    </StackPanel>
</Window>


4、TreeView  显示多层级,不同类型的数据。因为数据类型不同,我们需要每种数据类型设置一种模板。

       使用层级数据模板 HierarchicalDataTemplate


xml数据

<?xml version="1.0" encoding="utf-8" ?>
<Data xmlns="">
  <Grade Name="一年级">
    <Class Name="甲班">
      <Group Name="A组"/>
      <Group Name="B组"/>
    </Class>
  </Grade>
</Data>

<Window x:Class="AutomaticConfigurationAPP.Window3"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window3" Height="300" Width="300">
    <Window.Resources>
        <!--Source数据源 XPath数据源路径-->
        <XmlDataProvider x:Key="ds" Source="/Resources/Xml/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">
            <CheckBox Content="{Binding XPath=@Name}"/>
        </HierarchicalDataTemplate>      
    </Window.Resources>
    
    <Grid>
        <TreeView Margin="5" ItemsSource="{Binding Source={StaticResource ds}}"/>
    </Grid>
</Window>


5、同一种数据类型的嵌套结构。

      只设计一个 HierarchicalDataTemplate ,会自动产生迭代效果。


效果:


<?xml version="1.0" encoding="utf-8" ?>
<Data xmlns="">
  <Operation Name="文件" Gesture="F">
    <Operation Name="新建" Gesture="N">
      <Operation Name="项目" Gesture="Control + P"/>
      <Operation Name="网站" Gesture="Control + W"/>
    </Operation>
  </Operation>
</Data>


<Window x:Class="AutomaticConfigurationAPP.Window4"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window4" Height="300" Width="300">
    <Window.Resources>
        <!--Source数据源 XPath数据路径-->
        <XmlDataProvider x:Key="ds" Source="/Resources/Xml/Data2.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 MenuItem.Click="StackPanel_Click">
        <Menu ItemsSource="{Binding Source={StaticResource ds}}"/>
    </StackPanel>
       
</Window>


        private void StackPanel_Click(object sender, RoutedEventArgs e)
        {
            MenuItem mi = e.OriginalSource as MenuItem;
            //HierarchicalDataTemplate 作用的目标是MenuItem.Header
            XmlElement xe = mi.Header as XmlElement;
            MessageBox.Show(xe.Attributes["Name"].Value);

        }


六、从外部访问 Template (模板)的控件、获取它的属性值

        DataTemplate 和 ControlTemplate 两个类均派生自 FrameWorkTemplate类。这个类有个 FindName方法 供我们查询内部控件。

        ControlTemplate 对象: 访问其目标控件 Template . FindName就能拿到。

        DataTemplate 对象:     直接使用低层数据(如果想获得控件长度、宽度 Template . FindName)。


1、获得ControlTemplate 中的控件。

效果:



<Window x:Class="AutomaticConfigurationAPP.Window5"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window5" Height="300" Width="300">
    <Window.Resources>
        <ControlTemplate x:Key="cTmp">
            <StackPanel Background="Orange">
               <TextBox x:Name="textbox1" Margin="6"/>
                <TextBox x:Name="textbox2" Margin="6,0"/>
                <TextBox x:Name="textbox3" Margin="6"/>
            </StackPanel>
        </ControlTemplate>
    </Window.Resources>
    <StackPanel Background="Yellow">
        <UserControl x:Name="uc" Template="{StaticResource cTmp}" Margin="5"/>
        <Button Content="FindName" Click="Button_Click"/>
    </StackPanel>
</Window>

事件

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            //Template.FindName
            TextBox tb= uc.Template.FindName("textbox1", this.uc) as TextBox;

            tb.Text = "textbox1";
            StackPanel sp = tb.Parent as StackPanel;
            (sp.Children[1] as TextBox).Text = "textbox2";
            (sp.Children[2] as TextBox).Text = "textbox3";
        }


2、获得DataTemplate 中的控件。

     如果获得与用户界面相关的数据(比如控件的宽度、高度)ContentTemplate.FindName("")。

     如果获得与业务相关的数据,直接访问底层(WPF采用数据驱动UI逻辑)Content

效果:

    public class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Skill { get; set; }
        public bool HasJob { get; set; }
    }

XAML

<Window x:Class="AutomaticConfigurationAPP.Window6"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:AutomaticConfigurationAPP"
        Title="Window6" Height="300" Width="300">
    <Window.Resources>
        <local:Student x:Key="stu" Id="1" Name="姓名" 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="textblockname" Text="{Binding Name}" Margin="5"/>
                    <TextBlock Text="{Binding Skill}" Margin="5"/>
                </StackPanel>
            </Border>
        </DataTemplate>
    </Window.Resources>
    <StackPanel>
        <ContentPresenter x:Name="cp" 
                          Content="{StaticResource stu}" 
                          ContentTemplate="{StaticResource stuDT}"

                          Margin="5"/>
        <!--Content="{StaticResource 内容数据源}" ContentTemplate="{StaticResource 内容模板}"-->
        <Button Content="Find" Margin="5,0" Click="Button_Click"/>
    </StackPanel>
</Window>


C#

           //内容模板查找控件
           TextBlock tb= this.cp.ContentTemplate.FindName("textblockname", this.cp) as TextBlock;
           MessageBox.Show(tb.Text);


           //直接使用底层数据
           Student stu = this.cp.Content as Student;
           MessageBox.Show(stu.Name);



实例:访问业务逻辑数据、访问界面逻辑数据

界面:



XAML


<Window x:Class="AutomaticConfigurationAPP.Window2"
        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:AutomaticConfigurationAPP"

        Title="Window2" Height="300" Width="300">
    <Window.Resources>
        <c:ArrayList x:Key="stuList">
            <local:Student Id="1" Name="a" Skill="wpf" HasJob="True"/>
            <local:Student Id="2" Name="b" Skill="MVC" HasJob="True"/>
            <local:Student Id="3" Name="c" Skill="c#" 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="hasjobDT">
            <CheckBox x:Name="checkboxJob" IsChecked="{Binding HasJob}"/>
        </DataTemplate>
    </Window.Resources>
    <Grid Margin="5">
        <ListView x:Name="listviewStudent" ItemsSource="{StaticResource stuList}">
            <ListView.View>


                <!--ListView的View属性是GridView-->
                <GridView>
                    <GridViewColumn Header="ID" DisplayMemberBinding="{Binding Id}"/>
                    <!--CellTemplate是TextBox-->
                    <GridViewColumn Header="姓名" CellTemplate="{StaticResource nameDT}"/>
                    <GridViewColumn Header="技能" CellTemplate="{StaticResource skillDT}"/>
                    <GridViewColumn Header="已工作" CellTemplate="{StaticResource hasjobDT}"/>
                </GridView>
                
            </ListView.View>
        </ListView>


    </Grid>
</Window>


c#

        private void textboxname_GotFocus(object sender, RoutedEventArgs e)
        {
            //访问业务逻辑数据
            TextBox tb = e.OriginalSource as TextBox;//获得事件的源头(TextBox)
            //沿UI元素树上溯到DataTemplate的目标控件(ContentPresenter),并获取它内容,它内容一定是个Student
            ContentPresenter cp = tb.TemplatedParent as ContentPresenter;
            Student stu = cp.Content as Student;//一行
            //MessageBox.Show(stu.HasJob.ToString());
            this.listviewStudent.SelectedItem = stu;


            //访问界面逻辑数据
            //查找包含的ListViewItem
            ListViewItem lvi = this.listviewStudent.ItemContainerGenerator.ContainerFromItem(stu) as ListViewItem;
            CheckBox chb = this.FindVisualChild<CheckBox>(lvi);
            MessageBox.Show(chb.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;
        }


七、Style 样式

Style样式包含两种元素:

Setter类  设置控件静态的外观。

Trigger类 设置控件行为的风格。


实例一:模板绑定Padding属性的值,并使用属性值在ContentPresenter 元素周围创建外边距。



            <Button Margin="5" Padding="5" Background="Black" Template="{Binding Source={StaticResource ButtonTemplate}}">
                控件模板
            </Button>

    <Window.Resources>
        <ControlTemplate x:Key="ButtonTemplate" TargetType="{x:Type Button}">
            <Border Name="borderName" 
                    BorderBrush="Orange" 
                    BorderThickness="3" 
                    CornerRadius="2"
                    Background="Red" 
                    TextBlock.Foreground="White">

                <!-- ContentPresenter 就是負責將Content屬性顯示出來 -->
                <!--模板绑定:模板从 应用模板 中获取值 Margin="{TemplateBinding Padding}"-->
                <ContentPresenter  
                    RecognizesAccessKey="True" 
                    Margin="{TemplateBinding Padding}">
                    
                </ContentPresenter>
            </Border>
            <ControlTemplate.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter TargetName="borderName" Property="Background" Value="DarkRed"/>
                </Trigger>
                <Trigger Property="IsPressed" Value="True">
                    <Setter TargetName="borderName" Property="Background" Value="IndianRed"/>
                    <Setter TargetName="borderName" Property="BorderBrush" Value="DarkKhaki"/>
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>
    </Window.Resources>


实例二:动画按钮

    <Window.Resources>
        <ControlTemplate x:Key="ButtonTemplate" TargetType="{x:Type Button}">
            <Border BorderBrush="Orange" 
                    BorderThickness="3" 
                    CornerRadius="2"
                    Background="Red" 
                    TextBlock.Foreground="White" 
                    Name="Border">
                <Grid>
                    <Rectangle Name="FocusCue" 
                               Visibility="Hidden" 
                               Stroke="Black"
                               StrokeThickness="1" 
                               StrokeDashArray="1 2"
                               SnapsToDevicePixels="True" >
                        
                    </Rectangle>
                    <ContentPresenter RecognizesAccessKey="True"
                                 Margin="{TemplateBinding Padding}">
                        
                    </ContentPresenter>
                </Grid>
            </Border>
            <ControlTemplate.Triggers>
                <EventTrigger RoutedEvent="MouseEnter">
                    <BeginStoryboard>
                        <Storyboard>
                            <ColorAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="Background.Color"
                                            To="Blue" Duration="0:0:1" AutoReverse="True" RepeatBehavior="Forever"></ColorAnimation>
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger>
                <EventTrigger RoutedEvent="MouseLeave">
                    <BeginStoryboard>
                        <Storyboard>
                            <ColorAnimation Storyboard.TargetName="Border" Storyboard.TargetProperty="Background.Color" Duration="0:0:0.5"></ColorAnimation>
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger>

                <Trigger Property="IsPressed" Value="True">
                    <Setter TargetName="Border" Property="Background" Value="IndianRed" />
                    <Setter TargetName="Border" Property="BorderBrush" Value="DarkKhaki" />
                </Trigger>
                <Trigger Property="IsKeyboardFocused" Value="True">
                    <Setter TargetName="FocusCue" Property="Visibility" Value="Visible" />
                </Trigger>

            </ControlTemplate.Triggers>
        </ControlTemplate>

    </Window.Resources>


实例三:修改LIstBox样式(嵌套模板)。


步骤:

1、ItemsPresenter 外观(ListBox)

2、ContentPresenter 外观(ListBoxItem)

3、ScrollBar外观



<Window x:Class="AutomaticConfigurationAPP.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="300" Width="300">
    <Window.Resources>
        <!--单色画刷-->
        <SolidColorBrush x:Key="StandardBorderBrush" Color="#888" />
        <SolidColorBrush x:Key="HoverBorderBrush" Color="#DDD" />
        <SolidColorBrush x:Key="SelectedBackgroundBrush" Color="Gray" />
        <SolidColorBrush x:Key="SelectedForegroundBrush" Color="White" />
        <LinearGradientBrush x:Key="ListBoxBackgroundBrush" StartPoint="0,0" EndPoint="1,0.001">
            <GradientBrush.GradientStops>
                <GradientStopCollection>
                    <GradientStop Color="White" Offset="0.0" />
                    <GradientStop Color="White" Offset="0.6" />
                    <GradientStop Color="#DDDDDD" Offset="1.2"/>
                </GradientStopCollection>
            </GradientBrush.GradientStops>
        </LinearGradientBrush>

        <LinearGradientBrush x:Key="StandardBrush" StartPoint="0,0" EndPoint="0,1">
            <GradientBrush.GradientStops>
                <GradientStopCollection>
                    <GradientStop Color="#FFF" Offset="0.0"/>
                    <GradientStop Color="#CCC" Offset="1.0"/>
                </GradientStopCollection>
            </GradientBrush.GradientStops>
        </LinearGradientBrush>

        <SolidColorBrush x:Key="GlyphBrush" Color="#444" />
        <LinearGradientBrush x:Key="PressedBrush" StartPoint="0,0" EndPoint="0,1">
            <GradientBrush.GradientStops>
                <GradientStopCollection>
                    <GradientStop Color="#BBB" Offset="0.0"/>
                    <GradientStop Color="#EEE" Offset="0.1"/>
                    <GradientStop Color="#EEE" Offset="0.9"/>
                    <GradientStop Color="#FFF" Offset="1.0"/>
                </GradientStopCollection>
            </GradientBrush.GradientStops>
        </LinearGradientBrush>

        <!--滚动条按钮为圆圈-->
        <Style x:Key="ScrollBarLineButtonStyle" TargetType="{x:Type RepeatButton}">
            <Setter Property="Focusable" Value="False"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type RepeatButton}">
                        <Grid Margin="1">
                            <Ellipse Name="Border" StrokeThickness="1" Stroke="{StaticResource StandardBorderBrush}"
                     Fill="{StaticResource StandardBrush}"></Ellipse>
                            <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"></ContentPresenter>
                        </Grid>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsPressed" Value="true">
                                <Setter TargetName="Border" Property="Fill" Value="{StaticResource PressedBrush}" />
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <!--滚动条Thumb上的RepeatButton对象,背景为透明-->
        <Style x:Key="ScrollBarPageButtonStyle" TargetType="{x:Type RepeatButton}">
            <Setter Property="IsTabStop" Value="False"/>
            <Setter Property="Focusable" Value="False"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type RepeatButton}">
                        <Border Background="Transparent" />
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <!--滚动条Thumb形状为椭圆-->
        <Style x:Key="ScrollBarThumbStyle" TargetType="{x:Type Thumb}">
            <Setter Property="IsTabStop" Value="False"/>
            <Setter Property="Focusable" Value="False"/>
            <Setter Property="Margin" Value="1,0,1,0" />
            <Setter Property="Background" Value="{StaticResource StandardBrush}" />
            <Setter Property="BorderBrush" Value="{StaticResource StandardBorderBrush}" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Thumb}">
                        <Ellipse Stroke="{StaticResource StandardBorderBrush}"
                     Fill="{StaticResource StandardBrush}"></Ellipse>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

        <ControlTemplate x:Key="VerticalScrollBar" TargetType="{x:Type ScrollBar}">
            <Grid>
                <!--滚动条包含三行网格。顶部,低部放按钮。中间Track元素-->
                <Grid.RowDefinitions>
                    <RowDefinition MaxHeight="18"/>
                    <RowDefinition Height="*"/>
                    <RowDefinition MaxHeight="18"/>
                </Grid.RowDefinitions>

                <RepeatButton 
                    Grid.Row="0" 
                    Height="18"
                    Style="{StaticResource ScrollBarLineButtonStyle}"
                    Command="ScrollBar.LineUpCommand" >
                    <!--绘制向上箭头的Path对象(箭头使用,微语言路径)-->
                    <Path
                       Fill="{StaticResource GlyphBrush}"
                       Data="M 0 4 L 8 4 L 4 0 Z">
                    </Path>
                </RepeatButton>

                <!--Track名称必须是PART_Track,为了使ScrollBar能成功关联到它的代码-->
                <!--ViewportSize="0" 尺度更具内容变化-->
                <Track 
                    Name="PART_Track" 
                    Grid.Row="1" 
                    IsDirectionReversed="True" 
                    ViewportSize="0">
                    <!--Track封装两个RepeatButton对象和Thumb元素-->
                    <Track.DecreaseRepeatButton>
                        <RepeatButton Command="ScrollBar.PageUpCommand" Style="{StaticResource ScrollBarPageButtonStyle}">
                        </RepeatButton>
                    </Track.DecreaseRepeatButton>
                    <Track.Thumb>
                        <Thumb Style="{StaticResource ScrollBarThumbStyle}">
                        </Thumb>
                    </Track.Thumb>
                    <Track.IncreaseRepeatButton>
                        <RepeatButton Command="ScrollBar.PageDownCommand" Style="{StaticResource ScrollBarPageButtonStyle}">
                        </RepeatButton>
                    </Track.IncreaseRepeatButton>
                </Track>

                <RepeatButton
                    Grid.Row="3" 
                    Height="18"
                    Style="{StaticResource ScrollBarLineButtonStyle}"
                    Command="ScrollBar.LineDownCommand">
                    <Path              
                       Fill="{StaticResource GlyphBrush}"
                       Data="M 0 0 L 4 4 L 8 0 Z">
                    </Path>
                </RepeatButton>

            </Grid>
        </ControlTemplate>

        
        <Style TargetType="{x:Type ScrollBar}">
            <Setter Property="SnapsToDevicePixels" Value="True"/>
            <Setter Property="OverridesDefaultStyle" Value="true"/>
            <Style.Triggers>
                <!--垂直滚动条模板-->
                <Trigger Property="Orientation" Value="Vertical">
                    <Setter Property="Width" Value="18"/>
                    <Setter Property="Height" Value="Auto" />
                    <Setter Property="Template" Value="{StaticResource VerticalScrollBar}" />
                </Trigger>
            </Style.Triggers>
        </Style>

        <!-- ItemsPresenter 外观-->
        <Style TargetType="{x:Type ListBox}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ListBox}">
                        <!--嵌套模板-->
                        <Border
                            Name="border"
                            Background="{StaticResource ListBoxBackgroundBrush}"
                            BorderBrush="{StaticResource StandardBorderBrush}"
                            BorderThickness="1" CornerRadius="3"
                            >
                            <!--ScrollViewer 容纳所有列表项(ItemsPresenter)-->
                            <ScrollViewer Focusable="False">
                                <ItemsPresenter Margin="2"></ItemsPresenter>
                            </ScrollViewer>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        
        <!-- ContentPresenter 外观-->
        <Style TargetType="{x:Type ListBoxItem}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ListBoxItem}">
                        <Border
                           Name="Border" 
                           BorderThickness="2" 
                           CornerRadius="3"
                           Padding="1" 
                           SnapsToDevicePixels="True">
                            <ContentPresenter></ContentPresenter>
                        </Border>
                        <ControlTemplate.Triggers>
                            <EventTrigger RoutedEvent="ListBoxItem.MouseEnter">
                                <EventTrigger.Actions>
                                    <BeginStoryboard>
                                        <Storyboard>
                                            <DoubleAnimation 
                                                Storyboard.TargetProperty="FontSize"
                                                To="20" 
                                                Duration="0:0:1">
                                            </DoubleAnimation>
                                            <!--字体1秒钟=20大小-->
                                        </Storyboard>
                                    </BeginStoryboard>
                                </EventTrigger.Actions>
                            </EventTrigger>
                            <EventTrigger RoutedEvent="ListBoxItem.MouseLeave">
                                <EventTrigger.Actions>
                                    <BeginStoryboard>
                                        <Storyboard>
                                            <DoubleAnimation 
                                                Storyboard.TargetProperty="FontSize"
                                                BeginTime="0:0:0.5" 
                                                Duration="0:0:0.2">
                                                <!--字体延迟0.5秒,0.2秒缩小-->
                                            </DoubleAnimation>
                                        </Storyboard>
                                    </BeginStoryboard>
                                </EventTrigger.Actions>
                            </EventTrigger>

                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource HoverBorderBrush}"/>
                            </Trigger>
                            <Trigger Property="IsSelected" Value="True">
                                <Setter TargetName="Border" Property="Background" Value="{StaticResource SelectedBackgroundBrush}"/>
                                <Setter TargetName="Border" Property="TextBlock.Foreground" Value="{StaticResource SelectedForegroundBrush}"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        
    </Window.Resources>
    <Grid>
         <ListBox Height="100">
            <ListBoxItem Content="1" />
            <ListBoxItem Content="2" />
            <ListBoxItem Content="3" />
            <ListBoxItem Content="4" />
            <ListBoxItem Content="5" />
            <ListBoxItem Content="6" />
            <ListBoxItem Content="7" />
            <ListBoxItem Content="8" />
        </ListBox>
    </Grid>
</Window>


  • 6
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
### 回答1: WPF深入浅出源码是一篇由CSDN博主韦静之所写的源码解析文章,主要介绍了WPF的架构、控件布局数据绑定和事件系统等方面。文章从WPF的核心类DependencyObject和DependencyProperty入手,讲解了WPF的属性系统。在讲解控件的过程中,作者通过剖析Button控件的源码,介绍了WPF控件模板和样式。布局方面,文章从Grid和Canvas两种布局方式入手,详细介绍了WPF布局的机制和实现。数据绑定和事件系统也得到了充分的解析和剖析。整篇文章详细而清晰地解释了WPF的实现原理,并用大量的代码示例支撑,所以对于有一定WPF开发经验的开发人员来说,这篇文章意义重大,可以加深开发人员对WPF的理解和使用。对于初学WPF的人来说,这篇文章虽然内容有些难度,但通过认真阅读,也可以获得一定的收获和启示。总之,WPF深入浅出源码这篇文章对于各类WPF开发人员都有着较高的参考价值。 ### 回答2: WPF(Windows Presentation Foundation)是一种面向Windows的桌面应用程序开发技术,它提供了许多现代化的 UI 设计技术和工具,可以让开发者更加方便地创建富客户端应用程序。 WPF 是一个庞大而复杂的技术,要理解其底层原理,必须深入浅出地阅读源代码。CSDN 上有一篇与此相关的文章,名为《WPF深入浅出源码》。 这篇文章详细介绍了 WPF 的设计思路、架构、基本元素和核心代码实现。它以一个简单的例子从 UI 元素如何呈现开始,逐步深入探讨了视觉树、逻辑树、布局系统、绘图引擎以及与硬件的交互等方面。文章还提到了一些 WPF 开发中常见的问题和技巧,例如内存泄漏、多线程等。 读完这篇文章,你将深入了解 WPF 在实现高性能、灵活的 UI 渲染方面所做的工作,以及它与其他 Windows 技术的关系,对提升 WPF 开发技能将大有裨益。 总之,在学习 WPF 开发时,深度阅读源代码是非常必要的。《WPF深入浅出源码》是一篇值得一读的好文章,可以帮助你更好地掌握 WPF 的核心技术。 ### 回答3: 《WPF深入浅出源码》是一本关于Windows Presentation Foundation(WPF)的源码分析的书籍,作者介绍了WPF的核心概念与基础知识,并结合实例讲解WPF控件的使用方法。这本书提供了一种深入探究WPF内部实现的方式,让读者能够更好地理解WPF的本质,并学习如何设计开发高效的WPF应用程序。 本书共分为19章,对WPF的功能、数据绑定、命令、动画、触摸、布局、样式、模板等方面进行了详细讲解,同时提供了大量实例和源码分析,让读者通过实际操作深入理解WPF的使用方法和内部原理。 在本书中,作者详细介绍了WPF的元素树和布局系统,并深入剖析了WPF控件的内部实现和渲染过程。此外,本书还介绍了WPF的样式、模板控件模板,并详细讲解了自定义控件的开发方法。 总之,本书详细介绍了WPF的各个方面,并提供了大量实例和源码分析,对WPF的掌握提供了良好的指导。如果你想深入学习WPF,并想要更好地理解其内部原理,那么这本书一定是一个不错的选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tiz198183

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值