如何优雅的为文本框添加清除按钮
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;
}
}
OnIsClearChanged
当 IsClear
属性的值发生变化,调用这个方法。
当 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
值为 True
并 Text
值为 {x:Null}
则显示清除按钮。
附件属性 helpers:ElementHelper.IsClear
值为 True
并 Text
值为空字符串 ""
则显示清除按钮。
<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 库,如果直接拷贝使用,需要确保将相关的资源和控件进行正确的替换和配置。
如果你对此有任何更好的想法或建议,我们将非常感激并乐于听取。
原文链接: https://github.com/WPFDevelopersOrg/WPFDevelopers
[2]码云链接: https://gitee.com/WPFDevelopersOrg/WPFDevelopers