1 WPF概述
根據百度百科,WPF是Windows Presentation Foundation的縮寫,它是微軟推出的基于Windows 的新一代UI架構,屬于.NET Framework 3.0+的一部分。WPF提供了統一的程式設計模型、語言和架構,真正做到了UI與邏輯的分離,同時也提供了全新的多媒體互動使用者圖形界面。借助WPF,可以讓開發人員開發出絢麗的應用程式。
環境準備:Visual Studio 2019社群版(免費);.NET FrameWork 5 。
2 WPF項目建立
首先需要利用Visual Studio 2019社群版建立一個新的WPF桌面應用WpfControls和一個新的WPF控件庫項目Yd.WpfControls。并在WpfControls項目中添加對Yd.WpfControls項目的引用。具體如下所示:

此項目中,已經建立了一些檔案,下面将分重點進行說明。
3 自定義控件FlatButton
第一步,定義一些基礎的類,其中有字型的設定FlatFonts.cs,其代碼如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;
namespace Yd.WpfControls
{
public class FlatFonts
{
public static FontFamily contentFontFamily = new FontFamily("Microsoft YaHei");
public static double contentFontSize = 16D;
}
}
其中就是定義了一個預設的字型和字号,注意字号是double類型的。同樣的,下面給出顔色的定義,FlatColors.cs示例代碼如下:
using System;
using System.Collections.Generic;
using System.Windows.Media;
namespace Yd.WpfControls
{
public class FlatColors
{
//https://flatuicolors.com/palette/defo
public static Brush TUROUOISE = new SolidColorBrush( Color.FromRgb(26, 188, 156));
public static Brush EMERALD = new SolidColorBrush(Color.FromRgb(46, 204, 113));
public static Brush PETER_RIVER = new SolidColorBrush(Color.FromRgb(52, 152, 219));
public static Brush BELIZE_HOLE = new SolidColorBrush(Color.FromRgb(41, 128, 185));
public static Brush MIDNIGHT_BLUE = new SolidColorBrush(Color.FromRgb(44, 62, 80));
public static Brush CLOUDS = new SolidColorBrush(Color.FromRgb(236, 240, 241));
}
}
注意:顔色是來自Flat UI Color系列的網站,且顔色定義的類型的Brush,而不是Color。下面再給出FlatButton的Type定義,FlatButtonType.cs示例代碼如下:
namespace Yd.WpfControls
{
public enum FlatButtonType
{
Default,
Warn,
Danger
}
}
第二步,建立一個自定義控件,他會自動建立一個樣式Generic.xaml,這個是預設的控件樣式定義的檔案。建立的界面如下:
修改檔案名為FlatButton,并繼承自Button控件,FlatButton.cs示例代碼如下:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace Yd.WpfControls
{
public class FlatButton : Button
{
static FlatButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(FlatButton),
new FrameworkPropertyMetadata(typeof(FlatButton)));
}
//自定義控件屬性ButtonType,其DependencyProperty為ButtonType+Property
public static readonly DependencyProperty ButtonTypeProperty =
DependencyProperty.Register("ButtonType", typeof(FlatButtonType), typeof(FlatButton),
new PropertyMetadata(FlatButtonType.Default));
public FlatButtonType ButtonType
{
get { return (FlatButtonType)GetValue(ButtonTypeProperty); }
set { SetValue(ButtonTypeProperty, value); }
}
public static readonly DependencyProperty MouseOverBackgroundProperty =
DependencyProperty.Register("MouseOverBackground", typeof(Brush), typeof(FlatButton),
new PropertyMetadata(Brushes.RoyalBlue));
public Brush MouseOverBackground
{
get { return (Brush)GetValue(MouseOverBackgroundProperty); }
set { SetValue(MouseOverBackgroundProperty, value); }
}
public static readonly DependencyProperty MouseOverForegroundProperty =
DependencyProperty.Register("MouseOverForeground", typeof(Brush), typeof(FlatButton),
new PropertyMetadata(Brushes.White));
public Brush MouseOverForeground
{
get { return (Brush)GetValue(MouseOverForegroundProperty); }
set { SetValue(MouseOverForegroundProperty, value); }
}
public static readonly DependencyProperty CornerRadiusProperty =
DependencyProperty.Register("CornerRadius", typeof(CornerRadius), typeof(FlatButton),
new PropertyMetadata(new CornerRadius(2)));
public CornerRadius CornerRadius
{
get { return (CornerRadius)GetValue(CornerRadiusProperty); }
set { SetValue(CornerRadiusProperty, value); }
}
}
}
FlatButton的UI定義基本都是靠樣式定義檔案FlatButton.xaml,其類似于CSS,但是文法描述不同。FlatButton.xaml位于Style目錄下,其示例代碼如下:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Yd.WpfControls">
<Style TargetType="{x:Type local:FlatButton}">
<Setter Property="FontSize" Value="{x:Static local:FlatFonts.contentFontSize}"/>
<Setter Property="FontFamily" Value="{x:Static local:FlatFonts.contentFontFamily}"/>
<Style.Triggers>
<Trigger Property="ButtonType" Value="Default">
<Setter Property="Background" Value="{x:Static local:FlatColors.PETER_RIVER}"/>
<Setter Property="Foreground" Value="{x:Static local:FlatColors.CLOUDS}"/>
<Setter Property="MouseOverBackground" Value="{x:Static local:FlatColors.BELIZE_HOLE}"/>
<Setter Property="MouseOverForeground" Value="{x:Static local:FlatColors.CLOUDS}"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:FlatButton}">
<Border x:Name="border" Background="{TemplateBinding Background}" CornerRadius="{TemplateBinding CornerRadius}" BorderThickness="{TemplateBinding BorderThickness}" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" SnapsToDevicePixels="True">
<TextBlock x:Name="text" Text="{TemplateBinding Content}" Foreground="{TemplateBinding Foreground}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="Background" Value="{Binding MouseOverBackground,RelativeSource={RelativeSource TemplatedParent}}"/>
<Setter TargetName="text" Property="Foreground" Value="{Binding MouseOverForeground,RelativeSource={RelativeSource TemplatedParent}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="ButtonType" Value="Warn">
<Setter Property="Background" Value="#f1c40f"/>
<Setter Property="MouseOverBackground" Value="#f39c12"/>
<Setter Property="Foreground" Value="{x:Static local:FlatColors.CLOUDS}"/>
<Setter Property="MouseOverForeground" Value="#ecf0f1"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:FlatButton}">
<Border x:Name="border" Background="{TemplateBinding Background}" CornerRadius="{TemplateBinding CornerRadius}" BorderThickness="{TemplateBinding BorderThickness}" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" SnapsToDevicePixels="True">
<TextBlock x:Name="text" Text="{TemplateBinding Content}" Foreground="{TemplateBinding Foreground}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="Background" Value="{Binding MouseOverBackground,RelativeSource={RelativeSource TemplatedParent}}"/>
<Setter TargetName="text" Property="Foreground" Value="{Binding MouseOverForeground,RelativeSource={RelativeSource TemplatedParent}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="ButtonType" Value="Danger">
<Setter Property="Background" Value="#c0392b"/>
<Setter Property="MouseOverBackground" Value="#e74c3c"/>
<Setter Property="Foreground" Value="{x:Static local:FlatColors.CLOUDS}"/>
<Setter Property="MouseOverForeground" Value="#ecf0f1"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:FlatButton}">
<Border x:Name="border" Background="{TemplateBinding Background}" CornerRadius="{TemplateBinding CornerRadius}" BorderThickness="{TemplateBinding BorderThickness}" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" SnapsToDevicePixels="True">
<TextBlock x:Name="text" Text="{TemplateBinding Content}" Foreground="{TemplateBinding Foreground}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="Background" Value="{Binding MouseOverBackground,RelativeSource={RelativeSource TemplatedParent}}"/>
<Setter TargetName="text" Property="Foreground" Value="{Binding MouseOverForeground,RelativeSource={RelativeSource TemplatedParent}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
此樣式定義中,首先需要明确樣式是針對哪個控件的,其用如下指令指定:
<Style TargetType="{x:Type local:FlatButton}">
而local這個命名空間是在 xmlns:local="clr-namespace:Yd.WpfControls" 進行定義的,這個需要手動添加,否則内部找不到FlatButton空間。在樣式表中,可以對屬性進行綁定,其中的屬性可以是自定義的屬性,比如:
<Trigger Property="ButtonType" Value="Default">
也可以綁定到靜态類對象中,比如前面定義的顔色筆刷和字型對象,如:
<Setter Property="Background" Value="{x:Static local:FlatColors.PETER_RIVER}"/>
<Setter Property="FontSize" Value="{x:Static local:FlatFonts.contentFontSize}"/>
<Setter Property="FontFamily" Value="{x:Static local:FlatFonts.contentFontFamily}"/>
第三步,将樣式FlatButton.xaml添加到預設的Generic.xaml中,否則則UI無法加載樣式定義資訊,Generic.xaml其代碼如下:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Yd.WpfControls">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Yd.WpfControls;component/Style/FlatButton.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
注意:ResourceDictionary Source的定義中,/Yd.WpfControls;component/+樣式表路徑,這個字首不可少。
第四步,編譯後,可以将自定義控件添加到UI界面上,如下所示:
選中某個FlatButton控件,可以在屬性視窗中進行屬性設定,其中也包含自定義的屬性,示例如下:
主界面視窗的示例代碼如下:
<Window
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:WpfControls="clr-namespace:Yd.WpfControls;assembly=Yd.WpfControls"
x:Class="WpfControls.MainWindow"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<WpfControls:FlatButton Content="确定" HorizontalAlignment="Left" Height="37" Margin="89,83,0,0" VerticalAlignment="Top" ButtonType="Default" CornerRadius="18" Width="120" FontFamily="Microsoft YaHei" FontSize="16"/>
<WpfControls:FlatButton Content="取消" HorizontalAlignment="Left" Margin="412,88,0,0" VerticalAlignment="Top" Height="32" ButtonType="Warn" CornerRadius="18" Width="120" FontFamily="Microsoft YaHei" FontSize="16" Click="FlatButton_Click"/>
<WpfControls:FlatButton Content="删除" HorizontalAlignment="Left" Margin="249,88,0,0" VerticalAlignment="Top" Height="32" ButtonType="Danger" CornerRadius="0" Width="120" x:Name="btn01" FontFamily="Microsoft YaHei" FontSize="16"/>
<WpfControls:FlatButton Content="FlatButton" HorizontalAlignment="Left" Margin="72,0,0,0" VerticalAlignment="Center" Height="37" Width="137"/>
</Grid>
</Window>
第五步,編譯運作,主界面如下: