在不少的应用程序中,作为基本输入的文本框(TextBox)是少不了的,它可以很方便获取用户输入的值。本文将介绍一下自定义的文本框控件FlatTextBox,它与FlatCheckBox实现过程非常类似,它是继承自TextBox,但使用自定义的UI样式来美化界面,并添加了特有的一些依赖属性。下面将详细介绍具体的实现细节。
1 WPF项目结构
基于之前创建的WPF示例项目,在其中创建一个新的关于FlatTextBox的项目文件。本质上,FlatTextBox是继承TextBox控件,利用TextBox控件自身的属性和方法,可以减少FlatTextBox实现的难度和复杂度。首先,给出本项目文件结构,如下图所示:
其中的Fonts目录下存放各种图标字体文件,Style目录下存放各种控件的UI 样式定义文件,FlatComboBox.xaml就是FlatComboBox控件的样式定义文件。另外,需要将其注册到Generic.xaml文件中,示例代码如下:
<ResourceDictionaryxmlns="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><ResourceDictionarySource="/Yd.WpfControls;component/Style/IconFont.xaml"/><ResourceDictionarySource="/Yd.WpfControls;component/Style/FlatButton.xaml"/><ResourceDictionarySource="/Yd.WpfControls;component/Style/FlatCheckBox.xaml"/><ResourceDictionarySource="/Yd.WpfControls;component/Style/FlatRadioButton.xaml"/><ResourceDictionarySource="/Yd.WpfControls;component/Style/ToggleButton.xaml"/><ResourceDictionarySource="/Yd.WpfControls;component/Style/FlatComboBox.xaml"/><ResourceDictionarySource="/Yd.WpfControls;component/Style/FlatTextBox.xaml"/></ResourceDictionary.MergedDictionaries></ResourceDictionary>
2 WPF FlatTextBox实现
首先在Yd.WpfControls项目中添加一个类FlatTextBox.cs,它继承自TextBox控件,示例代码如下:
usingSystem; usingSystem.Collections.Generic; usingSystem.Linq; usingSystem.Text; usingSystem.Threading.Tasks; usingSystem.Windows; usingSystem.Windows.Controls; usingSystem.Windows.Media; namespaceYd.WpfControls{ publicclassFlatTextBox : TextBox { staticFlatTextBox() { DefaultStyleKeyProperty.OverrideMetadata(typeof(FlatTextBox), newFrameworkPropertyMetadata(typeof(FlatTextBox))); } publicstaticreadonlyDependencyPropertyTextTypeProperty=DependencyProperty.Register("TextType", typeof(FlatButtonType), typeof(FlatTextBox), newPropertyMetadata(FlatButtonType.Default)); publicFlatButtonTypeTextType { get { return (FlatButtonType)GetValue(TextTypeProperty); } set { SetValue(TextTypeProperty, value); } } publicstaticreadonlyDependencyPropertyIsRequreProperty=DependencyProperty.Register("IsRequre", typeof(bool), typeof(FlatTextBox), newPropertyMetadata(false)); publicboolIsRequre { get { return (bool)GetValue(IsRequreProperty); } set { SetValue(IsRequreProperty, value); } } publicboolIsValidate() { if ( IsRequre) { if (!string.IsNullOrEmpty(this.Text)) { TextType=FlatButtonType.Default; returntrue; } else { TextType=FlatButtonType.Danger; returntrue; } } else { TextType=FlatButtonType.Default; returntrue; } } } }
其中扩展了2个依赖属性,即TextType属性和IsRequre属性,前者代表当前输入框的类型,是Default状态的,还是Warn状态的,还是Danger状态;而后者代表当前文本框是否为必输,如果值是true,则当文本框值为空的时候,会显示红色边框。当然这个验证过程通过自定义的IsValidate()方法实现。
FlatTextBox控件的UI样式主要就是依靠FlatTextBox.xaml文件进行定义的,示例代码如下:
<ResourceDictionaryxmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="clr-namespace:Yd.WpfControls"><StyleTargetType="{x:Type local:FlatTextBox}"><SetterProperty="FontSize"Value="{x:Static local:FlatFonts.contentFontSize}"/><SetterProperty="FontFamily"Value="{x:Static local:FlatFonts.contentFontFamily}"/><SetterProperty="Background"Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/><SetterProperty="Foreground"Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/><SetterProperty="BorderThickness"Value="2"/><SetterProperty="KeyboardNavigation.TabNavigation"Value="None"/><SetterProperty="HorizontalContentAlignment"Value="Left"/><SetterProperty="FocusVisualStyle"Value="{x:Null}"/><SetterProperty="AllowDrop"Value="true"/><SetterProperty="Height"Value="28"/><SetterProperty="VerticalContentAlignment"Value="Center"/><SetterProperty="ScrollViewer.PanningMode"Value="VerticalFirst"/><SetterProperty="Stylus.IsFlicksEnabled"Value="False"/><Style.Triggers><TriggerProperty="TextType"Value="Default"><SetterProperty="Foreground"Value="{x:Static local:FlatColors.SILVER}"/><SetterProperty="BorderBrush"Value="{x:Static local:FlatColors.SILVER}"/><SetterProperty="BorderThickness"Value="2"/><SetterProperty="Template"><Setter.Value><ControlTemplateTargetType="{x:Type TextBox}"><Borderx:Name="border"Background="{TemplateBinding Background}"BorderThickness="{TemplateBinding BorderThickness}"BorderBrush="{TemplateBinding BorderBrush}"SnapsToDevicePixels="True"CornerRadius="14"Padding="10 0"><ScrollViewerx:Name="PART_ContentHost"Focusable="false"HorizontalScrollBarVisibility="Hidden"VerticalScrollBarVisibility="Hidden"/></Border><ControlTemplate.Triggers><TriggerProperty="IsEnabled"Value="false"><SetterProperty="Opacity"TargetName="border"Value="0.56"/></Trigger><TriggerProperty="IsMouseOver"Value="true"><SetterProperty="BorderBrush"TargetName="border"Value="{x:Static local:FlatColors.ASBESTOS}"/></Trigger><TriggerProperty="IsKeyboardFocused"Value="true"><SetterProperty="BorderBrush"TargetName="border"Value="{x:Static local:FlatColors.ASBESTOS}"/></Trigger></ControlTemplate.Triggers></ControlTemplate></Setter.Value></Setter></Trigger><TriggerProperty="TextType"Value="Warn"><SetterProperty="Foreground"Value="{x:Static local:FlatColors.SUN_FLOWER}"/><SetterProperty="BorderBrush"Value="{x:Static local:FlatColors.SUN_FLOWER}"/><SetterProperty="BorderThickness"Value="2"/><SetterProperty="Template"><Setter.Value><ControlTemplateTargetType="{x:Type TextBox}"><Borderx:Name="border"Background="{TemplateBinding Background}"BorderThickness="{TemplateBinding BorderThickness}"BorderBrush="{TemplateBinding BorderBrush}"SnapsToDevicePixels="True"CornerRadius="14"Padding="10 0"><ScrollViewerx:Name="PART_ContentHost"Focusable="false"HorizontalScrollBarVisibility="Hidden"VerticalScrollBarVisibility="Hidden"/></Border><ControlTemplate.Triggers><TriggerProperty="IsEnabled"Value="false"><SetterProperty="Opacity"TargetName="border"Value="0.56"/></Trigger><TriggerProperty="IsMouseOver"Value="true"><SetterProperty="BorderBrush"TargetName="border"Value="{x:Static local:FlatColors.ORANGE}"/></Trigger><TriggerProperty="IsKeyboardFocused"Value="true"><SetterProperty="BorderBrush"TargetName="border"Value="{x:Static local:FlatColors.ORANGE}"/></Trigger></ControlTemplate.Triggers></ControlTemplate></Setter.Value></Setter></Trigger><TriggerProperty="TextType"Value="Danger"><SetterProperty="Foreground"Value="{x:Static local:FlatColors.ALIZARIN}"/><SetterProperty="BorderBrush"Value="{x:Static local:FlatColors.ALIZARIN}"/><SetterProperty="BorderThickness"Value="2"/><SetterProperty="Template"><Setter.Value><ControlTemplateTargetType="{x:Type TextBox}"><Borderx:Name="border"Background="{TemplateBinding Background}"BorderThickness="{TemplateBinding BorderThickness}"BorderBrush="{TemplateBinding BorderBrush}"SnapsToDevicePixels="True"CornerRadius="14"Padding="10 0"><ScrollViewerx:Name="PART_ContentHost"Focusable="false"HorizontalScrollBarVisibility="Hidden"VerticalScrollBarVisibility="Hidden"/></Border><ControlTemplate.Triggers><TriggerProperty="IsEnabled"Value="false"><SetterProperty="Opacity"TargetName="border"Value="0.56"/></Trigger><TriggerProperty="IsMouseOver"Value="true"><SetterProperty="BorderBrush"TargetName="border"Value="{x:Static local:FlatColors.POMEGRANATE}"/></Trigger><TriggerProperty="IsKeyboardFocused"Value="true"><SetterProperty="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部分示例代码如下:
<Windowx: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:FlatTextBoxHorizontalAlignment="Center"Margin="0,81,0,0"Text="FlatTextBox"TextWrapping="Wrap"VerticalAlignment="Top"Width="320"Name="flattextbox1"IsRequre="True"/><WpfControls:FlatTextBoxHorizontalAlignment="Center"Margin="0,135,0,0"Text="FlatTextBox"TextWrapping="Wrap"VerticalAlignment="Top"Width="320"TextType="Warn"/><WpfControls:FlatTextBoxHorizontalAlignment="Center"Margin="0,184,0,0"Text="FlatTextBox"TextWrapping="Wrap"VerticalAlignment="Top"Width="320"TextType="Danger"/></Grid></Window>
其中的Name="flattextbox1"给控件起一个名称,这样可以在后台用C#来进行访问,下面给出后台示例代码:
usingSystem; usingSystem.Collections.Generic; usingSystem.Linq; usingSystem.Text; usingSystem.Threading.Tasks; usingSystem.Windows; usingSystem.Windows.Controls; usingSystem.Windows.Data; usingSystem.Windows.Documents; usingSystem.Windows.Input; usingSystem.Windows.Media; usingSystem.Windows.Media.Imaging; usingSystem.Windows.Shapes; namespaceWpfControls{ /// <summary>/// Window5.xaml 的交互逻辑/// </summary>publicpartialclassWindow5 : Window { publicWindow5() { InitializeComponent(); this.flattextbox1.Text=""; this.flattextbox1.IsValidate(); } } }
运行界面如下: