如何优雅的为文本框添加清除按钮

科技   2024-07-16 08:03   北京  

 如何优雅的为文本框添加清除按钮

ElementHelper

作 者:WPFDevelopersOrg - 驚鏵

原文链接[1]:https://github.com/WPFDevelopersOrg/WPFDevelopers

码云链接[2]:https://gitee.com/WPFDevelopersOrg/WPFDevelopers

  • 框架支持.NET4 至 .NET8
  • Visual Studio 2022;

如何优雅的为文本框添加清除按钮?

答:一般情况都会选择自定义控件,这样的话不清真,所以我们通过附加属性,可以让你的文本框变得更简洁。

新建一个 ElementHelper 辅助类

附加属性定义

   public static readonly DependencyProperty IsClearProperty =
       DependencyProperty.RegisterAttached("IsClear"typeof(bool), typeof(ElementHelper),
           new PropertyMetadata(false, OnIsClearChanged));

定义IsClearProperty 附加属性默认是 false

附加属性变更处理

   private static void OnIsClearChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
   {
       var button = d as Button;
       if (button != null)
       {
           if ((bool)e.NewValue)
               button.Click += ButtonClear_Click;
           else
               button.Click -= ButtonClear_Click;
       }
   }

OnIsClearChangedIsClear 属性的值发生变化,调用这个方法。

IsClear 属性的值为 true,则监听按钮的 Click 事件;如果为 false,移除监听事件。

按钮点击事件处理

private static void ButtonClear_Click(object sender, RoutedEventArgs e)
{
    if (sender is Button button)
    {
        if (button.TemplatedParent is TextBox textBox)
        {
            textBox.Clear();
        }
    }
}

当按钮被点击时,它检查按钮的模板父级是否是一个 TextBox

如果是,则调用 Clear() 方法来清除文本框的内容。

1)ElementHelper.cs 代码如下:

public class ElementHelper : DependencyObject
{
    public static readonly DependencyProperty IsClearProperty =
      DependencyProperty.RegisterAttached("IsClear"typeof(bool), typeof(ElementHelper),
          new PropertyMetadata(false, OnIsClearChanged));

    public static void SetIsClear(UIElement element, bool value)
    {
        element.SetValue(IsClearProperty, value);
    }

    public static bool GetIsClear(UIElement element)
    {
        return (bool)element.GetValue(IsClearProperty);
    }

    private static void OnIsClearChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var button = d as Button;
        if (button != null)
        {
            if ((bool)e.NewValue)
                button.Click += ButtonClear_Click;
            else
                button.Click -= ButtonClear_Click;
        }
    }

    private static void ButtonClear_Click(object sender, RoutedEventArgs e)
    {
        if (sender is Button button)
        {
            if (button.TemplatedParent is TextBox textBox)
            {
                textBox.Clear();
            }
        }
    }
}

2)TextBoxStyle.xaml 代码如下:

附件属性 helpers:ElementHelper.IsClear 值为 TrueText 值为 {x:Null} 则显示清除按钮。

附件属性 helpers:ElementHelper.IsClear 值为 TrueText 值为空字符串 ""则显示清除按钮。

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:controls="clr-namespace:WPFDevelopers.Controls"
    xmlns:helpers="clr-namespace:WPFDevelopers.Helpers"
    xmlns:resx="clr-namespace:WPFDevelopers">

    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="../Themes/Basic/ControlBasic.xaml" />
        <ResourceDictionary Source="../Themes/Basic/Animations.xaml" />
    </ResourceDictionary.MergedDictionaries>
    <Style
        x:Key="WD.DefaultTextBox"
        BasedOn="{StaticResource WD.ControlBasicStyle}"
        TargetType="{x:Type TextBox}">

        <Setter Property="KeyboardNavigation.TabNavigation" Value="None" />
        <Setter Property="FocusVisualStyle" Value="{x:Null}" />
        <Setter Property="VerticalContentAlignment" Value="Center" />
        <Setter Property="Padding" Value="{StaticResource WD.DefaultPadding}" />
        <Setter Property="AllowDrop" Value="True" />
        <Setter Property="BorderThickness" Value="1" />
        <Setter Property="BorderBrush" Value="{DynamicResource WD.BaseSolidColorBrush}" />
        <Setter Property="Background" Value="{DynamicResource WD.BackgroundSolidColorBrush}" />
        <Setter Property="SelectionBrush" Value="{DynamicResource WD.WindowBorderBrushSolidColorBrush}" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type TextBox}">
                    <Border
                        Name="PART_Border"
                        Width="{TemplateBinding Width}"
                        Height="{TemplateBinding Height}"
                        Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}"
                        CornerRadius="{Binding Path=(helpers:ElementHelper.CornerRadius), RelativeSource={RelativeSource TemplatedParent}}">

                        <controls:SmallPanel>
                            <TextBlock
                                x:Name="PART_Watermark"
                                Margin="{TemplateBinding Padding}"
                                Padding="1,0"
                                HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                FontSize="{StaticResource WD.NormalFontSize}"
                                Foreground="{DynamicResource WD.RegularTextSolidColorBrush}"
                                Text="{Binding Path=(helpers:ElementHelper.Watermark), RelativeSource={RelativeSource TemplatedParent}}"
                                Visibility="Collapsed" />

                            <ScrollViewer x:Name="PART_ContentHost" />
                            <Button
                                x:Name="PART_CloseButton"
                                Width="20"
                                Height="20"
                                Margin="0,0,4,0"
                                HorizontalAlignment="Right"
                                helpers:ElementHelper.IsClear="{Binding Path=(helpers:ElementHelper.IsClear), RelativeSource={RelativeSource TemplatedParent}}"
                                helpers:ElementHelper.IsRound="True"
                                Style="{StaticResource WD.NormalButton}"
                                ToolTip="{Binding [Clear], Source={x:Static resx:LanguageManager.Instance}}"
                                Visibility="Collapsed">

                                <controls:PathIcon
                                    Width="10"
                                    Height="10"
                                    Foreground="{DynamicResource WD.SecondaryTextSolidColorBrush}"
                                    Kind="WindowClose" />

                            </Button>
                        </controls:SmallPanel>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="Text" Value="">
                            <Setter TargetName="PART_Watermark" Property="Visibility" Value="Visible" />
                        </Trigger>
                        <Trigger Property="Text" Value="{x:Null}">
                            <Setter TargetName="PART_Watermark" Property="Visibility" Value="Visible" />
                        </Trigger>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter Property="BorderBrush" Value="{DynamicResource WD.PrimaryNormalSolidColorBrush}" />
                        </Trigger>
                        <Trigger Property="helpers:ElementHelper.IsClear" Value="True">
                            <Setter TargetName="PART_CloseButton" Property="Visibility" Value="Visible" />
                        </Trigger>
                        <MultiTrigger>
                            <MultiTrigger.Conditions>
                                <Condition Property="helpers:ElementHelper.IsClear" Value="True" />
                                <Condition Property="Text" Value="{x:Null}" />
                            </MultiTrigger.Conditions>
                            <Setter TargetName="PART_CloseButton" Property="Visibility" Value="Collapsed" />
                        </MultiTrigger>
                        <MultiTrigger>
                            <MultiTrigger.Conditions>
                                <Condition Property="helpers:ElementHelper.IsClear" Value="True" />
                                <Condition Property="Text" Value="" />
                            </MultiTrigger.Conditions>
                            <Setter TargetName="PART_CloseButton" Property="Visibility" Value="Collapsed" />
                        </MultiTrigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    
</Style>
    <Style BasedOn="{StaticResource WD.DefaultTextBox}" TargetType="{x:Type TextBox}" />
</ResourceDictionary>

当你已经成功实现了自定义的 Textbox 控件后,实现 PasswordBox 控件也可以按照类似的方法。文中 XAML 中使用 WPFDevelopers 库,如果直接拷贝使用,需要确保将相关的资源和控件进行正确的替换和配置。

如果你对此有任何更好的想法或建议,我们将非常感激并乐于听取。

参考资料
[1]

原文链接: https://github.com/WPFDevelopersOrg/WPFDevelopers

[2]

码云链接: https://gitee.com/WPFDevelopersOrg/WPFDevelopers


WPF开发者
「WPF开发者」现役微软MVP,专注 WPF 和 Avalonia 技术分享与传播。
 最新文章