資料模板
資料模板是一段如何顯示系結在VM物件的XAML代碼,資料模板可以包含任意元素的組合,基于Binding來顯示不同的資訊,
在實際的開發中資料模板的應用場景很多,同樣一個控制元件可以根據不同的系結源,根據以設定好的資料模板可以顯示對應的不同的內容,
很多人用不好控制元件模板和資料模板,覺得有點混亂,大部分都是在追求7天入門WPF,或者直接就問有沒有快速解決我目前問題的辦法,等等,我也沒有,但是資料模板其實比控制元件模板更好寫,因為他的思路比較簡單,就是這個資料,通過Binding后,界面上要顯示成什么樣,就這樣,然后我們基于這個理解來延伸內容,
首先創建一個用于顯示串列的ListBox我們模仿商店出售的商品,為了便于理解,所有的布局我都使簡單的grid分割不涉及其他布局,
<ListBox Name="ProductsListBox" MaxWidth="290" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Border Margin="5" CornerRadius="5" BorderThickness="1" BorderBrush="SteelBlue">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Margin="3" FontWeight="Bold" Text="{Binding Path=Name}"/>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="單價:"/>
<TextBlock Grid.Column="1" Text="{Binding Path=Price}"/>
</Grid>
<Grid Margin="1,0" Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="剩余數量:"/>
<TextBlock Grid.Column="1" Text="{Binding Path=Number}"/>
</Grid>
</Grid>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
我們在ListBox中定義了ItemTemplate的DataTemplate,DataTemplate會再ListBox的ItemsSource系結資料源后,拿出在DataTemplate中Binding的值,然后按照我們當前得DataTemplate來渲染,
我們定義在ItemTemplate下的資料模板,就是修改ListBox中的子物件如何顯示,
完成代碼如下:
xaml代碼
<Window x:
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:HowtoUseDataTemplate"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="6*"/>
</Grid.ColumnDefinitions>
<ListBox Name="ProductsListBox" MaxWidth="290" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Border Margin="5" CornerRadius="5" BorderThickness="1" BorderBrush="SteelBlue">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Margin="3" FontWeight="Bold" Text="{Binding Path=Name}"/>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="單價:"/>
<TextBlock Grid.Column="1" Text="{Binding Path=Price}"/>
</Grid>
<Grid Margin="1,0" Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="剩余數量:"/>
<TextBlock Grid.Column="1" Text="{Binding Path=Number}"/>
</Grid>
</Grid>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Border Margin="3" DataContext="{Binding ElementName=ProductsListBox,Path=SelectedItem}" Grid.Column="1" BorderBrush="AntiqueWhite" BorderThickness="1" CornerRadius="3">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Text="名稱:"/>
<TextBlock Grid.Column="1" Text="{Binding Name}"/>
<TextBlock Grid.Row="1" Text="單價:"/>
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Price}"/>
<TextBlock Grid.Row="2" Text="數量:"/>
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Number}"/>
<TextBlock Grid.Row="3" Text="介紹:"/>
<TextBlock Grid.Row="3" Grid.Column="1" Text="{Binding Introduce}"/>
</Grid>
</Border>
</Grid>
</Window>
cs代碼如下:
using System.Collections.Generic;
using System.Windows;
namespace HowtoUseDataTemplate
{
/// <summary>
/// MainWindow.xaml 的互動邏輯
/// </summary>
public partial class MainWindow : Window
{
List<Product> products;
public MainWindow()
{
InitializeComponent();
products = new List<Product>()
{
new Product(){Name="火箭彈",Price=1000.00M, Number=5000,Introduce="這玩意要買大量的,便宜又方便、一次齊發,基本上就完成任務了,就是誤差有點大,"},
new Product(){Name="自動步槍(洗掉敏感字眼)",Price=300.00M, Number=5000 ,Introduce="單兵裝備"},
new Product(){ Name="爆震彈",Price=50.00M, Number=5000 ,Introduce="單兵裝備"},
new Product(){Name="防彈衣",Price=100.00M, Number=5000,Introduce="單兵裝備"},
new Product(){ Name="防彈頭盔",Price=80.00M, Number=5000,Introduce="單兵裝備"},
new Product(){ Name="特別特別硬之真防彈頭盔",Price=99999.00M, Number=5000,Introduce="單兵裝備名字特別長"},
};
ProductsListBox.ItemsSource = products;
}
}
public class Product
{
public int Number { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string Introduce { get; set; }
}
}

資料模板中還有一些常用比較方便的功能,比如資料觸發器,和轉換器,之前講過,在之前講了屬性觸發器、事件觸發器,但是資料觸發器沒講,這里講一下,
(洗掉敏感字眼) 舉個例子,火箭彈熱賣了,我們虛擬出來的軟體產品既然包含了這些,產品經理就提出來要添加一個熱賣產品的提醒功能,在產品名稱旁邊添加一個熱賣品的紅色hot斜體的文字顯示,同時文字也要變成紅色,
回想到我們剛才的資料模板,使用資料觸發器來實作熱賣品顯示hot功能,在物體中添加是否是熱賣品屬性,實作轉換器、實作資料觸發器,
1)再工程下添加Converter檔案夾
撰寫HotBoolToVisibilityConverter類,代碼如下:
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace HowtoUseDataTemplate.Converter
{
public class HotBoolToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool b)
{
if (b)
{
return Visibility.Visible;
}
}
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
回到主表單找到Priduct類,添加IsHot屬性,設定默認值為false,
再Products中設定每個物件是否是熱賣,完整代碼如下:
using System.Collections.Generic;
using System.Windows;
namespace HowtoUseDataTemplate
{
/// <summary>
/// MainWindow.xaml 的互動邏輯
/// </summary>
public partial class MainWindow : Window
{
List<Product> products;
public MainWindow()
{
InitializeComponent();
products = new List<Product>()
{
new Product(){Name="火箭彈",IsHot=true, Price=1000.00M, Number=5000,Introduce="這玩意要買大量的,便宜又方便、一次齊發,基本上就完成任務了,就是誤差有點大,"},
new Product(){Name="自動步槍(洗掉敏感字眼)",Price=300.00M, Number=5000 ,Introduce="單兵裝備"},
new Product(){ Name="爆震彈",Price=50.00M, Number=5000 ,Introduce="單兵裝備"},
new Product(){Name="防彈衣",Price=100.00M, Number=5000,Introduce="單兵裝備"},
new Product(){ Name="防彈頭盔",Price=80.00M, Number=5000,Introduce="單兵裝備"},
new Product(){ Name="特別特別硬之真防彈頭盔",Price=99999.00M, Number=5000,Introduce="單兵裝備名字特別長"},
};
ProductsListBox.ItemsSource = products;
}
}
public class Product
{
public int Number { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string Introduce { get; set; }
public bool IsHot { get; set; } = false;
}
}
最后回到主表單的XAML檔案下,添加轉換器的參考和資源,
xmlns:converter="clr-namespace:HowtoUseDataTemplate.Converter"
<Window.Resources>
<converter:HotBoolToVisibilityConverter x:Key="HotBoolToVisibilityConverter"/>
</Window.Resources>
找到資料模板中的商品名稱,重新設計Grid布局并添加一個hot文本控制元件,
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="NameTextBlock" Margin="3" FontWeight="Bold" Text="{Binding Path=Name}"/>
<TextBlock Margin="0,2,2,2" Visibility="{Binding IsHot,Converter={StaticResource HotBoolToVisibilityConverter}}" FontSize="10" Grid.Column="1" FontStyle="Italic" Text="Hot" Foreground="Red"/>
</Grid>
寫一個資料觸發器,當IsHot等于true的時候,去找模板中Name=NameText的控制元件,設定他的Foreground顏色屬性為紅色,
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=IsHot}" Value="https://www.cnblogs.com/duwenlong/p/True">
<Setter Property="TextBlock.Foreground" TargetName="NameTextBlock" Value="https://www.cnblogs.com/duwenlong/p/Red"/>
</DataTrigger>
</DataTemplate.Triggers>
XAML完整代碼如下:
<Window x:
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:converter="clr-namespace:HowtoUseDataTemplate.Converter"
xmlns:local="clr-namespace:HowtoUseDataTemplate"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<converter:HotBoolToVisibilityConverter x:Key="HotBoolToVisibilityConverter"/>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="6*"/>
</Grid.ColumnDefinitions>
<ListBox Name="ProductsListBox" MaxWidth="290" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=IsHot}" Value="https://www.cnblogs.com/duwenlong/p/True">
<Setter Property="TextBlock.Foreground" TargetName="NameTextBlock" Value="https://www.cnblogs.com/duwenlong/p/Red"/>
</DataTrigger>
</DataTemplate.Triggers>
<Border Margin="5" CornerRadius="5" BorderThickness="1" BorderBrush="SteelBlue">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="NameTextBlock" Margin="3" FontWeight="Bold" Text="{Binding Path=Name}"/>
<TextBlock Margin="0,2,2,2" Visibility="{Binding IsHot,Converter={StaticResource HotBoolToVisibilityConverter}}" FontSize="10" Grid.Column="1" FontStyle="Italic" Text="Hot" Foreground="Red"/>
</Grid>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="單價:"/>
<TextBlock Grid.Column="1" Text="{Binding Path=Price}"/>
</Grid>
<Grid Margin="1,0" Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="剩余數量:"/>
<TextBlock Grid.Column="1" Text="{Binding Path=Number}"/>
</Grid>
</Grid>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Border Margin="3" DataContext="{Binding ElementName=ProductsListBox,Path=SelectedItem}" Grid.Column="1" BorderBrush="AntiqueWhite" BorderThickness="1" CornerRadius="3">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Text="名稱:"/>
<TextBlock Grid.Column="1" Text="{Binding Name}"/>
<TextBlock Grid.Row="1" Text="單價:"/>
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Price}"/>
<TextBlock Grid.Row="2" Text="數量:"/>
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Number}"/>
<TextBlock Grid.Row="3" Text="介紹:"/>
<TextBlock Grid.Row="3" Grid.Column="1" Text="{Binding Introduce}"/>
</Grid>
</Border>
</Grid>
</Window>
效果如下,這樣我們就實作了熱賣的功能,通過資料模板只修改了很少一部分內容,

我們去看上面的XAML代碼,我們現在只有一個簡單的ListBox重寫樣式,就這么多內容需要寫,那如果界面上內容特別多,代碼不是很難讀嗎,我們接下來把代碼拆出去,把DataTemplate寫到當前表單的Resources中,然后再ListBox中設定ItemTemplate等于我們再資源中定義的資料模板,代碼如下:
<Window x:
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:converter="clr-namespace:HowtoUseDataTemplate.Converter"
xmlns:local="clr-namespace:HowtoUseDataTemplate"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<converter:HotBoolToVisibilityConverter x:Key="HotBoolToVisibilityConverter"/>
<DataTemplate x:Key="ProductItemTemplate">
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=IsHot}" Value="https://www.cnblogs.com/duwenlong/p/True">
<Setter Property="TextBlock.Foreground" TargetName="NameTextBlock" Value="https://www.cnblogs.com/duwenlong/p/Red"/>
</DataTrigger>
</DataTemplate.Triggers>
<Border Margin="5" CornerRadius="5" BorderThickness="1" BorderBrush="SteelBlue">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="NameTextBlock" Margin="3" FontWeight="Bold" Text="{Binding Path=Name}"/>
<TextBlock Margin="0,2,2,2" Visibility="{Binding IsHot,Converter={StaticResource HotBoolToVisibilityConverter}}" FontSize="10" Grid.Column="1" FontStyle="Italic" Text="Hot" Foreground="Red"/>
</Grid>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="單價:"/>
<TextBlock Grid.Column="1" Text="{Binding Path=Price}"/>
</Grid>
<Grid Margin="1,0" Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="剩余數量:"/>
<TextBlock Grid.Column="1" Text="{Binding Path=Number}"/>
</Grid>
</Grid>
</Grid>
</Border>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="6*"/>
</Grid.ColumnDefinitions>
<ListBox Name="ProductsListBox" MaxWidth="290" HorizontalContentAlignment="Stretch" ItemTemplate="{StaticResource ProductItemTemplate}">
</ListBox>
<Border Margin="3" DataContext="{Binding ElementName=ProductsListBox,Path=SelectedItem}" Grid.Column="1" BorderBrush="AntiqueWhite" BorderThickness="1" CornerRadius="3">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Text="名稱:"/>
<TextBlock Grid.Column="1" Text="{Binding Name}"/>
<TextBlock Grid.Row="1" Text="單價:"/>
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Price}"/>
<TextBlock Grid.Row="2" Text="數量:"/>
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Number}"/>
<TextBlock Grid.Row="3" Text="介紹:"/>
<TextBlock Grid.Row="3" Grid.Column="1" Text="{Binding Introduce}"/>
</Grid>
</Border>
</Grid>
</Window>
這樣Grid下的內容就少了很多,結構就清晰了一些,不建議把DataTemplate放到單獨的檔案夾中,因為如果App初始化的時候就加載了這些內容的話,可能targetType會影響到所有的使用了這個類的物件,但是其實也有x:key這個可以控制,所以這里具體看需要把,覺得怎么合適怎么來,反正主要的目標都是解耦,
再上面點擊更換Item的時候有個比較討厭的地方,選中的物件有一個藍色的底色改變了,這個是ItemContainerSytle的內容,修改ListBox代碼如下:
<ListBox Name="ProductsListBox" MaxWidth="290" HorizontalContentAlignment="Stretch" ItemTemplate="{StaticResource ProductItemTemplate}">
<ListBox.ItemContainerStyle>
<Style>
<Setter Property="ItemsControl.Padding" Value="https://www.cnblogs.com/duwenlong/p/0"/>
<Style.Triggers>
<Trigger Property="ListBoxItem.IsSelected" Value="https://www.cnblogs.com/duwenlong/p/True">
<Setter Property="ListBoxItem.Background" Value="https://www.cnblogs.com/duwenlong/p/#84C1FF"/>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
上面的資料模板中最外層的Border 外面嵌套一層Grid 同時背景色設定為和Listbox背景色一樣,就可以拉,
這篇就寫這么多,到這篇為止就打算基礎部分結束了,下篇講MVVM,IOC,就開始進入財務軟體的專案實戰,因為剩下一些沒講的章節,我整理了一下,大概是Page、Window、控制元件、TreeView、DataGrid等等,我覺得講控制元件不屬于入門知識,因為控制元件實際使用中包含了VM、資料模板、控制元件模板、Style還有更加深入的串列虛擬化、和資料虛擬化等等,還有怎么除錯并分析問題等等,所以這里就不打算講了,以后作為WPF技巧相關的文章,去梳理這些知識點,
我創建了一個C#相關的交流群,用于分享學習資料和討論問題,歡迎有興趣的小伙伴:QQ群:542633085
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/285420.html
標籤:WPF
上一篇:dotnet 讀 WPF 源代碼筆記 插入觸摸設備的初始化獲取設備資訊
下一篇:WPF自定義加載控制元件
