zoukankan      html  css  js  c++  java
  • WPF 详解模板

    在WPF中有三大模板 ControlTemplate,ItemsPanelTemplate,DataTemplate.其中ControlTemplate和 ItemsPanelTemplate是控件模板,DataTemplate是数据模板,他们都派生自FrameworkTemplate抽象类。

    1、ControlTemplate

    ControlTemplate:控件模板主要有两个重要属性:VisualTree内容属性和Triggers触发器。所谓VisualTree(视觉树),就是呈现我们所画的控件。Triggers可以对我们的视觉树上的元素进行一些变化。一般用于单内容控件。

    画一个按钮模板来举例说明:

    <Style TargetType="Button">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="Button">
                            <Grid>
                                <Ellipse Width="100" Height="100">
                                    <Ellipse.Fill>
                                        <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                                            <GradientStop Offset="0" Color="blue"/>
                                            <GradientStop Offset="1" Color="LightBlue"/>
                                        </LinearGradientBrush>
                                    </Ellipse.Fill>
                                </Ellipse>
                                <Ellipse Width="80" Height="80">
                                    <Ellipse.Fill>
                                        <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                                            <GradientStop Offset="0" Color="White"/>
                                            <GradientStop Offset="1" Color="Transparent"/>
                                        </LinearGradientBrush>
                                    </Ellipse.Fill>
                                </Ellipse>
                            </Grid>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>

    <Button Content="Hello WPF"/>

    结果:image

    ControlTemplate之子 ContentControl和ContentPresenter

    我们在ControlTemplate中画了两个椭圆,应用于所有的Button按钮,但我们Button中有Content属性(内容为 Hello WPF),却没有显示出来。因为这里用ControlTemplate重写了Button的样式,所以我们也要在ControlTemplate中增加 ContentControl。通过ContentControl中的Content来绑定父容器的Content属性。

    <Style TargetType="Button">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="Button">
                            <Grid>
                                <Ellipse Width="100" Height="100">
                                    <Ellipse.Fill>
                                        <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                                            <GradientStop Offset="0" Color="blue"/>
                                            <GradientStop Offset="1" Color="LightBlue"/>
                                        </LinearGradientBrush>
                                    </Ellipse.Fill>
                                </Ellipse>
                                <Ellipse Width="80" Height="80">
                                    <Ellipse.Fill>
                                        <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                                            <GradientStop Offset="0" Color="White"/>
                                            <GradientStop Offset="1" Color="Transparent"/>
                                        </LinearGradientBrush>
                                    </Ellipse.Fill>
                                </Ellipse>
                                <ContentControl VerticalAlignment="Center" HorizontalAlignment="Center" Content="{TemplateBinding Content}"/>
                            </Grid>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
    

    这下内容出来了image

    我们来看一下,ContentControl继承于Control的,用MSDN的话是:表示包含单项内容的控件、ContentControl 可以包含任何类型的公共语言运行库对象。如下ContentControl类图。

    image

    为了提高性能,我们可以用一个ControlPresenter来代替ContentControl,效果还是一样,那他们有什么区别呢?

    来看下ControlPresenter这个类,它继承于FreameworkElement,如下图:

    image

    ControlPresenter 通常叫做内容占位符。所以我们可以看到

    <ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center"/>来代替<ContentControl VerticalAlignment="Center" HorizontalAlignment="Center" Content="{TemplateBinding Content}"/> 这里少了Content绑定父容器,因为ControlPresenter有个隐式的Content="{TemplateBinding Content}",也就是你可以写也可以不写它。

    从他们的基类可以看出,ContentControl比ContentPresenter大多了。其实ControlPresenter是一个原始的构建块,而ContentControl是一个带控件模板的成熟控件(里面包含ControlPresenter)。

    所以我们一般用ControlPresenter。

    ControlTemplate的VisualTree我们讲过了,下面看下他的trigger如何运用。

    我们在原来的代码中增加

    <ControlTemplate.Triggers>
                 <Trigger Property="IsMouseOver" Value="true">
                       <Setter TargetName="ellipse1" Property="Fill" Value="Red"/>
                 </Trigger>
     </ControlTemplate.Triggers>

    当我们把鼠标移上去的时候就会变成如下图所示:

    image

    发挥我们的想象力,我们可以根据ControlTemplate做更多的特效。如下

    <Style TargetType="CheckBox">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="CheckBox">
                            <DockPanel>
                                <ContentPresenter DockPanel.Dock="Left" VerticalAlignment="Center" />
                                <Grid>
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="30"/>
                                        <ColumnDefinition Width="30"/>
                                    </Grid.ColumnDefinitions>
                                    <Rectangle Grid.Column="0" Grid.ColumnSpan="2" Fill="Gray"/>
                                    <TextBlock x:Name="txtBox"  Foreground="White" />
                                </Grid>
                            </DockPanel>
                            <ControlTemplate.Triggers>
                                <Trigger Property="IsChecked" Value="True">
                                    <Setter TargetName="txtBox" Property="Grid.Column" Value="1"/>
                                    <Setter TargetName="txtBox" Property="Text" Value="On"/>
                                    <Setter TargetName="txtBox" Property="Background" Value="LightBlue"/>
                                </Trigger>
                                <Trigger Property="IsChecked" Value="{x:Null}">
                                    <Setter TargetName="txtBox" Property="Grid.Column" Value="0"/>
                                </Trigger>
                                <Trigger Property="IsChecked" Value="false">
                                    <Setter TargetName="txtBox" Property="Grid.Column" Value="0"/>
                                    <Setter TargetName="txtBox" Property="Text" Value="OFF"/>
                                </Trigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
    <Grid>
            <CheckBox Width="100" Height="30" Content="Click Me"/>
     </Grid>

    效果图:点击之前image点击之后image

    2、ItemsPanelTemplate

    ItemsPanelTemplate在MSDN的解释是:ItemsPanelTemplate 指定用于项的布局的面板。 GroupStyle 具有一个类型为 ItemsPanelTemplate 的 Panel 属性。 ItemsControl 类型具有一个类型为ItemsPanelTemplate 的 ItemsPanel 属性。

    我们先讲ItemTemplate。它一般用在多个内容控件的模板。比如ListBox。

    如下看ListBox应用ItemTemplate:

    在xaml中

    <Style TargetType="ListBox">
                <Setter Property="ItemTemplate">
                    <Setter.Value>
                        <DataTemplate>
                             <Image Source="{Binding UriSource}" Width="100" Height="100"/>
                        </DataTemplate>
                    </Setter.Value>
                </Setter>
            </Style>

      <ListBox x:Name="listBox" />

    在后台代码我们给它一些图片来填充这个ListBox.

     public partial class ListBoxUserControl : UserControl
        {
            public ListBoxUserControl()
            {
                InitializeComponent();
                listBox.ItemsSource = LoadImages();
            }
    
            public List<BitmapImage> LoadImages()
            {
                List<BitmapImage> bitmapImages=new List<BitmapImage>();
                DirectoryInfo directoryInfo = new DirectoryInfo(@"E:WPFDEMOControlTestControlTestImages");
                foreach (var item in directoryInfo.GetFiles("*.jpg"))
                {
                    Uri uri=new Uri(item.FullName);
                    bitmapImages.Add(new BitmapImage(uri));
                }
                return bitmapImages;
            }
        }
    

    image每一张图片就是一个Item。

    我们如果想让图片以横向显示。一开始我以为用StackPanel的Orientation=”Horiziontal”,发现犯了个错误。这样设 置是现在把Items中某一个item中的内容水平显示啊。这时就要用到ItemsPanelTemplate这个模板了。我们在ListBox的样式中 增加如下红色区域的代码:

     <Style TargetType="ListBox">
                <Setter Property="ItemTemplate">
                    <Setter.Value>
                        <DataTemplate>
                            <StackPanel Orientation="Horizontal">
                                <Image Source="{Binding UriSource}" Width="100" Height="100"/>
                                <TextBlock Text="qq" Background="Red"/>
                            </StackPanel>
                        </DataTemplate>
                    </Setter.Value>
                </Setter>
               

    <Setter Property="ItemsPanel"> <Setter.Value> <ItemsPanelTemplate> <StackPanel /> </ItemsPanelTemplate> </Setter.Value> </Setter>

            </Style>


    这里我在DataTemplate中加了StackPanel 为了说明加在这里的效果只是区分:Items整体横向和Item中内容的区别。

    image

    现在我们要想让布局可以随着窗体宽度变化,我们只要把ItemsPanelTemplate中的StackPanel 改成WrapPanel,并且设置ListBox的 ScrollViewer.HorizontalScrollBarVisibility="Disabled",这样才可以看到效果。

    image

    ControlTemplate之 ItemsPresenter和ContentPresenter

    我们先构造一个TreeView

    xaml中:

    在开始加个Loaded="UserControl_Loaded"

    <Grid>
            <TreeView x:Name="treeview" />
    </Grid>

    后台代码中:

      public class Node
        {
            private IList<Node> _childNodes;
            private string _name;
            public Node()
            {}
            public Node(string name)
            {
                _name = name;
            }
            public string Name
            {
                get { return _name; }
                set { _name = value; }
            }
    
            public IList<Node> ChildNodes
            {
                get
                {
                    if (_childNodes==null)
                        _childNodes=new List<Node>();
                    return _childNodes;
                }
               
            }
        }
    
    
    public partial class TreeViewUserControl : UserControl
        {
            public TreeViewUserControl()
            {
                InitializeComponent();
            }
    
            private void UserControl_Loaded(object sender, RoutedEventArgs e)
            {
                treeview.PreviewKeyDown += (o,a) => { a.Handled = true; };
                PopulateTreeView();
            }
            void PopulateTreeView()
            {
                Node rootNode=new Node("GrandFather");
                for (int i = 0; i < 2; i++)
                {
                    Node child=new Node("Father");
                    rootNode.ChildNodes.Add(child);
                    for (int j = 0; j < 3; j++)
                    {
                        Node child2=new Node("Son");
                        child.ChildNodes.Add(child2);
                    }
                }
    
                Node dummy=new Node();
                dummy.ChildNodes.Add(rootNode);
                treeview.ItemsSource = dummy.ChildNodes;
            }
    
           
    
        }
    

    没有任何样式的TreeView

    image

    下面我们如何运用ItemsPresenter和ContentPresenter来添加样式。来实现下面这幅图的效果

    image
    在TreeView中ItemsPresenter和ContentPresenter是什么关系:ContentPresenter是用来显示TreeView中Item的内容 。ItemsPresenter是用来显示它的子项(Item的子项,也就是说child’s Items)。

    <Style TargetType="TreeViewItem">
            <Style.Resources>
                <LinearGradientBrush x:Key="ItemAreaBrush" StartPoint="0,0.5" EndPoint="0.5,1">
                    <GradientStop Offset="0" Color="#66000000"/>
                    <GradientStop Offset="1" Color="#22000000"/>
                </LinearGradientBrush>
                <LinearGradientBrush x:Key="SelectedItemAreaBrush" StartPoint="0.5, 0" EndPoint="0.5, 1">
                    <GradientStop Color="Orange" Offset="0" />
                    <GradientStop Color="OrangeRed" Offset="1" />
                </LinearGradientBrush>
                <LinearGradientBrush x:Key="ItemBorderBrush" StartPoint="0.5, 0" EndPoint="0.5, 1">
                    <GradientStop Color="LightGray" Offset="0" />
                    <GradientStop Color="Gray" Offset="1" />
                </LinearGradientBrush>
                <LinearGradientBrush x:Key="SelectedItemBorderBrush" StartPoint="0.5, 0" EndPoint="0.5, 1">
                    <GradientStop Color="Yellow" Offset="0" />
                    <GradientStop Color="Black" Offset="1" />
                </LinearGradientBrush>
                <DropShadowBitmapEffect x:Key="DropShadowEffect"/>
            </Style.Resources>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="TreeViewItem">
                        <Grid Margin="2">
                            <Grid.RowDefinitions>
                                <RowDefinition Height="auto"/>
                                <RowDefinition Height="*"/>
                            </Grid.RowDefinitions>
                            <Border x:Name="border" Background="{StaticResource ResourceKey=ItemAreaBrush}" 
                                    BorderBrush="{StaticResource ItemBorderBrush}" BorderThickness="1" CornerRadius="8" Padding="6">
                                <ContentPresenter  ContentSource="Header" VerticalAlignment="Center" HorizontalAlignment="Center"/>
                            </Border>
                            <ItemsPresenter Grid.Row="1"/>
                        </Grid>
                        <ControlTemplate.Triggers>
                                <Trigger Property="IsSelected" Value="true">
                                    <Setter TargetName="border" Property="Panel.Background" Value="{StaticResource SelectedItemAreaBrush}"/>
                                </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Setter Property="ItemsPanel">
                <Setter.Value>
                    <ItemsPanelTemplate>
                        <StackPanel  Orientation="Horizontal" HorizontalAlignment="Center" IsItemsHost="True"/>
                    </ItemsPanelTemplate>
                </Setter.Value>
            </Setter>
            
        </Style>
    

    看红色代码的部分,这里ContentPresenter显示了父容器的Header的内容,比如GrandFather,Father,Son.而ItemsPresenter则是否让他显示其子元素。比如GrandFather的子元素为Father,没有设置<ItemsPresenter Grid.Row="1"/> 的话。子元素Father是不会显示的。

    这里为了突出层次化,运用了ItemsPanelTemplate。最后我们在资源里引用这个样式。

    <UserControl.Resources>
            <ResourceDictionary>
                <ResourceDictionary.MergedDictionaries>
                    <ResourceDictionary Source="/Controls/TreeViewItemStyle.xaml"/>
                </ResourceDictionary.MergedDictionaries>
                <HierarchicalDataTemplate DataType="{x:Type controls:Node}" ItemsSource="{Binding ChildNodes}">
                    <TextBlock Text="{Binding Name}"/>
                </HierarchicalDataTemplate>
            </ResourceDictionary>
        </UserControl.Resources>
    

    3、DataTemplate和HierarchicalDataTemplate

    DataTemplate就是显示绑定数据对象的模板。
    HierarchicalDataTemplate继承于DataTemplate,它专门对TreeViewItemMenuItem的一些数据对象的绑定。

    想了解下DataTemplate和HierarchicalDataTemplate,就看以上的例子吧。他们中只要有控件的绑定都会用到。想了解的更深一步,请看下南柯之石的这篇文章。

  • 相关阅读:
    数据库连接池的原理。为什么要使用连接池。
    如果你也用过 struts2.简单介绍下 springMVC 和 struts2 的区别有哪些?
    MyBatis 与 Hibernate 有哪些不同?
    Class.forName的作用?为什么要用?
    一个Redis实例最多能存放多少的keys?List、Set、Sorted Set他们最多能存放多少元素?
    MySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据?
    如果有大量的key需要设置同一时间过期,一般需要注意什么?
    使用过Redis做异步队列么,你是怎么用的?
    我们如何在Struts2应用程序中集成log4j?
    我们怎样才能在动作类中获得Servlet API请求,响应,HttpSession等对象?
  • 原文地址:https://www.cnblogs.com/jcomet/p/3291700.html
Copyright © 2011-2022 走看看