一、属性样式及资源定义
1、加载静态图标
2、定义ToggleButtonStyle样式
<!-- 图标控件预置属性及模板相关样式 -->
<Style x:Key="ToggleButtonStyle" TargetType="ToggleButton">
<Setter Property="Margin" Value="5,0" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Image x:Name="IconImage" Source="./Resources/eye_close.png" />
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="IconImage" Property="Source" Value="./Resources/eye_open.png" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
二、PasswordBox实现
1、预置属性及模板相关样式
<Style TargetType="PasswordBox">
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Padding" Value="5,0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="PasswordBox">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="4">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="32" />
</Grid.ColumnDefinitions>
<ScrollViewer Grid.Column="0" Name="PART_ContentHost"></ScrollViewer>
<TextBlock Grid.Column="0" x:Name="MessageTextBlock"
Text="{TemplateBinding Tag}"
FontSize="{TemplateBinding FontSize}"
Foreground="{TemplateBinding Foreground}"
Padding="{TemplateBinding Padding}"
VerticalAlignment="Center"
Visibility="Hidden"></TextBlock>
<!-- Text:对PasswordBox对象Password密文属性进行绑定 -->
<!-- MaxLength:对PasswordBox对象MaxLength长度属性进行绑定-->
<TextBox Grid.Column="0" VerticalContentAlignment="Center"
BorderThickness="0" Visibility="Hidden"
FontSize="{TemplateBinding FontSize}"
Foreground="{TemplateBinding Foreground}"
Padding="{TemplateBinding Padding}"
Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=PasswordBox},Path=(local:PasswordBoxAttached.Password)}"
MaxLength="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=PasswordBox},Path=(PasswordBox.MaxLength)}"
x:Name="PasswordTextBox"></TextBox>
<!-- 图标控件 -->
<ToggleButton Grid.Column="1" x:Name="CheckedToggleButton" Focusable="False"
Style="{StaticResource ToggleButtonStyle}"></ToggleButton>
</Grid>
</Border>
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsKeyboardFocused" Value="False"></Condition>
<Condition Property="local:PasswordBoxAttached.Password" Value=""></Condition>
</MultiTrigger.Conditions>
<Setter TargetName="MessageTextBlock" Property="Visibility" Value="Visible"></Setter>
<Setter TargetName="MessageTextBlock" Property="Opacity" Value="0.5"></Setter>
</MultiTrigger>
<DataTrigger Binding="{Binding ElementName=CheckedToggleButton,Path=IsChecked}" Value="True">
<Setter TargetName="PasswordTextBox" Property="Visibility" Value="Visible"></Setter>
<Setter TargetName="PART_ContentHost" Property="Visibility" Value="Hidden"></Setter>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
此处,传递的相关属性如下:
- TextBox.Text属性:绑定PasswordBoxAttached.Password依赖属性
- TextBox.MaxLength属性:绑定PasswordBox.MaxLength属性
这里笔者用到了MultiTrigger属性发生变化条件触发器以及数据发生变化条件触发器进行密文及明文的切换过程实现。
2、依赖属性的定义
public class PasswordBoxAttached
{
/// <summary>
/// 密文内容属性
/// </summary>
public static readonly DependencyProperty PasswordProperty = DependencyProperty.RegisterAttached(
"Password", typeof(string), typeof(PasswordBoxAttached),
new PropertyMetadata("", PasswordPropertyChangedCallback));
/// <summary>
/// Get密文字段
/// </summary>
[AttachedPropertyBrowsableForType(typeof(PasswordBox))]
public static string GetPassword(DependencyObject obj)
{
return (string)obj.GetValue(PasswordProperty);
}
/// <summary>
/// Set密文字段
/// </summary>
public static void SetPassword(DependencyObject obj, string value)
{
obj.SetValue(PasswordProperty, value);
}
/// <summary>
/// 密文回调事件
/// </summary>
/// <param name="d"></param>
/// <param name="e"></param>
private static void PasswordPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is PasswordBox pb)
{
pb.Password = e.NewValue.ToString();
pb.GetType().GetMethod("Select",
BindingFlags.Instance | BindingFlags.NonPublic).Invoke(pb, new object[] { pb.Password.Length, 0 });
}
}
}
3、场景应用
(1) XAML前端
<Window x:Class="WpfAppDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfAppDemo"
mc:Ignorable="d"
WindowStyle="None" WindowStartupLocation="CenterScreen"
AllowsTransparency="True" Background="Transparent"
Title="MainWindow" Height="200" Width="350" >
<Border BorderBrush="#20333333" BorderThickness="1" CornerRadius="8" Background="White" Margin="0">
<Border.Effect>
<DropShadowEffect BlurRadius="8" Color="#20333333" ShadowDepth="0"></DropShadowEffect>
</Border.Effect>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<TextBlock Text="密码:" Padding="0,10,0,10" HorizontalAlignment="Center" VerticalAlignment="Center" />
<PasswordBox x:Name="tBoxPwd" HorizontalAlignment="Center" VerticalAlignment="Center" MaxLength="10"
Width="120" PasswordChanged="LoginPasswordBox_OnPasswordChanged" />
</StackPanel>
</Border>
</Window>
(2) 内容发生改变事件
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
//private readonly PasswordBoxViewModel PwdViewModel;
public MainWindow()
{
InitializeComponent();
//PwdViewModel = new PasswordBoxViewModel();
//DataContext = PwdViewModel;
}
/// <summary>
/// 明文显示
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void LoginPasswordBox_OnPasswordChanged(object sender, RoutedEventArgs e)
{
if (sender is PasswordBox pb)
{
PasswordBoxAttached.SetPassword(pb, pb.Password);
}
}
}
最终效果: