WPF教程十一:簡單了解并使用控制元件模板
這一章梳理控制元件模板,每個WPF控制元件都設計成無外觀的,但是行為設計上是不允許改變的,比如使用Button的控制元件時,按鈕提供了能被點擊的內容,那么自由的改變控制元件外觀時,Button作為按鈕的本質是沒有改變的,同樣回應的是按鈕的邏輯,所以我們使用控制元件模板主要也是為了修改對應控制元件的顯示內容,這個比樣式資源和觸發器可改變的內容更多,同時控制元件模板也能結合樣式和觸發器自定義更多的顯示內容,
我平時寫控制元件模板的時候,我喜歡打開Blend然后再Blend下通過滑鼠直接生成對應的模板,但是這里是了解,所以我們使用代碼的方式來刨析模板下都有什么,這樣會更深入的了解控制元件模板,而不是只會用Blend,Blend是一個很方便的工具,但是細節我們還是要關注一下的,
我們通過創建一個沒有任何修改的Button和一個設定了最小代碼量的控制元件模板來觀察2個的Button結構有什么不一致,來理解模板的作用,
? 復制以下代碼,嘗試跑起來觀察一下,代碼如下:
<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:WPFControlTemplate"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<ControlTemplate x:Key="CustomDefaultButtonTemplate" TargetType="Button">
<Border BorderBrush="Red" BorderThickness="1">
<ContentPresenter />
</Border>
</ControlTemplate>
</Window.Resources>
<Grid>
<StackPanel>
<Button Width="230" Height="30" Content="默認按鈕" Click="GetDefaultButtonControlTemplate_OnClick"/>
<TextBox Height="200" Text="{Binding TemplateDefaultContent}"/>
<Button Width="230" Height="30" Template="{StaticResource CustomDefaultButtonTemplate}" Content="設定了控制元件模板,給你展示我內部是啥樣" Click="GetCustomButtonControlTemplate_OnClick"/>
<TextBox Height="130" Text="{Binding TemplateCustomContent}"/>
</StackPanel>
</Grid>
</Window>
using System;
using System.ComponentModel;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;
using System.Xml;
namespace WPFControlTemplate
{
/// <summary>
/// MainWindow.xaml 的互動邏輯
/// </summary>
public partial class MainWindow : Window
{
TemplateContent viewmodel = new TemplateContent();
public MainWindow()
{
InitializeComponent();
this.DataContext = viewmodel;
}
private string GetControlTempalte(ControlTemplate template)
{
StringBuilder sb = new StringBuilder();
try
{
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
XmlWriter write = XmlWriter.Create(sb, settings);
XamlWriter.Save(template, write);
}
catch (Exception ex)
{
sb.Append($"Error generating template:{ex.Message}");
}
return sb.ToString();
}
private void GetCustomButtonControlTemplate_OnClick(object sender, RoutedEventArgs e)
{
var buttonTemplate = (sender as Button).Template;
var buttonTemplateStr = GetControlTempalte(buttonTemplate);
viewmodel.TemplateCustomContent = buttonTemplateStr;
}
private void GetDefaultButtonControlTemplate_OnClick(object sender, RoutedEventArgs e)
{
var buttonTemplate = (sender as Button).Template;
var buttonTemplateStr = GetControlTempalte(buttonTemplate);
viewmodel.TemplateDefaultContent = buttonTemplateStr;
}
}
public class TemplateContent : INotifyPropertyChanged
{
private string templateDefaultContent = string.Empty;
public string TemplateDefaultContent
{
get
{
return templateDefaultContent;
}
set
{
templateDefaultContent = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("TemplateDefaultContent"));
}
}
private string templateCustomContent = string.Empty;
public string TemplateCustomContent
{
get
{ return templateCustomContent; }
set
{
templateCustomContent = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("TemplateCustomContent"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}

通過復制出來上下2個ControlTemplate下的內容對比,發現默認的Button中ControlTemplate包含一個Border嵌套一個ContentPresenter其中很多屬性都系結了TemplateBinding的值,他代表的是從模板中系結,獲取使用模板的控制元件對應的控制元件值,而我們自己創建的ControlTemplate的Border則都是自己寫的BorderThickness=1和BorderBrush=#FFFF0000(紅色),值都是我們直接在模板中寫好的,所以多處參考的話,固定的這些值都是一樣的,
最近胃不好需要健康飲食,要多吃蔬菜,勤鍛煉,所以在寫這個自定義Template的話,我們通過模板讓所有使用控制元件模板的Button都帶一個一樣的蔬菜圖片(不允許更換圖片,自定義控制元件會講使用依賴項屬性系結圖片路徑,這里模板就一張一樣的蔬菜圖片),
<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:WPFControlTemplate"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<ControlTemplate x:Key="CustomDefaultButtonTemplate" TargetType="Button">
<Border BorderBrush="Red" BorderThickness="1">
<ContentPresenter />
</Border>
</ControlTemplate>
<ControlTemplate x:Key="CustomAddImageButtonTemplate" TargetType="Button" >
<Border BorderBrush="Red" BorderThickness="1">
<StackPanel Orientation="Horizontal">
<Image Height="80" Source="pack://application:,,,/Images/2.png"/>
<TextBlock Text="{TemplateBinding Content}"/>
</StackPanel>
</Border>
</ControlTemplate>
</Window.Resources>
<Grid>
<StackPanel>
<Button Width="230" Height="30" Content="默認按鈕" Click="GetDefaultButtonControlTemplate_OnClick"/>
<TextBox Height="100" Text="{Binding TemplateDefaultContent}"/>
<Button Width="230" Height="30" Template="{StaticResource CustomDefaultButtonTemplate}" Content="設定了控制元件模板,給你展示我內部是啥樣" Click="GetCustomButtonControlTemplate_OnClick"/>
<TextBox Height="100" Text="{Binding TemplateCustomContent}"/>
<StackPanel Orientation="Horizontal">
<Button Width="140" Height="80" Template="{StaticResource CustomAddImageButtonTemplate}" Content="蔬菜"/>
<Button Width="140" Height="80" Template="{StaticResource CustomAddImageButtonTemplate}" Content="健康"/>
</StackPanel>
</StackPanel>
</Grid>
</Window>
我們定義了一個CustomAddImageButtonTemplate的控制元件模板,并在里面創建了一個Images和Textblock,一個用于放置了一個蔬菜的圖片,一個是系結了template的顯示文本,模板系結和普通的資料系結類似,因為它們是專門在控制元件模板中使用的,它們只支持單向資料系結,它們可以從控制元件向模板傳遞資訊,但不能從模板向控制元件傳遞資訊,控制元件模板寫到現在這個程度有點丑,我們在控制元件模板中加一個半透效果,滑鼠移動上去圖片半透,滑鼠移開,圖片顯示,
<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:WPFControlTemplate"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<ControlTemplate x:Key="CustomDefaultButtonTemplate" TargetType="Button">
<Border BorderBrush="Red" BorderThickness="1">
<ContentPresenter />
</Border>
</ControlTemplate>
<ControlTemplate x:Key="CustomAddImageButtonTemplate" TargetType="Button" >
<Border BorderBrush="Red" BorderThickness="1">
<StackPanel Orientation="Horizontal">
<Image x:Name="img" Height="80" Source="pack://application:,,,/Images/2.png"/>
<TextBlock Text="{TemplateBinding Content}"/>
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="https://www.cnblogs.com/duwenlong/p/true">
<Setter TargetName="img" Property="Opacity" Value="https://www.cnblogs.com/duwenlong/p/0.3"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Window.Resources>
<Grid>
<StackPanel>
<Button Width="230" Height="30" Content="默認按鈕" Click="GetDefaultButtonControlTemplate_OnClick"/>
<TextBox Height="100" Text="{Binding TemplateDefaultContent}"/>
<Button Width="230" Height="30" Template="{StaticResource CustomDefaultButtonTemplate}" Content="設定了控制元件模板,給你展示我內部是啥樣" Click="GetCustomButtonControlTemplate_OnClick"/>
<TextBox Height="100" Text="{Binding TemplateCustomContent}"/>
<StackPanel Orientation="Horizontal">
<Button Width="140" Height="80" Template="{StaticResource CustomAddImageButtonTemplate}" Content="蔬菜"/>
<Button Width="140" Height="80" Template="{StaticResource CustomAddImageButtonTemplate}" Content="健康"/>
</StackPanel>
</StackPanel>
</Grid>
</Window>
這樣修改控制元件模板的話,在滑鼠經過的時候圖片就會有半透效果,
我這里寫的控制元件模板很丑,模板和樣式都可以修改外觀,樣式可以調整控制元件的屬性,但是不能重新定義外觀,但是通過控制元件模板,則可以實作這個效果,后面慢慢的視覺效果就會變得更好了,我們添加新的代碼,使用事件觸發器實作移入移出時的影片效果,修改代碼如下:
<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:WPFControlTemplate"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<ControlTemplate x:Key="CustomDefaultButtonTemplate" TargetType="Button">
<Border BorderBrush="Red" BorderThickness="1">
<ContentPresenter />
</Border>
</ControlTemplate>
<ControlTemplate x:Key="CustomAddImageButtonTemplate" TargetType="Button" >
<Border BorderBrush="Red" BorderThickness="1">
<StackPanel Orientation="Horizontal">
<Image x:Name="img" Height="80" Source="pack://application:,,,/Images/2.png"/>
<TextBlock Text="{TemplateBinding Content}"/>
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="https://www.cnblogs.com/duwenlong/p/true">
<Setter TargetName="img" Property="Opacity" Value="https://www.cnblogs.com/duwenlong/p/0.3"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<ControlTemplate TargetType="Button" x:Key="FadeAwayAnimitionButtonTemplate">
<Border BorderBrush="Blue" BorderThickness="1">
<StackPanel Orientation="Horizontal">
<Image x:Name="img" Height="80" Source="pack://application:,,,/Images/2.png"/>
<TextBlock Text="{TemplateBinding Content}"/>
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:1" To="0.3" Storyboard.TargetName="img" Storyboard.TargetProperty="Opacity" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.5" To="1" Storyboard.TargetName="img" Storyboard.TargetProperty="Opacity"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Window.Resources>
<Grid>
<StackPanel>
<Button Width="230" Height="30" Content="默認按鈕" Click="GetDefaultButtonControlTemplate_OnClick"/>
<TextBox Height="100" Text="{Binding TemplateDefaultContent}"/>
<Button Width="230" Height="30" Template="{StaticResource CustomDefaultButtonTemplate}" Content="設定了控制元件模板,給你展示我內部是啥樣" Click="GetCustomButtonControlTemplate_OnClick"/>
<TextBox Height="100" Text="{Binding TemplateCustomContent}"/>
<StackPanel Orientation="Horizontal">
<Button Width="140" Height="80" Template="{StaticResource CustomAddImageButtonTemplate}" Content="蔬菜"/>
<Button Width="140" Height="80" Template="{StaticResource CustomAddImageButtonTemplate}" Content="健康"/>
<Button Width="140" Height="80" Template="{StaticResource FadeAwayAnimitionButtonTemplate}" Content="影片"/>
</StackPanel>
</StackPanel>
</Grid>
</Window>
這樣就實作了移入移出時的影片效果,

注意:因為同樣一種控制元件在程式中為了顯示不同的外觀,可能有對應的N個控制元件模板,為了不讓代碼現得雜亂無章,可以把每個型別的控制元件對應的模板都歸類到一個資源字典中,比如Button.xaml、TextBox.xaml請注意這個添加的是Resource Dictionary,不是WPF表單,因為如果混在一起,內容一旦太多,無法找你到要的控制元件模板,記得再程式集下的ResourceDictionary下添加你的資源檔案,同樣的如果控制元件模板內,有使用的畫刷,建議也單獨拿出來,單獨存放在這種資源下的Button.xaml或者TextBox.xaml,
? 這里寫一個重要的知識點,在樣式中使用控制元件模板,并重新設定控制元件模板的部分內容樣式,我們修改了FadeAwayAnimitionButtonTemplate控制元件模板的border的顏色和粗細使用系結,然后在資源下使用模板,并在樣式下修改模板border的粗細和顏色,我們修改資源CustomButtonStyle,樣式中使用模板可以更高效的設計好的控制元件樣式和行為,在樣式中使用模板代碼修改如下:
<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:WPFControlTemplate"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<ControlTemplate x:Key="CustomDefaultButtonTemplate" TargetType="Button">
<Border BorderBrush="Red" BorderThickness="1">
<ContentPresenter />
</Border>
</ControlTemplate>
<ControlTemplate x:Key="CustomAddImageButtonTemplate" TargetType="Button" >
<Border BorderBrush="Red" BorderThickness="1">
<StackPanel Orientation="Horizontal">
<Image x:Name="img" Height="80" Source="pack://application:,,,/Images/2.png"/>
<TextBlock Text="{TemplateBinding Content}"/>
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="https://www.cnblogs.com/duwenlong/p/true">
<Setter TargetName="img" Property="Opacity" Value="https://www.cnblogs.com/duwenlong/p/0.3"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<ControlTemplate TargetType="Button" x:Key="FadeAwayAnimitionButtonTemplate">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<StackPanel Orientation="Horizontal">
<Image x:Name="img" Height="80" Source="pack://application:,,,/Images/2.png"/>
<TextBlock Text="{TemplateBinding Content}"/>
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:1" To="0.3" Storyboard.TargetName="img" Storyboard.TargetProperty="Opacity" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.5" To="1" Storyboard.TargetName="img" Storyboard.TargetProperty="Opacity"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<Style x:Key="CustomButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Control.Template" Value="https://www.cnblogs.com/duwenlong/p/{StaticResource FadeAwayAnimitionButtonTemplate}"></Setter>
<Setter Property="BorderBrush" Value="https://www.cnblogs.com/duwenlong/p/Orange"/>
<Setter Property="BorderThickness" Value="https://www.cnblogs.com/duwenlong/p/3"/>
<Setter Property="FontSize" Value="https://www.cnblogs.com/duwenlong/p/22"/>
<Setter Property="FontWeight" Value="https://www.cnblogs.com/duwenlong/p/Bold"/>
</Style>
</Window.Resources>
<Grid>
<StackPanel>
<Button Width="230" Height="30" Content="默認按鈕" Click="GetDefaultButtonControlTemplate_OnClick"/>
<TextBox Height="100" Text="{Binding TemplateDefaultContent}"/>
<Button Width="230" Height="30" Template="{StaticResource CustomDefaultButtonTemplate}" Content="設定了控制元件模板,給你展示我內部是啥樣" Click="GetCustomButtonControlTemplate_OnClick"/>
<TextBox Height="100" Text="{Binding TemplateCustomContent}"/>
<StackPanel Orientation="Horizontal">
<Button Width="140" Height="80" Template="{StaticResource CustomAddImageButtonTemplate}" Content="蔬菜"/>
<Button Width="140" Height="80" Template="{StaticResource CustomAddImageButtonTemplate}" Content="健康"/>
<Button Width="140" Height="80" BorderBrush="Red" BorderThickness="1" Template="{StaticResource FadeAwayAnimitionButtonTemplate}" Content="影片"/>
<Button Width="280" Height="80" Style="{StaticResource CustomButtonStyle}" Content="樣式參考控制元件模板并修改"/>
</StackPanel>
</StackPanel>
</Grid>
</Window>
在樣式中不包含模板,只是參考模板,所以在樣式中不能深入到可視化層去修改對應的元素,所以在設計主題或者控制元件庫的時候再來仔細分析這個問題,
這篇就寫這么多主要內容把,這章本來是要重點寫的,但是腦袋最近確實太迷糊了,最近鬧心的事情太多太多了,但是不想傳播負能量,不想抱怨,自己這幾天沒有寫博客的狀態,進度一拖再拖,但是任何讓你覺得不好的事情不是都已經發生了嗎,未來要充滿希望和動力呀,一起加油各位,
重點來啦!!
1)如果你把上面的代碼敲了一遍,但是你如果跟我一樣RoutedEvent="MouseLeave",Trigger Property="IsMouseOver" Value="https://www.cnblogs.com/duwenlong/p/true",后面的這些屬性名、事件名不知道怎么寫的時候,是不是一樣懵逼,告訴你一個我發現的技巧,跳轉到你要設定的物件元素上比如Button 使用F12跳轉過去,如果是屬性一般就有了,事件如果沒有的話,去看自己繼承時的父類,需要的屬性和事件一般也都在里面,
2)另外一個重點,使用Blend,可以快速的創建當前模板,然后再修改成自己需要的,具體用法就是再Blend下,右鍵對應的元素編輯模板,創建,就會出來一大串啦,
3)集合類的元素有一個容器樣式、子元素樣式,都可以定制,這里需要你自己去實驗了,我這里就不寫了,原理是一樣的,只是模板對應的內容不一樣,集合類的是容器和子元素2個單獨創建的的模板,可以自己嘗試去百度搜索一下“Blend編輯WPF模板”

這章就寫這么多拉,感謝觀看博文的新人或大佬們,很多時候雖然人生處在低谷,但是總要有些東西需要自己堅持并努力下去,不是嗎,加油!
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/279152.html
標籤:WPF
