【轉】WPF Template模版之DataTemplate與ControlTemplate(一)

 WPF系統不但支持傳統的Winfrom編程的用戶界面和用戶體驗設計,更支持使用專門的設計工具Blend進行專業設計,同時還推出了以模板爲核心的新一代設計理念。html

1. 模板的內涵

    做爲表現形式,每一個控件都是爲了實現某種用戶操做算法和直觀顯示某種數據而生,一個控件看上去是什麼樣子由它的「算法內容」和「數據內容"決定,這就是內容決定形式,這裏,咱們引入兩個概念:程序員

控件的算法內容:控件能展現哪些數據、具備哪些方法、能響應哪些操做、能激發什麼事件,簡而言之就是控件的功能,它們是一組相關的算法邏輯。
控件的數據內容:控件具體展現的數據是什麼。算法


     以往的GUI開發技術(ASP.NET+Winform)中,控件內部邏輯和數據是固定的,程序員不能改變;對於控件的外觀,程序員能作的改變也很是的有限,通常也就是設置控件的屬性,想改變控件的內部結構是不可能的。若是想擴展一個控件的功能或者更改器外觀讓其更適應業務邏輯,哪怕只是一丁點的改變,也須要建立控件的子類或者建立用戶控件。形成這個局面的根本緣由是數據和算法的「形式」和「內容」耦合的太緊了。編程


    在WPF中,經過引入模板,微軟將數據和算法的內容與形式解耦了。WPF中的Template分爲兩大類:
ControlTemplate:是算法內容的表現形式,一個控件怎麼組織其內部結構才能讓它更符合業務邏輯、讓用戶操做起來更舒服就是由它來控制的。它決定了控件「長成什麼樣子」,並讓程序員有機會在控件原有的內部邏輯基礎上擴展本身的邏輯。
DataTemplate:是數據內容的展現方式,一條數據顯示成什麼樣子,是簡單的文本仍是直觀的圖形就由它來決定了。工具


Template就是數據的外衣-----ControlTemplate是控件的外衣,DataTemplate是數據的外衣。url

2. 數據的外衣DataTemplate

    WPF不但支持UserControl還支持DataTemplate爲數據造成視圖。不要覺得DataTemplate有多難!從UserControl升級到DataTemplate通常就是複製,粘貼一下再改幾個字符的事兒。spa

DataTemplate經常使用的地方有三處,分別是:
ContentControl的ContentTemplate屬性,至關於給ContentControl的內容穿衣服。
ItemsControl的ItemTemplate,至關於給ItemControl的數據條目穿衣服。
GridViewColumn的CellTempldate屬性,至關於給GridViewColumn的數據條目穿衣服。設計

    事件驅動是控件和控件之間溝通或者說是形式和形式之間的溝通,數據驅動則是數據與控件之間的溝通,是內容決定形式。使用DataTemplate就能夠方便的把事件驅動模式轉換爲數據驅動模式。code

 

    讓咱們用一個例子體現DataTemplate的使用。例子實現的需求是這樣的:有一列汽車數據,這列數據顯示在ListBox裏面,要求ListBox的條目顯示汽車的廠商圖標和簡要參數,單擊某個條目後在窗體的詳細內容區顯示汽車的圖片和詳細參數。廠商的Logo和汽車的照片都是要用到的,因此先在項目中創建資源管理目錄並把圖片添加進來。Logo文件名與廠商的名稱一致,照片的名稱則與車名一致。組織結構如圖:orm

 

建立Car數據類型:

/// <summary>  
/// Car數據類型 -- 必須定義成屬性{ get; set; }  
/// </summary>  
public class Car  
{  
    public string Name { get; set; }  
    public string ImagePath { get; set; }  
    public string Automarker { get; set; }  
    public string Year { get; set; }  
}  

汽車廠商和名稱不能直接拿來做爲圖片路徑,這時就要使用Converter:

/// <summary>  
    /// 路徑轉圖片  
    /// </summary>  
    public class PathToImage:IValueConverter  
    {  
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)  
        {  
            string url = (string)value;  
            return(new BitmapImage(new Uri(url, UriKind.Relative)));  
        }  
  
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)  
        {  
            throw new NotImplementedException();  
        }  
    }  

XAML中添加條目模版:

<DataTemplate x:Key="_carListItemViewTemplate">  
    <Grid Margin="2">  
        <StackPanel Orientation="Horizontal">  
            <Image Source="{Binding Path=Automarker, Converter={StaticResource _path2Image}}" Grid.RowSpan="3"  
                   Width="64" Height="64"></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>  

XAML中添加顯示詳細信息的模版:

<DataTemplate x:Key="_carDetailViewTemplate">  
    <Border BorderBrush="Black" BorderThickness="1" CornerRadius="6">  
        <StackPanel Margin="5">  
            <Image Width="400" Height="250" Source="{Binding Path=ImagePath, Converter={StaticResource _path2Image}}"></Image>  
            <StackPanel Orientation="Horizontal" Margin="5, 0">  
                <TextBlock Text="Name:" FontWeight="Bold" FontSize="20"></TextBlock>  
                <TextBlock Text="{Binding Path=Name}" FontSize="20" Margin="5, 0"></TextBlock>  
            </StackPanel>  
        </StackPanel>  
    </Border>  
</DataTemplate>  

完整的XAML代碼:

<Window x:Class="WpfApplication11.wnd112"  
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
        xmlns:local="clr-namespace:WpfApplication11"  
        Title="DataTemplate" Height="350" Width="623">  
    <Window.Resources>  
        <!--Convert-->  
        <local:PathToImage x:Key="_path2Image"></local:PathToImage>  
        <!--DataTemplate for Detail View -->  
        <DataTemplate x:Key="_carDetailViewTemplate">  
            <Border BorderBrush="Black" BorderThickness="1" CornerRadius="6">  
                <StackPanel Margin="5">  
                    <Image Width="400" Height="250" Source="{Binding Path=ImagePath, Converter={StaticResource _path2Image}}"></Image>  
                    <StackPanel Orientation="Horizontal" Margin="5, 0">  
                        <TextBlock Text="Name:" FontWeight="Bold" FontSize="20"></TextBlock>  
                        <TextBlock Text="{Binding Path=Name}" FontSize="20" Margin="5, 0"></TextBlock>  
                    </StackPanel>  
                </StackPanel>  
            </Border>  
        </DataTemplate>  
        <!--DataTemplate for Item View -->  
        <DataTemplate x:Key="_carListItemViewTemplate">  
            <Grid Margin="2">  
                <StackPanel Orientation="Horizontal">  
                    <Image Source="{Binding Path=Automarker, Converter={StaticResource _path2Image}}" Grid.RowSpan="3"  
                           Width="64" Height="64"></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>      
    <!---Window Content-->  
    <StackPanel Orientation="Horizontal" Margin="5">  
        <UserControl ContentTemplate="{StaticResource _carDetailViewTemplate}" Content="{Binding Path=SelectedItem, ElementName=_listBoxCars}"></UserControl>  
        <ListBox x:Name="_listBoxCars" Width="180" Margin="5,0" ItemTemplate="{StaticResource _carListItemViewTemplate}"></ListBox>  
    </StackPanel>  
</Window>  

代碼對於初學者來講有點長可是結構很是簡單。其中最重要的有兩句:
ContentTemplate="{StaticResource _carDatialViewTemplate}",至關於給一個普通的UserControl穿上了一件外衣、讓Car數據以圖文並茂的方式展示出來。這件外衣就是x:Key="_carDatialViewTemplate"標記的DataTemplate資源。
ItemTemplate="{StaticResource _listBoxCars}",把每一件數據的外衣交給ListBox,當ListBox的ItemSource被賦值的時候,ListBox就會爲每一個條目穿上這件外衣。這件外衣是以x:Key="_listBoxCars"標記的DataTemplate資源。
由於再也不使用事件驅動,並且爲數據穿衣服的事也已經自動完成,因此後臺的C#代碼就很是的簡單。窗體的C#代碼就只剩下這些:

/// <summary>  
/// wnd112.xaml 的交互邏輯  
/// </summary>  
public partial class wnd112 : Window  
{  
    List<Car> _carList;  
    public wnd112()  
    {  
        InitializeComponent();  
  
        _carList = new List<Car>()  
        {  
            new Car(){Name = "Aodi1", ImagePath=@"/Resources/Images/Aodi.jpg", Automarker=@"/Resources/Images/01077_1.png", Year="1990"},  
            new Car(){Name = "Aodi2", ImagePath=@"/Resources/Images/Aodi.png", Automarker=@"/Resources/Images/01077_1.png", Year="2001"},  
        };  
  
        _listBoxCars.ItemsSource = _carList;  
    }  
}  

運行程序,效果以下圖:

3. 控件的外衣ControlTemplate

    往往提到ControlTemplate我都會想到「披着羊皮的狼」這句話-----披上羊皮以後,雖然看上去像只羊,但其行爲仍然是匹狼。狼的行爲指的是它能吃別的動物、對着滿月嚎叫等事情,控件也有本身的行爲,好比顯示數據、執行方法、激發事件等。控件的行爲要靠編程邏輯來實現,因此也能夠把控件的行爲稱爲控件的算法內容。舉個例子,WPF中的CheckBox與其基類ToggleButton的功能幾乎徹底同樣,但外觀差異上卻很是的大,這就是更換ControlTemplate的結果。通過更換ControlTemplate,咱們不但能夠製做披着CheckBox外衣的ToggleButton,還能製做披着溫度計外衣的ProgressBar控件。
注意:
實際項目中,ControlTemplate主要有兩大用武之地:
經過更換ControlTemplate來更換控件的外觀,使之具備更優的用戶體驗和外觀。
藉助ControlTemplate,程序員和設計師能夠並行工做,程序員可使用WPF標準控件進行編程,等設計師的工做完成以後,只須要把新的ControlTemplate應用的程序中便可。

    ItemsControl具備一個名爲ItemsPanel的屬性,它的數據類型是ItemsPanelTemplate。ItemsPanelTemplate也是一種控件Template,它的做用是可讓程序員能夠控制ItemsControl的條目容器。

舉例而言,在咱們的印象中ListBox中的條目都是至上而下排列的,若是客戶要求咱們作一個水平排列的ListBox怎麼辦呢?WPF以前,咱們只能重寫控件比較底層的方法和屬性,而如今咱們只須要調整ListBox的ItemsPanel屬性。

<Window x:Class="WpfApplication11.wnd1132"  
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
        Title="wnd1132" Height="80" Width="300">  
    <Grid>  
        <ListBox>  
            <!--條目容器-->  
            <ListBox.ItemsPanel>  
                <ItemsPanelTemplate>  
                    <StackPanel Orientation="Horizontal"></StackPanel>  
                </ItemsPanelTemplate>  
            </ListBox.ItemsPanel>  
            <!--條目元素-->  
            <TextBlock Text="Allan"></TextBlock>  
            <TextBlock Text="Allan2"></TextBlock>  
            <TextBlock Text="Allan3"></TextBlock>  
            <TextBlock Text="Allan4"></TextBlock>  
        </ListBox>  
    </Grid>  
</Window>  

條目就會包裝在一個水平排列的StackPanel中,從而橫向排列,以下圖所示:

 

原文地址:http://www.javashuo.com/article/p-ompuaevu-cu.html

相關文章
相關標籤/搜索