zoukankan      html  css  js  c++  java
  • 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
    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>
                    <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()
            {
                //ClearTextCommand
                ClearTextCommand = new RoutedUICommand();
                ClearTextCommandBinding = new CommandBinding(ClearTextCommand);
                ClearTextCommandBinding.Executed += ClearButtonClick;
                //OpenFileCommand
                OpenFileCommand = new RoutedUICommand();
                OpenFileCommandBinding = new CommandBinding(OpenFileCommand);
                OpenFileCommandBinding.Executed += OpenFileButtonClick;
                //OpenFolderCommand
                OpenFolderCommand = new RoutedUICommand();
                OpenFolderCommandBinding = new CommandBinding(OpenFolderCommand);
                OpenFolderCommandBinding.Executed += OpenFolderButtonClick;
    
                SaveFileCommand = new RoutedUICommand();
                SaveFileCommandBinding = new CommandBinding(SaveFileCommand);
                SaveFileCommandBinding.Executed += SaveFileButtonClick;
            }
    View Code

      效果:

      

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

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

     三.富文本框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>
    View Code

    使用示例及效果:  

    附录.参考引用

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

    WPF自定义控件与样式(2)-自定义按钮FButton  

    《深入浅出WPF》学习笔记二数据绑定(Binding)、依赖属性和附加属性

    版权所有,文章来源:http://www.cnblogs.com/anding

    个人能力有限,本文内容仅供学习、探讨,欢迎指正、交流。

  • 相关阅读:
    phpcms后台进入地址(包含No permission resources错误)
    phpmyadmin上传大sql文件办法
    ubuntu彻底卸载mysql
    Hdoj 2602.Bone Collector 题解
    一篇看懂词向量
    Hdoj 1905.Pseudoprime numbers 题解
    The Python Challenge 谜题全解(持续更新)
    Hdoj 2289.Cup 题解
    Hdoj 2899.Strange fuction 题解
    Hdoj 2199.Can you solve this equation? 题解
  • 原文地址:https://www.cnblogs.com/anding/p/4970845.html
Copyright © 2011-2022 走看看