WPF⾃定义控件与样式(3)-TextBoxRichTextBoxPasswordBox
样。。。
⼀.前⾔.预览
  申明: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
View Code
2.2 ⽔印效果实现
  通过2.1的代码⽰例,可以看出,⽔印是内置了⼀个TextBlock,⽤附加属性ControlAttachProperty.Watermark设置⽔印内容,在触发器中检测,当TextBox中有输⼊值,则隐藏⽔印的TextBlock,使⽤⽰例:
<StackPanel>
<TextBox Width="140" Height="40" Margin="3" TextWrapping="Wrap" VerticalScrollBarVisibility="Visible">333333333333333</TextBox>
<TextBox Width="150" Height="30" Margin="3"core:ControlAttachProperty.Watermark="我是⽔印"core:ControlAttachProperty.CornerRadius="2"></TextBox>
<TextBox Width="150" Height="30" Margin="3" IsReadOnly="True"core:ControlAttachProperty.CornerRadius="15" SnapsToDevicePixels="True">我是只读的</TextBox> <TextBox Width="150" Height="30" Margin="3" IsEnabled="False">IsEnabled="False"</TextBox>
<TextBox Width="150" Height="30"core:ControlAttachProperty.Watermark="我是⽔印"></TextBox>
</StackPanel>
  效果:
2.3 Label标签实现
  参考2.1的代码,预留了Label的区域,通过设置附加属性local:ControlAttachProperty.Label设置标签⽂
本,local:ControlAttachProperty.LabelTemplate设置Label标签的模板样式,即可⾃定义实现Label标签,⾃定义样式:
<!--TextBox包含附加属性Label的样式-->
<Style TargetType="{x:Type TextBox}" x:Key="LabelTextBox" BasedOn="{StaticResource DefaultTextBox}">
<Setter Property="local:ControlAttachProperty.LabelTemplate">
<Setter.Value>
<ControlTemplate TargetType="ContentControl">
<Border Width="60" Background="{StaticResource TextLabelBackground}">
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Right" Margin="3" Text="{TemplateBinding Content}"></TextBlock>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
  使⽤⽰例及效果:
<TextBox Width="200" Height="30" Margin="3"core:ControlAttachProperty.Watermark="请输⼊姓名"
Style="{StaticResource LabelTextBox}"core:ControlAttachProperty.Label="姓名:"></TextBox>
2.4 扩展功能及⾃定义扩展
  思路和2.3的Label标签实现相似,清除⽂本框内的内容是⼀个常⽤需求,我们就线扩展⼀个这么⼀个功能的TextBox,通过附加属性ControlAttachProperty.AttachContent定义扩展功能的模板,模板内定义的是⼀个按钮FButton(可参考上⼀篇,本⽂末尾附录中有链接)
<!--TextBox包含清除Text按钮的样式-->
<Style TargetType="{x:Type TextBox}" x:Key="ClearButtonTextBox" BasedOn="{StaticResource DefaultTextBox}">
<Setter Property="local:ControlAttachProperty.AttachContent">
<Setter.Value>
borderbox
<ControlTemplate>
<local:FButton FIcon="" Style="{StaticResource FButton_Transparency}" IsTabStop="False" FIconMargin="0"
local:ControlAttachProperty.IsClearTextButtonBehaviorEnabled="True" Command="local:ControlAttachProperty.ClearTextCommand"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type TextBox}}}"
Margin="1,3,1,4" FIconSize="14" Foreground="{StaticResource TextForeground}" Cursor="Arrow"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
  这⾥定义的是显⽰效果,清除TextBox内容的逻辑代码如何实现的呢?还是附加属性:
ControlAttachProperty.IsClearTextButtonBehaviorEnabled="True" :注⼊事件到当前Button
Command="local:ControlAttachProperty.ClearTextCommand":定义Fbutton的命令对象实例Command
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Typ
e TextBox}}}":把TextBox作为参数传⼊
  逻辑代码如下,从代码不难看出,它是⽀持多种输⼊控件的内容清除的,也就是说该扩展功能可以轻松⽀持其他输⼊控件,第四节密码数据的清除也是这样使⽤的。
#region IsClearTextButtonBehaviorEnabledProperty 清除输⼊框Text值按钮⾏为开关(设为ture时才会绑定事件)
///<summary>
///清除输⼊框Text值按钮⾏为开关
///</summary>
public static readonly DependencyProperty IsClearTextButtonBehaviorEnabledProperty = DependencyProperty.RegisterAttached("IsClearTextButtonBehaviorEnabled"            , typeof(bool), typeof(ControlAttachProperty), new FrameworkPropertyMetadata(false, IsClearTextButtonBehaviorEnabledChanged));
[AttachedPropertyBrowsableForType(typeof(TextBox))]
public static bool GetIsClearTextButtonBehaviorEnabled(DependencyObject d)
{
return (bool)d.GetValue(IsClearTextButtonBehaviorEnabledProperty);
}
public static void SetIsClearTextButtonBehaviorEnabled(DependencyObject obj, bool value)
{
obj.SetValue(IsClearTextButtonBehaviorEnabledProperty, value);
}
///<summary>
///绑定清除Text操作的按钮事件
/
//</summary>
private static void IsClearTextButtonBehaviorEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var button = d as FButton;
if (e.OldValue != e.NewValue && button != null)
{
button.CommandBindings.Add(ClearTextCommandBinding);
}
}
#endregion
#region ClearTextCommand 清除输⼊框Text事件命令
///<summary>
///清除输⼊框Text事件命令,需要使⽤IsClearTextButtonBehaviorEnabledChanged绑定命令
///</summary>
public static RoutedUICommand ClearTextCommand { get; private set; }
///<summary>
/// ClearTextCommand绑定事件
///</summary>
private static readonly CommandBinding ClearTextCommandBinding;
///<summary>
///清除输⼊框⽂本值
///</summary>
private static void ClearButtonClick(object sender, ExecutedRoutedEventArgs e)
{
var tbox = e.Parameter as FrameworkElement;
if (tbox == null) return;
if (tbox is TextBox)
{
((TextBox)tbox).Clear();
}
if (tbox is PasswordBox)
{
((PasswordBox)tbox).Clear();
}
if (tbox is ComboBox)
{
var cb = tbox as ComboBox;
cb.SelectedItem = null;
cb.Text = string.Empty;
}
if (tbox is MultiComboBox)
{
var cb = tbox as MultiComboBox;
cb.SelectedItem = null;
cb.UnselectAll();
cb.Text = string.Empty;
}
if (tbox is DatePicker)
{
var dp = tbox as DatePicker;
dp.SelectedDate = null;
dp.Text = string.Empty;
}
tbox.Focus();
}
#endregion
///<summary>
///静态构造函数
///</summary>
static ControlAttachProperty()
{
//ClearTextCommand
ClearTextCommand = new RoutedUICommand();
ClearTextCommandBinding = new CommandBinding(ClearTextCommand);
ClearTextCommandBinding.Executed += ClearButtonClick;
//OpenFileCommand

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。