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

一.前言.预览

  申明: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="&#xe60a;" 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="&#xe60b;"  Style="{StaticResource FButton_Transparency}" IsTabStop="False" FIconMargin="0"FIconSize="18" Margin="1,1,2,3" Foreground="{StaticResource TextForeground}" Cursor="Arrow"/><core:FButton FIcon="&#xe628;"  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="&#xe60a;" 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="&#xe64e;" 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="&#xe636;" 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="&#xe61a;" 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="&#xe60a;" 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="&#xe60a;" 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/387735.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JVM调优 dump文件怎么生成和分析

1、获取JVM的dump文件的两种方式   1. JVM启动时增加两个参数: #出现 OOME 时生成堆 dump: -XX:HeapDumpOnOutOfMemoryError #生成堆文件地址&#xff1a; -XX:HeapDumpPath/home/liuke/jvmlogs/ 2. 发现程序异常前通过执行指令&#xff0c;直接生成当前JVM的dmp文件&#x…

【网络安全/CTF】unseping 江苏工匠杯

该题考察序列化反序列化及Linux命令执行相关知识。 题目 <?php highlight_file(__FILE__);class ease{private $method;private $args;function __construct($method, $args) {$this->method $method;$this->args $args;}function __destruct(){if (in_array($thi…

yum配置中driver-class-name: com.mysql.jdbc.Driver报错

错误&#xff1a; 原因&#xff1a; 解决方法&#xff1a;把方框中的<scope>runtime</scope>删掉 转载于:https://www.cnblogs.com/zly123/p/10834958.html

Red Hat 8.0中设置光盘为软件源

为什么80%的码农都做不了架构师&#xff1f;>>> 以管理员身份登录 su 编辑设置软件源的repo文件 gedit /etc/yum.repos.d/redhat.repo 粘贴如下文本至空白处&#xff1a; [InstallMedia] nameRed Hat Enterprise Linux 8.0.0 mediaidNone metadata_expire-1 gpgche…

C++11并发编程:多线程std::thread

一&#xff1a;概述 C11引入了thread类&#xff0c;大大降低了多线程使用的复杂度&#xff0c;原先使用多线程只能用系统的API&#xff0c;无法解决跨平台问题&#xff0c;一套代码平台移植&#xff0c;对应多线程代码也必须要修改。现在在C11中只需使用语言层面的thread可以解…

图像特征提取——韦伯局部描述符(WLD)

一、原理及概述 韦伯局部描述符&#xff08;WLD&#xff09;是一种鲁棒性好、简单高效的局部特征描述符。WLD由两个部分组成&#xff1a;差分激励和梯度方向。 其具体算法是对于给定的一幅图像&#xff0c;通过对每个像素进行这两个分量的计算来提取其差分激励图像和梯度方向图…

Linux下Nagios的安装与配置

Linux下Nagios的安装与配置 一、Nagios简介 Nagios是一款开源的电脑系统和网络监视工具&#xff0c;能有效监控Windows、Linux和Unix的主机状态&#xff0c;交换机路由器等网络设置&#xff0c;打印机等。在系统或服务状态异常时发出邮件或短信报警第一时间通知网站运维人员&am…

POJ_3262 Protecting the Flowers 【贪心】

一、题面 POJ3262 二、分析 这题要往贪心上面想应该还是很容易的&#xff0c;但问题是要证明为什么比值关系就能满足。 可以选择几个去分析&#xff0c;入1-6 与 2-15 和 1-6 与2-5 和 1-6 与 2- 12。 三、AC代码 1 #include <cstdio>2 #include <iostream>3 #in…

jquery如何阻止子元素继承父元素的事件(又称事件冒泡)

非常简单&#xff0c;子元素上添加如下代码即可 1 $(a).click(function(e){ 2 e.stopPropagation(); 3 }); 老版本为event,现在用e就行 转载于:https://www.cnblogs.com/chengbo2130/p/10152747.html

Beta 冲刺 (7/7)

团队信息 队名&#xff1a;爸爸饿了组长博客&#xff1a;here作业博客&#xff1a;here组员情况 组员1&#xff08;组长&#xff09;&#xff1a;王彬 过去两天完成了哪些任务 协助完成安卓端的整合完成安卓端的美化协助制作宣传视频 接下来的计划 & 还剩下哪些任务 I am d…

Java Web 学习与总结(一)Servlet基础

配置环境&#xff1a;https://www.cnblogs.com/qq965921539/p/9821374.html 简介&#xff1a; Servlet是Sun公司提供的一种实现动态网页的解决方案&#xff0c;在制定J2EE时引入它作为实现了基于Java语言的动态技术&#xff0c;目前流行的Web框架基本都基于Servlet技术&#xf…

堪称经典

程序员作为高智商、高收入、高压力群体&#xff0c;经常会有各种自嘲&#xff0c;而且还天生携带段子手基因。不信看看下面这些段子&#xff0c;一般人哪能懂&#xff01; 1、杀一个程序员不需要用枪&#xff0c;改三次需求就可以了 2、程序员退休后决定练习书法&#xff0c;于…

Judy Beta 第三天

概述 前端部分对于打包的流程以及相关 package.json 的配置都已经比较熟悉了&#xff0c;目前主要负责对新实现的 feature 做测试&#xff0c;以及参考 DAP 为后端明确数据交换的格式及参数内容等。 后端部分按照更新的 wiki 实现了变量展开&#xff0c;并且根据 DAP 的协议流程…

python学习中遇到的问题

2019独角兽企业重金招聘Python工程师标准>>> def contract_str():index 8db_size 8new_table_name b_str(index & (db_size-1))print new_table_name ImportError: No module named mysql.connector import mysql.connector as sql_connector Python保存时提…

Anti-Aliasing SSAA MSAA MLAA SRAA 简介

http://blog.csdn.net/codeboycjy/article/details/6312758 前两天在浏览游民星空的时候&#xff0c;小编居然在文章中挂了一篇技术文章&#xff0c;是关于SRAA的。对于AA的了解很少&#xff0c;正好入职之前还有几天的空闲时间&#xff0c;所以就这个机会把AA的一些基本算法简…

Qt的安装和使用中的常见问题(详细版)

对于太长不看的朋友&#xff0c;可参考Qt的安装和使用中的常见问题&#xff08;简略版&#xff09;。 目录 1、引入2、Qt简介3、Qt版本 3.1 查看安装的Qt版本3.2 查看当前项目使用的Qt版本3.3 查看当前项目使用的QtCreator版本3.4 Linux命令行下查看和使用不同版本的Qt4、Qt模块…

各行业大数据可视化界面参考

转载于:https://www.cnblogs.com/wangsongbai/p/10178096.html

今日听闻这几款手机软件比较火爆 果然名不虚传!

如今的时代&#xff0c;智能手机已经成为我们生活中不可缺少的一部分&#xff0c;大家之所以这么爱玩手机&#xff0c;其实并不是手机本身有多么吸引人&#xff0c;而是安装在手机上的各种各样的APP&#xff0c;比如各种社交软件、音频软件、购物软件以及地图软件等等。下面我们…

postman提取返回值

Postman是做接口测试的&#xff0c;但是很多接口并不是直接就能测&#xff0c;有的需要一些预处理。比如说身份认证&#xff0c;需要传递一个token。如果做网页测试&#xff0c;一般打开登陆界面的时候就会生成一个token&#xff0c;如果返回值是json格式&#xff0c;用Postman…

大数据学习要知道的十大发展趋势,以及学习大数据的几点建议

2016年&#xff0c;近40%的公司正在实施和扩展大数据技术应用&#xff0c;另有30%的公司计划在未来12个月内采用大数据技术&#xff0c;62.5%的公司现在至少有一个大数据项目投入生产&#xff0c;只有5.4%的公司没有大数据应用计划&#xff0c;或者是没有正在进行的大数据项目&…