一.前言.预览
申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等,若有不明白的地方可以参考本系列前面的文章,文末附有部分文章链接。
本文主要是对文本输入控件进行样式开发,及相关扩展功能开发,主要内容包括:
- 基本文本框TextBox控件样式及扩展功能,实现了样式、水印、Label标签、功能扩展;
- 富文本框RichTextBox控件样式;
- 密码输入框PasswordBox控件样式及扩展功能;
效果图:
二.基本文本框TextBox控件样式及扩展功能
2.1 TextBox基本样式
样式代码如下:
<!--TextBox默认样式--> <Style TargetType="{x:Type TextBox}" x:Key="DefaultTextBox"> <Setter Property="ContextMenu" Value="{DynamicResource TextBoxContextMenu}" /> <Setter Property="SelectionBrush" Value="{StaticResource TextSelectionBrush}" /> <Setter Property="FontFamily" Value="{StaticResource FontFamily}" /> <Setter Property="FontSize" Value="{StaticResource FontSize}" /> <Setter Property="BorderThickness" Value="1" /> <Setter Property="MinHeight" Value="26" /> <Setter Property="Width" Value="100" /> <Setter Property="Background" Value="{StaticResource TextBackground}" /> <Setter Property="Foreground" Value="{StaticResource TextForeground}" /> <Setter Property="Padding" Value="0" /> <Setter Property="BorderBrush" Value="{StaticResource ControlBorderBrush}" /> <Setter Property="local:ControlAttachProperty.FocusBorderBrush" Value="{StaticResource FocusBorderBrush}" /> <Setter Property="local:ControlAttachProperty.MouseOverBorderBrush" Value="{StaticResource MouseOverBorderBrush}" /> <Setter Property="VerticalContentAlignment" Value="Center" /> <!-- change SnapsToDevicePixels to True to view a better border and validation error --> <Setter Property="SnapsToDevicePixels" Value="True" /> <!--英 ['kærət] 美 ['kærət] 插入符号--> <Setter Property="CaretBrush" Value="{StaticResource TextForeground}" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type TextBox}"> <Grid x:Name="PART_Root"> <Border x:Name="Bg" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" CornerRadius="{TemplateBinding local:ControlAttachProperty.CornerRadius}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" /> <Grid x:Name="PART_InnerGrid"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <!--Label区域--> <ContentControl x:Name="Label" Margin="1" Template="{TemplateBinding local:ControlAttachProperty.LabelTemplate}" Content="{TemplateBinding local:ControlAttachProperty.Label}"/> <!--内容区域--> <ScrollViewer x:Name="PART_ContentHost" BorderThickness="0" Grid.Column="1" IsTabStop="False" Margin="2" VerticalAlignment="Stretch" Background="{x:Null}" /> <!--水印--> <TextBlock x:Name="Message" Padding="{TemplateBinding Padding}" Visibility="Collapsed" Text="{TemplateBinding local:ControlAttachProperty.Watermark}" Grid.Column="1" Foreground="{TemplateBinding Foreground}" IsHitTestVisible="False" Opacity="{StaticResource WatermarkOpacity}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Margin="5,2,5,2" /> <!--附加内容区域--> <Border x:Name="PART_AttachContent" Grid.Column="2" Margin="2" VerticalAlignment="Center" HorizontalAlignment="Center" > <ContentControl VerticalAlignment="Center" VerticalContentAlignment="Center" Template="{TemplateBinding local:ControlAttachProperty.AttachContent}" /> </Border> </Grid> </Grid> <ControlTemplate.Triggers> <!--显示水印--> <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Text}" Value=""> <Setter TargetName="Message" Property="Visibility" Value="Visible" /> </DataTrigger> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="BorderBrush" Value="{Binding Path=(local:ControlAttachProperty.MouseOverBorderBrush),RelativeSource={RelativeSource Self}}"/> </Trigger> <Trigger Property="IsFocused" Value="True"> <Setter Property="BorderBrush" Value="{Binding Path=(local:ControlAttachProperty.FocusBorderBrush),RelativeSource={RelativeSource Self}}"/> </Trigger> <!--不可用--> <Trigger Property="IsEnabled" Value="False"> <Setter TargetName="PART_Root" Property="Opacity" Value="{StaticResource DisableOpacity}" /> </Trigger> <!--只读时,禁用PART_AttachContent--> <Trigger Property="IsReadOnly" Value="True"> <Setter TargetName="PART_AttachContent" Property="IsEnabled" Value="False" /> <Setter TargetName="Bg" Property="Opacity" Value="{StaticResource ReadonlyOpacity}" /> <Setter TargetName="PART_ContentHost" Property="Opacity" Value="{StaticResource ReadonlyOpacity}" /> <Setter TargetName="Label" Property="Opacity" Value="{StaticResource ReadonlyOpacity}" /> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style>
模板内容主要包含四部分:
- 用于实现Label标签的预留区域;
- TextBox本身的文本输入显示部分;
- 水印显示部分;
- 功能扩展的预留区域;
其中Label标签、功能扩展,还有输入框的不同状态显示效果都是通过附加属性来实现的,其实从本质上附加属性和控件上定义的依赖属性是同一个概念,有些时候附加属性会更加方便,对于一些可共用的属性,就比较方便,这一点怎本文是有体现的。上面代码使用到的附加属性代码:
#region FocusBorderBrush 焦点边框色,输入控件 public static readonly DependencyProperty FocusBorderBrushProperty = DependencyProperty.RegisterAttached( "FocusBorderBrush", typeof(Brush), typeof(ControlAttachProperty), new FrameworkPropertyMetadata(null)); public static void SetFocusBorderBrush(DependencyObject element, Brush value) { element.SetValue(FocusBorderBrushProperty, value); } public static Brush GetFocusBorderBrush(DependencyObject element) { return (Brush)element.GetValue(FocusBorderBrushProperty); } #endregion #region MouseOverBorderBrush 鼠标进入边框色,输入控件 public static readonly DependencyProperty MouseOverBorderBrushProperty = DependencyProperty.RegisterAttached("MouseOverBorderBrush", typeof(Brush), typeof(ControlAttachProperty), new FrameworkPropertyMetadata(Brushes.Transparent, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.Inherits)); /// <summary> /// Sets the brush used to draw the mouse over brush. /// </summary> public static void SetMouseOverBorderBrush(DependencyObject obj, Brush value) { obj.SetValue(MouseOverBorderBrushProperty, value); } /// <summary> /// Gets the brush used to draw the mouse over brush. /// </summary> [AttachedPropertyBrowsableForType(typeof(TextBox))] [AttachedPropertyBrowsableForType(typeof(CheckBox))] [AttachedPropertyBrowsableForType(typeof(RadioButton))] [AttachedPropertyBrowsableForType(typeof(DatePicker))] [AttachedPropertyBrowsableForType(typeof(ComboBox))] [AttachedPropertyBrowsableForType(typeof(RichTextBox))] public static Brush GetMouseOverBorderBrush(DependencyObject obj) { return (Brush)obj.GetValue(MouseOverBorderBrushProperty); } #endregion #region AttachContentProperty 附加组件模板 /// <summary> /// 附加组件模板 /// </summary> public static readonly DependencyProperty AttachContentProperty = DependencyProperty.RegisterAttached( "AttachContent", typeof(ControlTemplate), typeof(ControlAttachProperty), new FrameworkPropertyMetadata(null)); public static ControlTemplate GetAttachContent(DependencyObject d) { return (ControlTemplate)d.GetValue(AttachContentProperty); } public static void SetAttachContent(DependencyObject obj, ControlTemplate value) { obj.SetValue(AttachContentProperty, value); } #endregion #region WatermarkProperty 水印 /// <summary> /// 水印 /// </summary> public static readonly DependencyProperty WatermarkProperty = DependencyProperty.RegisterAttached( "Watermark", typeof(string), typeof(ControlAttachProperty), new FrameworkPropertyMetadata("")); public static string GetWatermark(DependencyObject d) { return (string)d.GetValue(WatermarkProperty); } public static void SetWatermark(DependencyObject obj, string value) { obj.SetValue(WatermarkProperty, value); } #endregion #region CornerRadiusProperty Border圆角 /// <summary> /// Border圆角 /// </summary> public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.RegisterAttached( "CornerRadius", typeof(CornerRadius), typeof(ControlAttachProperty), new FrameworkPropertyMetadata(null)); public static CornerRadius GetCornerRadius(DependencyObject d) { return (CornerRadius)d.GetValue(CornerRadiusProperty); } public static void SetCornerRadius(DependencyObject obj, CornerRadius value) { obj.SetValue(CornerRadiusProperty, value); } #endregion #region LabelProperty TextBox的头部Label /// <summary> /// TextBox的头部Label /// </summary> public static readonly DependencyProperty LabelProperty = DependencyProperty.RegisterAttached( "Label", typeof(string), typeof(ControlAttachProperty), new FrameworkPropertyMetadata(null)); [AttachedPropertyBrowsableForType(typeof(TextBox))] public static string GetLabel(DependencyObject d) { return (string)d.GetValue(LabelProperty); } public static void SetLabel(DependencyObject obj, string value) { obj.SetValue(LabelProperty, value); } #endregion #region LabelTemplateProperty TextBox的头部Label模板 /// <summary> /// TextBox的头部Label模板 /// </summary> public static readonly DependencyProperty LabelTemplateProperty = DependencyProperty.RegisterAttached( "LabelTemplate", typeof(ControlTemplate), typeof(ControlAttachProperty), new FrameworkPropertyMetadata(null)); [AttachedPropertyBrowsableForType(typeof(TextBox))] public static ControlTemplate GetLabelTemplate(DependencyObject d) { return (ControlTemplate)d.GetValue(LabelTemplateProperty); } public static void SetLabelTemplate(DependencyObject obj, ControlTemplate value) { obj.SetValue(LabelTemplateProperty, value); } #endregion