天天看點

WPF自定義控件07:FlatTextBox

在不少的應用程式中,作為基本輸入的文本框(TextBox)是少不了的,它可以很友善擷取使用者輸入的值。本文将介紹一下自定義的文本框控件FlatTextBox,它與FlatCheckBox實作過程非常類似,它是繼承自TextBox,但使用自定義的UI樣式來美化界面,并添加了特有的一些依賴屬性。下面将詳細介紹具體的實作細節。

1 WPF項目結構

    基于之前建立的WPF示例項目,在其中建立一個新的關于FlatTextBox的項目檔案。本質上,FlatTextBox是繼承TextBox控件,利用TextBox控件自身的屬性和方法,可以減少FlatTextBox實作的難度和複雜度。首先,給出本項目檔案結構,如下圖所示:

WPF自定義控件07:FlatTextBox

其中的Fonts目錄下存放各種圖示字型檔案,Style目錄下存放各種控件的UI 樣式定義檔案,FlatComboBox.xaml就是FlatComboBox控件的樣式定義檔案。另外,需要将其注冊到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/IconFont.xaml"/>
        <ResourceDictionary Source="/Yd.WpfControls;component/Style/FlatButton.xaml"/>
        <ResourceDictionary Source="/Yd.WpfControls;component/Style/FlatCheckBox.xaml"/>
        <ResourceDictionary Source="/Yd.WpfControls;component/Style/FlatRadioButton.xaml"/>
        <ResourceDictionary Source="/Yd.WpfControls;component/Style/ToggleButton.xaml"/>
        <ResourceDictionary Source="/Yd.WpfControls;component/Style/FlatComboBox.xaml"/>
        <ResourceDictionary Source="/Yd.WpfControls;component/Style/FlatTextBox.xaml"/>
    </ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
      

2 WPF FlatTextBox實作

    首先在Yd.WpfControls項目中添加一個類FlatTextBox.cs,它繼承自TextBox控件,示例代碼如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace Yd.WpfControls
{
    public class FlatTextBox : TextBox
    {
        static FlatTextBox()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(FlatTextBox),
                new FrameworkPropertyMetadata(typeof(FlatTextBox)));
        }

        public static readonly DependencyProperty TextTypeProperty =
           DependencyProperty.Register("TextType", typeof(FlatButtonType), typeof(FlatTextBox),
               new PropertyMetadata(FlatButtonType.Default));

        public FlatButtonType TextType
        {
            get { return (FlatButtonType)GetValue(TextTypeProperty); }
            set { SetValue(TextTypeProperty, value); }
        }

        public static readonly DependencyProperty IsRequreProperty =
         DependencyProperty.Register("IsRequre", typeof(bool), typeof(FlatTextBox),
             new PropertyMetadata(false));

        public bool IsRequre
        {
            get { return (bool)GetValue(IsRequreProperty); }
            set { SetValue(IsRequreProperty, value); }
        }

        public bool IsValidate()
        {
            if ( IsRequre)
            {
                if (!string.IsNullOrEmpty(this.Text))
                {
                    TextType = FlatButtonType.Default;
                    return true;
                }
                else
                {
                    TextType = FlatButtonType.Danger;
                    return true;
                }
            }
            else
            {
                TextType = FlatButtonType.Default;
                return true;
            }

        }
    }
}
      

其中擴充了2個依賴屬性,即TextType屬性和IsRequre屬性,前者代表目前輸入框的類型,是Default狀态的,還是Warn狀态的,還是Danger狀态;而後者代表目前文本框是否為必輸,如果值是true,則當文本框值為空的時候,會顯示紅色邊框。當然這個驗證過程通過自定義的IsValidate()方法實作。

FlatTextBox控件的UI樣式主要就是依靠FlatTextBox.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">
   
    <Style TargetType="{x:Type local:FlatTextBox}">
        <Setter Property="FontSize" Value="{x:Static local:FlatFonts.contentFontSize}"/>
        <Setter Property="FontFamily" Value="{x:Static local:FlatFonts.contentFontFamily}"/>
        <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
        <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
         <Setter Property="BorderThickness" Value="2"/>
         <Setter Property="KeyboardNavigation.TabNavigation" Value="None"/>
         <Setter Property="HorizontalContentAlignment" Value="Left"/>
         <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
         <Setter Property="AllowDrop" Value="true"/>
         <Setter Property="Height" Value="28"/>
         <Setter Property="VerticalContentAlignment" Value="Center"/>
         <Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
        <Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
        <Style.Triggers>
            <Trigger Property="TextType" Value="Default">
                <Setter Property="Foreground" Value="{x:Static local:FlatColors.SILVER}"/>
                <Setter Property="BorderBrush" Value="{x:Static local:FlatColors.SILVER}"/>
                <Setter Property="BorderThickness" Value="2"/>
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type TextBox}">
                            <Border x:Name="border" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" 
                                BorderBrush="{TemplateBinding BorderBrush}" SnapsToDevicePixels="True"
                                CornerRadius="14" Padding="10 0">
                                <ScrollViewer x:Name="PART_ContentHost" Focusable="false" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
                            </Border>
                            <ControlTemplate.Triggers>
                                <Trigger Property="IsEnabled" Value="false">
                                    <Setter Property="Opacity" TargetName="border" Value="0.56"/>
                                </Trigger>
                                <Trigger Property="IsMouseOver" Value="true">
                                    <Setter Property="BorderBrush" TargetName="border" Value="{x:Static local:FlatColors.ASBESTOS}"/>
                                </Trigger>
                                <Trigger Property="IsKeyboardFocused" Value="true">
                                    <Setter Property="BorderBrush" TargetName="border" Value="{x:Static local:FlatColors.ASBESTOS}"/>
                                </Trigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Trigger>

            <Trigger Property="TextType" Value="Warn">
                <Setter Property="Foreground" Value="{x:Static local:FlatColors.SUN_FLOWER}"/>
                <Setter Property="BorderBrush" Value="{x:Static local:FlatColors.SUN_FLOWER}"/>
                <Setter Property="BorderThickness" Value="2"/>
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type TextBox}">
                            <Border x:Name="border" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" 
                                BorderBrush="{TemplateBinding BorderBrush}" SnapsToDevicePixels="True"
                                CornerRadius="14" Padding="10 0">
                                <ScrollViewer x:Name="PART_ContentHost" Focusable="false" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
                            </Border>
                            <ControlTemplate.Triggers>
                                <Trigger Property="IsEnabled" Value="false">
                                    <Setter Property="Opacity" TargetName="border" Value="0.56"/>
                                </Trigger>
                                <Trigger Property="IsMouseOver" Value="true">
                                    <Setter Property="BorderBrush" TargetName="border" Value="{x:Static local:FlatColors.ORANGE}"/>
                                </Trigger>
                                <Trigger Property="IsKeyboardFocused" Value="true">
                                    <Setter Property="BorderBrush" TargetName="border" Value="{x:Static local:FlatColors.ORANGE}"/>
                                </Trigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Trigger>
            <Trigger Property="TextType" Value="Danger">
                <Setter Property="Foreground" Value="{x:Static local:FlatColors.ALIZARIN}"/>
                <Setter Property="BorderBrush" Value="{x:Static local:FlatColors.ALIZARIN}"/>
                <Setter Property="BorderThickness" Value="2"/>
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type TextBox}">
                            <Border x:Name="border" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" 
                                BorderBrush="{TemplateBinding BorderBrush}" SnapsToDevicePixels="True"
                                CornerRadius="14" Padding="10 0">
                                <ScrollViewer x:Name="PART_ContentHost" Focusable="false" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
                            </Border>
                            <ControlTemplate.Triggers>
                                <Trigger Property="IsEnabled" Value="false">
                                    <Setter Property="Opacity" TargetName="border" Value="0.56"/>
                                </Trigger>
                                <Trigger Property="IsMouseOver" Value="true">
                                    <Setter Property="BorderBrush" TargetName="border" Value="{x:Static local:FlatColors.POMEGRANATE}"/>
                                </Trigger>
                                <Trigger Property="IsKeyboardFocused" Value="true">
                                    <Setter Property="BorderBrush" TargetName="border" Value="{x:Static local:FlatColors.POMEGRANATE}"/>
                                </Trigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Trigger>


        </Style.Triggers>
    </Style>

</ResourceDictionary>      

其中的文本框預設的字型和字型大小通過如下設定方法實作:

 <Setter Property="FontSize" Value="{x:Static local:FlatFonts.contentFontSize}"/>

<Setter Property="FontFamily" Value="{x:Static local:FlatFonts.contentFontFamily}"/>

另外就是,定義了根據依賴屬性TextType的值,通過觸發器來設定不同的屬性,進而得到不同UI樣式的目的。

3 WPF FlatTextBox測試

    首先,需要重新生成一下項目檔案,然後在WpfControls項目中添加自定義控件FlatTextBox,Window5.xaml部分示例代碼如下:

<Window x:Class="WpfControls.Window5"
        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" 
        xmlns:local="clr-namespace:WpfControls"
        mc:Ignorable="d"
        Title="Window5" Height="350" Width="500">
    <Grid>
        <WpfControls:FlatTextBox HorizontalAlignment="Center" Margin="0,81,0,0" 
                                 Text="FlatTextBox" TextWrapping="Wrap" 
                                 VerticalAlignment="Top" Width="320" 
                                 Name="flattextbox1" IsRequre="True"/>
        <WpfControls:FlatTextBox HorizontalAlignment="Center" Margin="0,135,0,0" 
                                 Text="FlatTextBox" TextWrapping="Wrap" 
                                 VerticalAlignment="Top" Width="320" 
                                 TextType="Warn"/>
        <WpfControls:FlatTextBox HorizontalAlignment="Center" Margin="0,184,0,0" 
                                 Text="FlatTextBox" TextWrapping="Wrap" 
                                 VerticalAlignment="Top" Width="320" 
                                 TextType="Danger"/>

    </Grid>
</Window>
      

其中的Name="flattextbox1"給控件起一個名稱,這樣可以在背景用C#來進行通路,下面給出背景示例代碼:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

namespace WpfControls
{
    /// <summary>
    /// Window5.xaml 的互動邏輯
    /// </summary>
    public partial class Window5 : Window
    {
        public Window5()
        {
            InitializeComponent();

            this.flattextbox1.Text = "";
            this.flattextbox1.IsValidate();
        }
    }
}      

運作界面如下:

WPF自定義控件07:FlatTextBox