zoukankan      html  css  js  c++  java
  • 带清空按钮TextBox的实现(WPF)

    本博文针对人群:WPF新手。
    博文内容:通过Style制定包含清空Button的TextBox样式模板,通过在Style中引入自定义类的附加属性完成对TextBox的内容清空。
    [csharp] view plain copy
     
    1. <span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">博文目的:帮助刚入门的WPF开发人员或有需要的人快速了解该样式的实现。由于本人水平有限,难免有不足的地方,欢迎请指正,共同进步!</span>  

    正题:

    带清空按钮TextBox的实现(WPF)分为两部分:样式模板(Style)部分和附加属性类(TextBoxHelper)部分。

    一、样式模板(Style)

    我们先定义Style,目标类型:TextBox

    下面针对TextBox的Template部分详细说明:

    关键代码如下(只保留核心属性设置)

    [csharp] view plain copy
     
    1. <ControlTemplate TargetType="{x:Type TextBox}">  
    2.                     <Border >  
    3.                         <DockPanel LastChildFill="True">  
    4.                             <Button x:Name="Part_ClearButton" DockPanel.Dock="Right"  Width="{Binding ActualHeight, RelativeSource={RelativeSource Self}}" />  
    5.                             <ScrollViewer x:Name="PART_ContentHost" DockPanel.Dock="Left" />  
    6.                         </DockPanel>  
    7.                     </Border>  
    8.                     <ControlTemplate.Triggers>  
    9.                         <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Text}" Value="">  
    10.                             <Setter TargetName="Part_ClearButton" Property="Visibility" Value="Collapsed" />  
    11.                         </DataTrigger>  
    12.                         ................................省略  
    13.                     </ControlTemplate.Triggers>  
    14.                 </ControlTemplate>  

    为了方便直观理解,下图为样式模板(ControlTemplate)的示意图。

    关于ControlTemplate相信大家都有个大概了解了,下面针对其中容易出错的地方补充几点说明:

    1.关于最外层的Border对于它的 BorderBrush、BorderThickness、 SnapsToDevicePixels、 Background最好都不要自己设定尽量都使用TemplateBinding绑定到应用模板的TextBox上。原因嘛,当然是为了留给用户使用时尽量多的对于TextBox外观自主权,总不能说用了这个Style后边框、背景都不能设定吧。

    2.对于DockPanel,其属性LastChildFill="True",本身默认值即为“True”,可以省略不写,但是一定要知道。

    3.关于DockPanel中ScrollViewer和Button的布局,一定要先定义Button,后定义ScrollViewer。原因:就是因为第2条的LastChildFill="True"属性,如果先定义ScrollViewer,后定义Button会出现什么效果,大家可以自己试试看。

    4.对于ScrollViewer 它的名字一定要是"PART_ContentHost" 否则将Windows将无法识别导致不能显示文字。

    5.对于Button为了是Button显示正方形所以Width="{Binding ActualHeight, RelativeSource={RelativeSource Self}}" 将自己的宽绑定到了实际显示高度上。最好将Focusable设为False。原因:防止输入时点击清空按钮导致ScrollViewer失去焦点,具体表现为点击清空按钮,输入框的光标不闪烁了。FontSize尽量TemplateBinding到应用模板的目标控件上。

    将Style重构提取,最后放入资源词典以供重用。

    提炼后的Style

    [csharp] view plain copy
     
    1. <!--获得焦点后边框颜色-->  
    2. <SolidColorBrush x:Key="FocusedBorderBrush" Color="Black"/>  
    3. <!--鼠标移上时背景色-->  
    4. <SolidColorBrush x:Key="MouseOverBackground" Color="LightGray"/>  
    5.   
    6. <!--清空按钮模板样式-->  
    7. <ControlTemplate x:Key="ClearButtonTemplate" TargetType="Button">  
    8.         <Grid>  
    9.             <Rectangle x:Name="rctButton" Fill="Transparent" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"></Rectangle>  
    10.             <ContentPresenter Content="{Binding Content, RelativeSource={RelativeSource TemplatedParent}}"   
    11.                               HorizontalAlignment="Center"   
    12.                               VerticalAlignment="Center">  
    13.             </ContentPresenter>  
    14.         </Grid>  
    15.         <ControlTemplate.Triggers>  
    16.             <Trigger Property="IsMouseOver" Value="True">  
    17.                 <Setter TargetName="rctButton" Property="Fill" Value="{DynamicResource MouseOverBackground}"/>  
    18.             </Trigger>  
    19.         </ControlTemplate.Triggers>  
    20. </ControlTemplate>  
    21.   
    22. <!--带有清空按钮的TextBox风格-->  
    23. <Style x:Key="ClearButtonTextBoxStyle" TargetType="{x:Type TextBox}">  
    24.     <Setter Property="Template">  
    25.         <Setter.Value>  
    26.             <ControlTemplate TargetType="{x:Type TextBox}">  
    27.                 <Border x:Name="bdRoot"   
    28.                         BorderBrush="{TemplateBinding BorderBrush}"   
    29.                         BorderThickness="{TemplateBinding BorderThickness}"   
    30.                         SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"   
    31.                         Background="{TemplateBinding Background}">  
    32.                     <DockPanel LastChildFill="True">  
    33.                         <Button x:Name="Part_ClearButton"   
    34.                                 UC:TextBoxHelper.IsClearButton="True"  
    35.                                 Content="X"   
    36.                                 DockPanel.Dock="Right"   
    37.                                 Focusable="False"  
    38.                                 Width="{Binding ActualHeight, RelativeSource={RelativeSource Self}}"   
    39.                                 Template="{DynamicResource ClearButtonTemplate}"  
    40.                                 FontSize="{TemplateBinding FontSize}">  
    41.                         </Button>  
    42.                         <ScrollViewer x:Name="PART_ContentHost" DockPanel.Dock="Left" Background="{TemplateBinding Background}"/>  
    43.                     </DockPanel>  
    44.                 </Border>  
    45.                 <ControlTemplate.Triggers>  
    46.                     <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Text}" Value="">  
    47.                         <Setter TargetName="Part_ClearButton" Property="Visibility" Value="Collapsed" />  
    48.                     </DataTrigger>  
    49.                     <Trigger Property="IsFocused" Value="True">  
    50.                         <Setter TargetName="bdRoot" Property="BorderBrush" Value="{DynamicResource FocusedBorderBrush}"/>  
    51.                     </Trigger>  
    52.                     <Trigger Property="IsMouseOver" Value="True">  
    53.                         <Setter TargetName="bdRoot" Property="BorderBrush" Value="{DynamicResource FocusedBorderBrush}"/>  
    54.                     </Trigger>  
    55.                 </ControlTemplate.Triggers>  
    56.             </ControlTemplate>  
    57.         </Setter.Value>  
    58.     </Setter>  
    59. </Style>  

    二、附加属性类

    为什么不采用Trigger置空Text实现而要采用附加属性类?
    以下为原因,不关心的可以跳过...

    由于在Style中通过Trigger来设置TextBox的Text属性为“”暂时存在缺陷:
    当TextBox定义时如果设置Text属性将导致Trigger将Text置空失效,详细描述如下
    使用Trigger置空Text原理,我们在模板触发器中添加数据触发器
    [csharp] view plain copy
     
    1. <DataTrigger Binding="{Binding IsPressed,ElementName=Part_ClearButton}" Value="True">  
    2.                         <Setter Property="Text" Value=""/>  
    3.                     </DataTrigger>  

    当清空按钮按下时,触发
    [csharp] view plain copy
     
    1. <Setter Property="Text" Value=""/>  
    但是如果定义TextBox时
    [csharp] view plain copy
     
    1. <TextBox Text="{Binding xxx}"  
    2.                  Visibility="Collapsed"  
    3.                  Height="24"  
    4.                  Width="250"  
    5.                  Style="{DynamicResource ClearButtonTextBoxStyle}"  
    6.                  >  
    7.         </TextBox>  
    [csharp] view plain copy
     
    1. <TextBox Text="xxx"  
    2.                  Visibility="Collapsed"  
    3.                  Height="24"  
    4.                  Width="250"  
    5.                  Style="{DynamicResource ClearButtonTextBoxStyle}"  
    6.                  >  
    7.         </TextBox>  
    此时,风格中 的触发器将失效,无法清空文本内容,经尝试,Trigger、DataTrigger均会失效,而且"Text"属性不支持StoryBorad也无法通过故事板清空。

    使用附加属性清空文本内容

    原理,通过在Style的ControlTemplate中 的清空按钮Part_ClearButton
    加上自定义的附加属性,在附加属性改变的回调函数中获得依赖对象,该对象即为我们定义的Part_ClearButton
    ,获得按钮后为它的Click挂上我们的自定义事件,这样当点击清空按钮时,即可执行我们的自定义事件,只需要在事件里通过VisualTreeHelper.GetParent向上获取到应用模板的TextBox,然后通过TextBox.Clear()清空内容即可.
    下面为代码,比较简单
    [csharp] view plain copy
     
    1. public class TextBoxHelper  
    2.     {  
    3.         #region 附加属性 IsClearButton  
    4.         /// <summary>  
    5.         /// 附加属性,是否带清空按钮  
    6.         /// </summary>  
    7.         public static readonly DependencyProperty IsClearButtonProperty =  
    8.             DependencyProperty.RegisterAttached("IsClearButton", typeof(bool), typeof(TextBoxHelper), new PropertyMetadata(false, ClearText));  
    9.   
    10.   
    11.         public static bool GetIsClearButton(DependencyObject obj)  
    12.         {  
    13.             return (bool)obj.GetValue(IsClearButtonProperty);  
    14.         }  
    15.   
    16.         public static void SetIsClearButton(DependencyObject obj, bool value)  
    17.         {  
    18.             obj.SetValue(IsClearButtonProperty, value);  
    19.         }  
    20.  
    21.         #endregion  
    22.  
    23.         #region 回调函数和清空输入框内容的实现  
    24.         /// <summary>  
    25.         /// 回调函数若附加属性IsClearButton值为True则挂载清空TextBox内容的函数  
    26.         /// </summary>  
    27.         /// <param name="d">属性所属依赖对象</param>  
    28.         /// <param name="e">属性改变事件参数</param>  
    29.         private static void ClearText(DependencyObject d, DependencyPropertyChangedEventArgs e)  
    30.         {  
    31.             Button btn = d as Button;  
    32.             if (d != null && e.OldValue != e.NewValue)  
    33.             {  
    34.                 btn.Click -= ClearTextClicked;  
    35.                 if ((bool)e.NewValue)  
    36.                 {  
    37.                     btn.Click += ClearTextClicked;  
    38.                 }  
    39.             }  
    40.         }  
    41.   
    42.         /// <summary>  
    43.         /// 清空应用该附加属性的父TextBox内容函数  
    44.         /// </summary>  
    45.         /// <param name="sender">发送对象</param>  
    46.         /// <param name="e">路由事件参数</param>  
    47.         public static void ClearTextClicked(object sender, RoutedEventArgs e)  
    48.         {  
    49.             Button btn = sender as Button;  
    50.             if (btn != null)  
    51.             {  
    52.                 var parent = VisualTreeHelper.GetParent(btn);  
    53.                 while (!(parent is TextBox))  
    54.                 {  
    55.                     parent = VisualTreeHelper.GetParent(parent);  
    56.                 }  
    57.                 TextBox txt = parent as TextBox;  
    58.                 if (txt != null)  
    59.                 {  
    60.                     txt.Clear();  
    61.                 }  
    62.             }  
    63.         }  
    64.  
    65.         #endregion  
    66.     }  
    最后我们在Style所在地引入TextBoxHelper命名空间,在Style的ControlTemplate里的按钮加上改附加属性即可
    [csharp] view plain copy
     
    1. <Button x:Name="Part_ClearButton"   
    2.                                     UC:TextBoxHelper.IsClearButton="True"  
    3.                                     Content="X"   
    4.                                     DockPanel.Dock="Right"   
    5.                                     Focusable="False"  
    6.                                     Width="{Binding ActualHeight, RelativeSource={RelativeSource Self}}"   
    7.                                     Template="{DynamicResource ClearButtonTemplate}"  
    8.                                     FontSize="{TemplateBinding FontSize}">  
    9.                             </Button>  

    总结

    经过以上博文,相信大家都理解带清空按钮TextBox的实现,当然这只是一个开始!最后把源代码上传,分享给大家,有什么不足的地方欢迎指正,谢谢!
    我们的目标,帮助自己,帮助别人。
  • 相关阅读:
    Python函数式编程学习笔记
    DOS常用命令总结
    Python高级特性学习笔记
    Git bash使用中...
    《零基础入门学习Python》学习过程笔记【30模块中的函数,os模块,ospath模块中的函数(看了一点)】
    这两天将这段时间的python笔记常用知识复习了一遍...前天半夜吃了盘烤羊肉..得了急性肠炎
    《零基础入门学习Python》学习过程笔记【29对文件进行分割】(没看)
    《零基础入门学习Python》学习过程笔记【28文件】
    《零基础入门学习Python》学习过程笔记【27集合】
    用python写一个简单的用户登陆程序(要求写成函数)
  • 原文地址:https://www.cnblogs.com/sjqq/p/8666495.html
Copyright © 2011-2022 走看看