电竞比分网-中国电竞赛事及体育赛事平台

分享

WPF 詳解模板

 xyjackxjw 2013-05-16

WPF 詳解模板

在WPF中有三大模板ControlTemplate,ItemsPanelTemplate,DataTemplate.其中ControlTemplate和ItemsPanelTemplate是控件模板,DataTemplate是數(shù)據(jù)模板,他們都派生自FrameworkTemplate抽象類(lèi)。

1、ControlTemplate

ControlTemplate:控件模板主要有兩個(gè)重要屬性:VisualTree內(nèi)容屬性和Triggers觸發(fā)器。所謂VisualTree(視覺(jué)樹(shù)),就是呈現(xiàn)我們所畫(huà)的控件。Triggers可以對(duì)我們的視覺(jué)樹(shù)上的元素進(jìn)行一些變化。一般用于單內(nèi)容控件。

畫(huà)一個(gè)按鈕模板來(lái)舉例說(shuō)明:

<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"/>

結(jié)果:image

ControlTemplate之子 ContentControl和ContentPresenter

我們?cè)贑ontrolTemplate中畫(huà)了兩個(gè)橢圓,應(yīng)用于所有的Button按鈕,但我們Button中有Content屬性(內(nèi)容為Hello WPF),卻沒(méi)有顯示出來(lái)。因?yàn)檫@里用ControlTemplate重寫(xiě)了Button的樣式,所以我們也要在ControlTemplate中增加ContentControl。通過(guò)ContentControl中的Content來(lái)綁定父容器的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>

這下內(nèi)容出來(lái)了image

我們來(lái)看一下,ContentControl繼承于Control的,用MSDN的話是:表示包含單項(xiàng)內(nèi)容的控件、ContentControl 可以包含任何類(lèi)型的公共語(yǔ)言運(yùn)行庫(kù)對(duì)象。如下ContentControl類(lèi)圖。

image

為了提高性能,我們可以用一個(gè)ControlPresenter來(lái)代替ContentControl,效果還是一樣,那他們有什么區(qū)別呢?

來(lái)看下ControlPresenter這個(gè)類(lèi),它繼承于FreameworkElement,如下圖:

image

ControlPresenter 通常叫做內(nèi)容占位符。所以我們可以看到

<ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center"/>來(lái)代替<ContentControl VerticalAlignment="Center" HorizontalAlignment="Center" Content="{TemplateBinding Content}"/> 。這里少了Content綁定父容器,因?yàn)镃ontrolPresenter有個(gè)隱式的Content="{TemplateBinding Content}",也就是你可以寫(xiě)也可以不寫(xiě)它。

從他們的基類(lèi)可以看出,ContentControl比ContentPresenter大多了。其實(shí)ControlPresenter是一個(gè)原始的構(gòu)建塊,而ContentControl是一個(gè)帶控件模板的成熟控件(里面包含ControlPresenter)。

所以我們一般用ControlPresenter。

ControlTemplate的VisualTree我們講過(guò)了,下面看下他的trigger如何運(yùn)用。

我們?cè)谠瓉?lái)的代碼中增加

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

當(dāng)我們把鼠標(biāo)移上去的時(shí)候就會(huì)變成如下圖所示:

image

發(fā)揮我們的想象力,我們可以根據(jù)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>

效果圖:點(diǎn)擊之前image點(diǎn)擊之后image

2、ItemsPanelTemplate

ItemsPanelTemplate在MSDN的解釋是:ItemsPanelTemplate 指定用于項(xiàng)的布局的面板。 GroupStyle 具有一個(gè)類(lèi)型為 ItemsPanelTemplate 的 Panel 屬性。 ItemsControl 類(lèi)型具有一個(gè)類(lèi)型為ItemsPanelTemplate 的 ItemsPanel 屬性。

我們先講ItemTemplate。它一般用在多個(gè)內(nèi)容控件的模板。比如ListBox。

如下看ListBox應(yīng)用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" />

在后臺(tái)代碼我們給它一些圖片來(lái)填充這個(gè)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:\WPFDEMO\ControlTest\ControlTest\Images");
            foreach (var item in directoryInfo.GetFiles("*.jpg"))
            {
                Uri uri=new Uri(item.FullName);
                bitmapImages.Add(new BitmapImage(uri));
            }
            return bitmapImages;
        }
    }

image每一張圖片就是一個(gè)Item。

我們?nèi)绻胱寛D片以橫向顯示。一開(kāi)始我以為用StackPanel的Orientation=”Horiziontal”,發(fā)現(xiàn)犯了個(gè)錯(cuò)誤。這樣設(shè)置是現(xiàn)在把Items中某一個(gè)item中的內(nèi)容水平顯示啊。這時(shí)就要用到ItemsPanelTemplate這個(gè)模板了。我們?cè)贚istBox的樣式中增加如下紅色區(qū)域的代碼:

 <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 為了說(shuō)明加在這里的效果只是區(qū)分:Items整體橫向和Item中內(nèi)容的區(qū)別。

image

現(xiàn)在我們要想讓布局可以隨著窗體寬度變化,我們只要把ItemsPanelTemplate中的StackPanel 改成WrapPanel,并且設(shè)置ListBox的ScrollViewer.HorizontalScrollBarVisibility="Disabled",這樣才可以看到效果。

image

ControlTemplate之 ItemsPresenter和ContentPresenter

我們先構(gòu)造一個(gè)TreeView

xaml中:

在開(kāi)始加個(gè)Loaded="UserControl_Loaded"

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

后臺(tái)代碼中:

  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;
        }

       

    }

沒(méi)有任何樣式的TreeView

image

下面我們?nèi)绾芜\(yùn)用ItemsPresenter和ContentPresenter來(lái)添加樣式。來(lái)實(shí)現(xiàn)下面這幅圖的效果

image
在TreeView中ItemsPresenter和ContentPresenter是什么關(guān)系:ContentPresenter是用來(lái)顯示TreeView中Item的內(nèi)容 。ItemsPresenter是用來(lái)顯示它的子項(xiàng)(Item的子項(xiàng),也就是說(shuō)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的內(nèi)容,比如GrandFather,Father,Son.而ItemsPresenter則是否讓他顯示其子元素。比如GrandFather的子元素為Father,沒(méi)有設(shè)置<ItemsPresenter Grid.Row="1"/> 的話。子元素Father是不會(huì)顯示的。

這里為了突出層次化,運(yùn)用了ItemsPanelTemplate。最后我們?cè)谫Y源里引用這個(gè)樣式。

<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就是顯示綁定數(shù)據(jù)對(duì)象的模板。
HierarchicalDataTemplate繼承于DataTemplate,它專(zhuān)門(mén)對(duì)TreeViewItemMenuItem的一些數(shù)據(jù)對(duì)象的綁定。

想了解下DataTemplate和HierarchicalDataTemplate,就看以上的例子吧。他們中只要有控件的綁定都會(huì)用到。想了解的更深一步,請(qǐng)看下南柯之石的這篇文章。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約