天天看點

WPF自定義控件01:FlatButton

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項目的引用。具體如下所示:

WPF自定義控件01:FlatButton

此項目中,已經建立了一些檔案,下面将分重點進行說明。

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,這個是預設的控件樣式定義的檔案。建立的界面如下:

WPF自定義控件01:FlatButton

修改檔案名為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界面上,如下所示:

WPF自定義控件01:FlatButton

選中某個FlatButton控件,可以在屬性視窗中進行屬性設定,其中也包含自定義的屬性,示例如下:

WPF自定義控件01: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>      

第五步,編譯運作,主界面如下:

WPF自定義控件01:FlatButton

繼續閱讀