一.前言.预览

  申明: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

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><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:Type 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(){//ClearTextCommandClearTextCommand = new RoutedUICommand();ClearTextCommandBinding = new CommandBinding(ClearTextCommand);ClearTextCommandBinding.Executed += ClearButtonClick;//OpenFileCommandOpenFileCommand = new RoutedUICommand();OpenFileCommandBinding = new CommandBinding(OpenFileCommand);OpenFileCommandBinding.Executed += OpenFileButtonClick;//OpenFolderCommandOpenFolderCommand = new RoutedUICommand();OpenFolderCommandBinding = new CommandBinding(OpenFolderCommand);OpenFolderCommandBinding.Executed += OpenFolderButtonClick;SaveFileCommand = new RoutedUICommand();SaveFileCommandBinding = new CommandBinding(SaveFileCommand);SaveFileCommandBinding.Executed += SaveFileButtonClick;}

效果:

  

当然我们也可以自定义扩展其他功能,如:

<TextBox Width="200" Height="30" Margin="3" core:ControlAttachProperty.Watermark="查询关键词" IsEnabled="True"><core:ControlAttachProperty.AttachContent><ControlTemplate><StackPanel Orientation="Horizontal"><core:FButton FIcon=""  Style="{StaticResource FButton_Transparency}" IsTabStop="False" FIconMargin="0"FIconSize="18" Margin="1,1,2,3" Foreground="{StaticResource TextForeground}" Cursor="Arrow"/><core:FButton FIcon=""  Style="{StaticResource FButton_Transparency}" IsTabStop="False" FIconMargin="0"FIconSize="22" Foreground="{StaticResource TextForeground}" Cursor="Arrow"/></StackPanel></ControlTemplate></core:ControlAttachProperty.AttachContent></TextBox>

效果:

由上不难同时实现Label标签和清除文本内容的样式:

<!--TextBox包含附加属性Label,以及ClearText按钮的样式--><Style TargetType="{x:Type TextBox}" x:Key="LabelClearButtonTextBox" 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><Setter Property="local:ControlAttachProperty.AttachContent"><Setter.Value><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="0,3,1,4" FIconSize="14" Foreground="{StaticResource TextForeground}" Cursor="Arrow"/></ControlTemplate></Setter.Value></Setter></Style>

2.6 文件选择输入相关扩展

  先看看效果,就明白了。

  

具体实现原理和上面2.4差不多 ,实现了三个文件、文件夹选择相关的功能扩展,样式代码:

<!--LabelOpenFileTextBox--><Style TargetType="{x:Type TextBox}" x:Key="LabelOpenFileTextBox" BasedOn="{StaticResource LabelClearButtonTextBox}"><Setter Property="local:ControlAttachProperty.Label" Value="文件路径"/><Setter Property="local:ControlAttachProperty.Watermark" Value="选择文件路径"/><Setter Property="local:ControlAttachProperty.AttachContent"><Setter.Value><ControlTemplate><local:FButton FIcon="" Style="{StaticResource FButton_Transparency}" IsTabStop="False" FIconMargin="0"local:ControlAttachProperty.IsOpenFileButtonBehaviorEnabled="True" Command="local:ControlAttachProperty.OpenFileCommand" CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type TextBox}}}"Margin="0,1,0,1"  FIconSize="22" Foreground="{StaticResource TextForeground}" Cursor="Arrow"/></ControlTemplate></Setter.Value></Setter></Style><!--LabelOpenFolderTextBox--><Style TargetType="{x:Type TextBox}" x:Key="LabelOpenFolderTextBox" BasedOn="{StaticResource LabelClearButtonTextBox}"><Setter Property="local:ControlAttachProperty.Label" Value="设置路径"/><Setter Property="local:ControlAttachProperty.Watermark" Value="选择文件夹路径"/><Setter Property="local:ControlAttachProperty.AttachContent"><Setter.Value><ControlTemplate><local:FButton FIcon="" Style="{StaticResource FButton_Transparency}" IsTabStop="False" FIconMargin="0"local:ControlAttachProperty.IsOpenFolderButtonBehaviorEnabled="True" Command="local:ControlAttachProperty.OpenFolderCommand" CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type TextBox}}}"Margin="0,1,0,1"  FIconSize="22" Foreground="{StaticResource TextForeground}" Cursor="Arrow"/></ControlTemplate></Setter.Value></Setter></Style><!--LabelSaveFileTextBox--><Style TargetType="{x:Type TextBox}" x:Key="LabelSaveFileTextBox" BasedOn="{StaticResource LabelClearButtonTextBox}"><Setter Property="local:ControlAttachProperty.Label" Value="保存路径"/><Setter Property="local:ControlAttachProperty.Watermark" Value="选择文件保存路径"/><Setter Property="local:ControlAttachProperty.AttachContent"><Setter.Value><ControlTemplate><local:FButton FIcon="" Style="{StaticResource FButton_Transparency}" IsTabStop="False" FIconMargin="0"local:ControlAttachProperty.IsSaveFileButtonBehaviorEnabled="True" Command="local:ControlAttachProperty.SaveFileCommand" CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type TextBox}}}"Margin="0,1,0,1"  FIconSize="20" Foreground="{StaticResource TextForeground}" Cursor="Arrow"/></ControlTemplate></Setter.Value></Setter></Style>

当然实现原理和2.4一样,都是依赖属性来实现事件的注入和绑定的,所以就不多废话了:

#region IsOpenFileButtonBehaviorEnabledProperty 选择文件命令行为开关/// <summary>/// 选择文件命令行为开关/// </summary>public static readonly DependencyProperty IsOpenFileButtonBehaviorEnabledProperty = DependencyProperty.RegisterAttached("IsOpenFileButtonBehaviorEnabled", typeof(bool), typeof(ControlAttachProperty), new FrameworkPropertyMetadata(false, IsOpenFileButtonBehaviorEnabledChanged));[AttachedPropertyBrowsableForType(typeof(TextBox))]public static bool GetIsOpenFileButtonBehaviorEnabled(DependencyObject d){return (bool)d.GetValue(IsOpenFileButtonBehaviorEnabledProperty);}public static void SetIsOpenFileButtonBehaviorEnabled(DependencyObject obj, bool value){obj.SetValue(IsOpenFileButtonBehaviorEnabledProperty, value);}private static void IsOpenFileButtonBehaviorEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e){var button = d as FButton;if (e.OldValue != e.NewValue && button != null){button.CommandBindings.Add(OpenFileCommandBinding);}}#endregion#region IsOpenFolderButtonBehaviorEnabledProperty 选择文件夹命令行为开关/// <summary>/// 选择文件夹命令行为开关/// </summary>public static readonly DependencyProperty IsOpenFolderButtonBehaviorEnabledProperty = DependencyProperty.RegisterAttached("IsOpenFolderButtonBehaviorEnabled", typeof(bool), typeof(ControlAttachProperty), new FrameworkPropertyMetadata(false, IsOpenFolderButtonBehaviorEnabledChanged));[AttachedPropertyBrowsableForType(typeof(TextBox))]public static bool GetIsOpenFolderButtonBehaviorEnabled(DependencyObject d){return (bool)d.GetValue(IsOpenFolderButtonBehaviorEnabledProperty);}public static void SetIsOpenFolderButtonBehaviorEnabled(DependencyObject obj, bool value){obj.SetValue(IsOpenFolderButtonBehaviorEnabledProperty, value);}private static void IsOpenFolderButtonBehaviorEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e){var button = d as FButton;if (e.OldValue != e.NewValue && button != null){button.CommandBindings.Add(OpenFolderCommandBinding);}}#endregion#region IsSaveFileButtonBehaviorEnabledProperty 选择文件保存路径及名称/// <summary>/// 选择文件保存路径及名称/// </summary>public static readonly DependencyProperty IsSaveFileButtonBehaviorEnabledProperty = DependencyProperty.RegisterAttached("IsSaveFileButtonBehaviorEnabled", typeof(bool), typeof(ControlAttachProperty), new FrameworkPropertyMetadata(false, IsSaveFileButtonBehaviorEnabledChanged));[AttachedPropertyBrowsableForType(typeof(TextBox))]public static bool GetIsSaveFileButtonBehaviorEnabled(DependencyObject d){return (bool)d.GetValue(IsSaveFileButtonBehaviorEnabledProperty);}public static void SetIsSaveFileButtonBehaviorEnabled(DependencyObject obj, bool value){obj.SetValue(IsSaveFileButtonBehaviorEnabledProperty, value);}private static void IsSaveFileButtonBehaviorEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e){var button = d as FButton;if (e.OldValue != e.NewValue && button != null){button.CommandBindings.Add(SaveFileCommandBinding);}}#endregion#region OpenFileCommand 选择文件命令/// <summary>/// 选择文件命令,需要使用IsClearTextButtonBehaviorEnabledChanged绑定命令/// </summary>public static RoutedUICommand OpenFileCommand { get; private set; }/// <summary>/// OpenFileCommand绑定事件/// </summary>private static readonly CommandBinding OpenFileCommandBinding;/// <summary>/// 执行OpenFileCommand/// </summary>private static void OpenFileButtonClick(object sender, ExecutedRoutedEventArgs e){var tbox = e.Parameter as FrameworkElement;var txt = tbox as TextBox;string filter = txt.Tag == null ? "所有文件(*.*)|*.*" : txt.Tag.ToString();if (filter.Contains(".bin")){filter += "|所有文件(*.*)|*.*";}if (txt == null) return;OpenFileDialog fd = new OpenFileDialog();fd.Title = "请选择文件";//“图像文件(*.bmp, *.jpg)|*.bmp;*.jpg|所有文件(*.*)|*.*”fd.Filter = filter;fd.FileName = txt.Text.Trim();if (fd.ShowDialog() == true){txt.Text = fd.FileName;}tbox.Focus();}#endregion#region OpenFolderCommand 选择文件夹命令/// <summary>/// 选择文件夹命令/// </summary>public static RoutedUICommand OpenFolderCommand { get; private set; }/// <summary>/// OpenFolderCommand绑定事件/// </summary>private static readonly CommandBinding OpenFolderCommandBinding;/// <summary>/// 执行OpenFolderCommand/// </summary>private static void OpenFolderButtonClick(object sender, ExecutedRoutedEventArgs e){var tbox = e.Parameter as FrameworkElement;var txt = tbox as TextBox;if (txt == null) return;FolderBrowserDialog fd = new FolderBrowserDialog();fd.Description = "请选择文件路径";fd.SelectedPath = txt.Text.Trim();if (fd.ShowDialog() == DialogResult.OK){txt.Text = fd.SelectedPath;}tbox.Focus();}#endregion#region SaveFileCommand 选择文件保存路径及名称/// <summary>/// 选择文件保存路径及名称/// </summary>public static RoutedUICommand SaveFileCommand { get; private set; }/// <summary>/// SaveFileCommand绑定事件/// </summary>private static readonly CommandBinding SaveFileCommandBinding;/// <summary>/// 执行OpenFileCommand/// </summary>private static void SaveFileButtonClick(object sender, ExecutedRoutedEventArgs e){var tbox = e.Parameter as FrameworkElement;var txt = tbox as TextBox;if (txt == null) return;SaveFileDialog fd = new SaveFileDialog();fd.Title = "文件保存路径";fd.Filter = "所有文件(*.*)|*.*";fd.FileName = txt.Text.Trim();if (fd.ShowDialog() == DialogResult.OK){txt.Text = fd.FileName;}tbox.Focus();}#endregion/// <summary>/// 静态构造函数/// </summary>static ControlAttachProperty(){//ClearTextCommandClearTextCommand = new RoutedUICommand();ClearTextCommandBinding = new CommandBinding(ClearTextCommand);ClearTextCommandBinding.Executed += ClearButtonClick;//OpenFileCommandOpenFileCommand = new RoutedUICommand();OpenFileCommandBinding = new CommandBinding(OpenFileCommand);OpenFileCommandBinding.Executed += OpenFileButtonClick;//OpenFolderCommandOpenFolderCommand = new RoutedUICommand();OpenFolderCommandBinding = new CommandBinding(OpenFolderCommand);OpenFolderCommandBinding.Executed += OpenFolderButtonClick;SaveFileCommand = new RoutedUICommand();SaveFileCommandBinding = new CommandBinding(SaveFileCommand);SaveFileCommandBinding.Executed += SaveFileButtonClick;}

三.富文本框RichTextBox控件样式

  RichTextBox的样式比较简单:

<!--***************************DefaultRichTextBox***************************--><Style x:Key="DefaultRichTextBox" TargetType="{x:Type RichTextBox}"><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="BorderBrush" Value="{StaticResource ControlBorderBrush}" /><Setter Property="MinHeight" Value="26" /><Setter Property="MinWidth" Value="10" /><Setter Property="Background" Value="{StaticResource TextBackground}" /><Setter Property="Foreground" Value="{StaticResource TextForeground}" /><Setter Property="CaretBrush" Value="{StaticResource TextForeground}" /><Setter Property="local:ControlAttachProperty.FocusBorderBrush" Value="{StaticResource FocusBorderBrush}" /><Setter Property="local:ControlAttachProperty.MouseOverBorderBrush" Value="{StaticResource MouseOverBorderBrush}" /><Setter Property="Padding" Value="1" /><Setter Property="AllowDrop" Value="True" /><Setter Property="VerticalScrollBarVisibility" Value="Auto" /><Setter Property="FocusVisualStyle" Value="{x:Null}" /><Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst" /><!--该值指示是否启用了笔势--><Setter Property="Stylus.IsFlicksEnabled" Value="False" /><!--SnapsToDevicePixels:该值来确定呈现此元素是否应使用特定于设备的像素设置--><Setter Property="SnapsToDevicePixels" Value="True" /><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type TextBoxBase}"><Grid><Border x:Name="Bd"BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"Background="{TemplateBinding Background}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"><ScrollViewer x:Name="PART_ContentHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" /></Border></Grid><ControlTemplate.Triggers><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="Bd" Property="Opacity" Value="0.5" /></Trigger><Trigger Property="IsReadOnly" Value="True"><Setter TargetName="Bd" Property="Opacity" Value="0.85" /></Trigger></ControlTemplate.Triggers></ControlTemplate></Setter.Value></Setter></Style>

使用实力及效果:

四.密码输入框PasswordBox控件样式及扩展功能

  密码输入控件的样式和第二节文本框TextBox基本一致,就不做详细的说明了,直接上样式的代码,相关逻辑(C#) 代码和上面是一样的(复用)。

<!--TextBox默认样式--><Style TargetType="{x:Type PasswordBox}" x:Key="DefaultPasswordBox"><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="PasswordChar" Value="●"/><Setter Property="Height" Value="30" /><Setter Property="Width" Value="200" /><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 PasswordBox}"><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}" /><!--附加内容区域--><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><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}"></Setter></Trigger></ControlTemplate.Triggers></ControlTemplate></Setter.Value></Setter></Style><!--TextBox包含清除Text按钮的样式--><Style TargetType="{x:Type PasswordBox}" x:Key="ClearButtonPasswordBox" BasedOn="{StaticResource DefaultPasswordBox}"><Setter Property="local:ControlAttachProperty.AttachContent"><Setter.Value><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 PasswordBox}}}"Margin="1,3,1,4" FIconSize="14" Foreground="{StaticResource TextForeground}" Cursor="Arrow"/></ControlTemplate></Setter.Value></Setter></Style><!--TextBox包含附加属性Label的样式--><Style TargetType="{x:Type PasswordBox}" x:Key="LabelPasswordBox" BasedOn="{StaticResource DefaultPasswordBox}"><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包含附加属性Label,以及ClearText按钮的样式--><Style TargetType="{x:Type PasswordBox}" x:Key="LabelClearButtonPasswordBox" BasedOn="{StaticResource DefaultPasswordBox}"><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><Setter Property="local:ControlAttachProperty.AttachContent"><Setter.Value><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 PasswordBox}}}"Margin="0,3,1,4" FIconSize="14" Foreground="{StaticResource TextForeground}" Cursor="Arrow"/></ControlTemplate></Setter.Value></Setter></Style>

使用示例及效果:

原文地址:http://www.cnblogs.com/anding/p/4970845.html

转载于:https://www.cnblogs.com/mqxs/p/10142316.html

【转】WPF自定义控件与样式(3)-TextBox RichTextBox PasswordBox样式、水印、Label标签、功能扩展...相关推荐

  1. WPF自定义控件与样式(8)-ComboBox与自定义多选控件MultComboBox

    一.前言 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等,若有不明白的地方可以参考本系列前面的文章,文末附有部分文章链接. 本文主要内容: 下拉选 ...

  2. WPF自定义控件与样式(5)-Calendar/DatePicker日期控件自定义样式及扩展

    原文:WPF自定义控件与样式(5)-Calendar/DatePicker日期控件自定义样式及扩展 一.前言 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐 ...

  3. WPF自定义控件与样式(4)-CheckBox/RadioButton自定义样式

    原文:WPF自定义控件与样式(4)-CheckBox/RadioButton自定义样式 一.前言 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等, ...

  4. WPF自定义控件与样式(1)-矢量字体图标(iconfont)

    原文:WPF自定义控件与样式(1)-矢量字体图标(iconfont) 一.图标字体 图标字体在网页开发上运用非常广泛,具体可以网络搜索了解,网页上的运用有很多例子,如Bootstrap.但在C/S程序 ...

  5. WPF自定义控件与样式(13)-自定义窗体Window 自适应内容大小消息框MessageBox

    一.前言 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等,若有不明白的地方可以参考本系列前面的文章,文末附有部分文章链接. 本文主要内容: 自定义 ...

  6. 【转】WPF自定义控件与样式(13)-自定义窗体Window 自适应内容大小消息框MessageBox...

    一.前言 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等. 本文主要内容: 自定义Window窗体样式: 基于自定义窗体实现自定义MessageB ...

  7. [WPF自定义控件库]以Button为例谈谈如何模仿Aero2主题

    1. 为什么选择Aero2 除了以外观为卖点的控件库,WPF的控件库都默认使用"素颜"的外观,然后再提供一些主题包.这样做的最大好处是可以和原生控件或其它控件库兼容,而且对于大部分 ...

  8. WPF控件之自定义TextBox控件

    首先我们要知道用户控件与自定义用户控件的确保 用户控件 1将多个现有的控件组合成一个可重用的"组". 2不能使用样式和模板. 3继承自UserControl类. 自定义控件 1在现 ...

  9. WPF自定义控件(四)の自定义控件

    原文:WPF自定义控件(四)の自定义控件 在实际工作中,WPF提供的控件并不能完全满足不同的设计需求.这时,需要我们设计自定义控件. 这里LZ总结一些自己的思路,特性如下: Coupling UITe ...

最新文章

  1. html 布局兼容性,HTML+CSS入门 浏览器兼容性问题及解决方案
  2. EditText的另类用法
  3. LeetCode Unique Binary Search Trees(dp)
  4. oracle管理 题库,Oracle数据库管理与开发习题集
  5. 加州大学新算法:让智能汽车更精准检测行人
  6. [bzoj2213][Poi2011]Difference_动态规划
  7. LinkedList 源码分析(JDK 1.8)
  8. 【SQL Sever】将SQL Sever中的一个数据表的数据导出为insert语句
  9. 频率和波特率_实例详解 | 变频器端子启停与通信设定频率(附程序)
  10. 3(1)-字符缓冲流
  11. python安装失败0x80070570_win7系统固态硬盘装系统出现错误代码0x80070570的解决方法...
  12. weblogic 解决线程阻塞
  13. 如何彻底删除SQL 2005数据库(完整版)
  14. Python编程实现后剪枝的CART决策树
  15. 手机 机器人 谢超_大咖云集 长三角智造峰会演讲嘉宾名单曝光
  16. 简单谈谈编程语言(二)
  17. 零基础想学习大数据?(同样适合有一定基础想进阶的)跟着这几个步骤走
  18. UVA 11384 Help is needed for Dexter (递归函数)
  19. seastar介绍及源码分析
  20. 关于xamarin汉字转换成拼音

热门文章

  1. Visual Studio Team System 2008 Team Suite 简体中文正式版
  2. java sql server连接字符串_关于Java:SQL Server的等效jdbc连接字符串
  3. 在jsp文件中通过超链接访问servlet_Eclipse中创建Servlet
  4. c语言int 转bool_C++代码实现逆波兰式_C 语言
  5. 虚拟机游戏获取服务器地址,vue获取服务器地址
  6. mysql sohu_【MySQL中间件之SOHU-DBProxy】
  7. c++word书签_「职场必备」干货!WORD办公软件快捷键,小编整理拿走不谢
  8. linux系统硬件配置查看方法
  9. stl中copy()函数_std :: rotate_copy()函数以及C ++ STL中的示例
  10. c语言中负数_C语言中负数的模数