最近学习WPF,突然想做PasswordBox明文显示的样式,在网上搜索了一下,代码都有问题,不能够直接复用。所以,在同事的帮助下,做了一个明文显示样式的Demo。
首先,创建一个WPF App的项目。
打开MainWindow.xaml文件,代码如下:
1 <Window x:Class="WpfApp4.MainWindow"
2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
6 xmlns:local="clr-namespace:WpfApp4"
7 mc:Ignorable="d"
8 Title="MainWindow" Height="450" Width="800">
9 <Grid>
10 </Grid>
11 </Window>
构建Grid布局,并创建两个控件,分别为TextBox、PasswordBox,代码如下:
1 <Grid>
2 <Grid.RowDefinitions>
3 <RowDefinition Height="40"/>
4 <RowDefinition Height="auto"/>
5 </Grid.RowDefinitions>
6 <TextBox x:Name="userName" Grid.Row="0" Width="200" Height="40" HorizontalAlignment="Left"></TextBox>
7 <PasswordBox x:Name="userPwd" Grid.Row="1" Width="200" Height="40" HorizontalAlignment="Left" Style="{StaticResource PasswordBoxStyle1}" FontSize="20" VerticalAlignment="Center" local:ControlAttachProperty.PlaceHolder="请输入密码" />
8 </Grid>
此时,Style="{StaticResource PasswordBoxStyle1}" 与 local:ControlAttachProperty.PlaceHolder="请输入密码" 会报错。(现在可以删掉这两句)
现在要实现的样式效果:
1、输入密码后,点击眼睛按钮可以显示密码明文效果。
2、密码框为空且没有获取焦点时,显示提示文字。
3、密码为空时,不允许点击眼睛按钮
样式代码需要放在Window标签下Grid标签前,代码如下:
1 <Window.Resources>
2 <Style x:Key="FocusVisual">
3 <Setter Property="Control.Template">
4 <Setter.Value>
5 <ControlTemplate>
6 <Rectangle Margin="2" SnapsToDevicePixels="true" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
7 </ControlTemplate>
8 </Setter.Value>
9 </Setter>
10 </Style>
11
12 <!--眼睛按钮的样式-->
13 <Style TargetType="Button" x:Key="EyeButton">
14 <Setter Property="Template">
15 <Setter.Value>
16 <ControlTemplate TargetType="Button">
17 <Border Background="{TemplateBinding Background}" />
18 </ControlTemplate>
19 </Setter.Value>
20 </Setter>
21 </Style>
22
23 <!--PassWordBox样式-->
24 <SolidColorBrush x:Key="TextBox.Static.Border" Color="#FFABAdB3"/>
25 <SolidColorBrush x:Key="TextBox.MouseOver.Border" Color="#FF7EB4EA"/>
26 <SolidColorBrush x:Key="TextBox.Focus.Border" Color="#FF569DE5"/>
27 <Style x:Key="PasswordBoxStyle1" TargetType="{x:Type PasswordBox}">
28 <Setter Property="local:PasswordBoxHelper.Attach" Value="True"/>
29 <Setter Property="PasswordChar" Value="●"/>
30 <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
31 <Setter Property="BorderBrush" Value="{StaticResource TextBox.Static.Border}"/>
32 <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
33 <Setter Property="BorderThickness" Value="1"/>
34 <Setter Property="KeyboardNavigation.TabNavigation" Value="None"/>
35 <Setter Property="HorizontalContentAlignment" Value="Left"/>
36 <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
37 <Setter Property="AllowDrop" Value="true"/>
38 <Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
39 <Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
40 <Setter Property="Template">
41 <Setter.Value>
42 <ControlTemplate TargetType="{x:Type PasswordBox}">
43 <Border x:Name="border" CornerRadius="10" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}">
44 <!--重写构造PasswordBox-->
45 <Grid x:Name="PART_InnerGrid">
46 <Grid.ColumnDefinitions>
47 <ColumnDefinition/>
48 <ColumnDefinition Width="Auto"/>
49 </Grid.ColumnDefinitions>
50 <!--PasswordBox原有的显示节点-->
51 <ScrollViewer x:Name="PART_ContentHost" BorderThickness="0" IsTabStop="False" VerticalAlignment="Stretch" Background="{x:Null}" VerticalContentAlignment="Center" Margin="5,5"/>
52 <!--创建明文显示的TextBox-->
53 <TextBox x:Name="PART_PasswordShower" BorderBrush="Transparent" Text="{Binding Path=(local:PasswordBoxHelper.Password),RelativeSource={RelativeSource TemplatedParent}}" BorderThickness="0" Visibility="Collapsed" HorizontalAlignment="Stretch" VerticalAlignment="Center" Margin="5,5"/>
54 <!--创建提示字符-->
55 <TextBlock x:Name="PART_PlaceHolder" Text="{Binding Path=(local:ControlAttachProperty.PlaceHolder),RelativeSource={RelativeSource TemplatedParent}}" Visibility="Collapsed" Opacity="0.6" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="5,5"/>
56 <!--触发按钮显示样式-->
57 <Button x:Name="PART_ToggleEye" Grid.Column="1" Width="40" Margin="3,3" BorderThickness="0" Style="{StaticResource EyeButton}" >
58 <Button.Background>
59 <ImageBrush x:Name="img_eye" ImageSource="eye_slash.png"/>
60 </Button.Background>
61 </Button>
62 </Grid>
63 </Border>
64 <ControlTemplate.Triggers>
65 <Trigger Property="IsEnabled" Value="false">
66 <Setter Property="Opacity" TargetName="border" Value="0.56"/>
67 </Trigger>
68 <Trigger Property="IsMouseOver" Value="true">
69 <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource TextBox.MouseOver.Border}"/>
70 </Trigger>
71 <Trigger Property="IsKeyboardFocused" Value="true">
72 <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource TextBox.Focus.Border}"/>
73 </Trigger>
74 <!--密码框为空设置按钮禁用-->
75 <Trigger Property="local:PasswordBoxHelper.Password" Value="">
76 <Setter TargetName="PART_ToggleEye" Property="IsEnabled" Value="False"/>
77 </Trigger>
78 <!--按住按钮,更改按钮背景图片并设置明文框显示且密码框不显示且不占用-->
79 <Trigger Property="IsPressed" SourceName="PART_ToggleEye" Value="true">
80 <Setter TargetName="PART_ToggleEye" Property="Background">
81 <Setter.Value>
82 <ImageBrush ImageSource="eye.png"/>
83 </Setter.Value>
84 </Setter>
85 <Setter TargetName="PART_ContentHost" Property="Visibility" Value="Collapsed"/>
86 <Setter TargetName="PART_PasswordShower" Property="Visibility" Value="Visible"/>
87 </Trigger>
88 <!--密码框为空不且没有获取焦点时,设置提示文字显示-->
89 <MultiTrigger>
90 <MultiTrigger.Conditions>
91 <Condition Property="local:PasswordBoxHelper.Password" Value=""/>
92 <Condition Property="IsFocused" Value="False"/>
93 </MultiTrigger.Conditions>
94 <Setter TargetName="PART_PlaceHolder" Property="Visibility" Value="Visible"/>
95 </MultiTrigger>
96 </ControlTemplate.Triggers>
97 </ControlTemplate>
98 </Setter.Value>
99 </Setter>
100 <Style.Triggers>
101 <MultiTrigger>
102 <MultiTrigger.Conditions>
103 <Condition Property="IsInactiveSelectionHighlightEnabled" Value="true"/>
104 <Condition Property="IsSelectionActive" Value="false"/>
105 </MultiTrigger.Conditions>
106 <Setter Property="SelectionBrush" Value="{DynamicResource {x:Static SystemColors.InactiveSelectionHighlightBrushKey}}"/>
107 </MultiTrigger>
108 </Style.Triggers>
109 </Style>
110 </Window.Resources>
代码中使用了PasswordBoxHelper及ControlAttachProperty的类,PasswordBoxHelper用于获取PasswordwordBox的密码,提供给显示框显示明文密码。ControlAttachProperty用于创建提示文字的依赖属性。(注意:两个类放在MainWindow.xaml同级目录中)
PasswordBoxHelper代码如下:
1 public class PasswordBoxHelper
2 {
3 public static readonly DependencyProperty PasswordProperty =
4 DependencyProperty.RegisterAttached("Password",
5 typeof(string), typeof(PasswordBoxHelper),
6 new FrameworkPropertyMetadata(string.Empty, OnPasswordPropertyChanged));
7 public static readonly DependencyProperty AttachProperty =
8 DependencyProperty.RegisterAttached("Attach",
9 typeof(bool), typeof(PasswordBoxHelper), new PropertyMetadata(false, Attach));
10 private static readonly DependencyProperty IsUpdatingProperty =
11 DependencyProperty.RegisterAttached("IsUpdating", typeof(bool),
12 typeof(PasswordBoxHelper));
13
14 public static void SetAttach(DependencyObject dp, bool value)
15 {
16 dp.SetValue(AttachProperty, value);
17 }
18 public static bool GetAttach(DependencyObject dp)
19 {
20 return (bool)dp.GetValue(AttachProperty);
21 }
22 public static string GetPassword(DependencyObject dp)
23 {
24 return (string)dp.GetValue(PasswordProperty);
25 }
26 public static void SetPassword(DependencyObject dp, string value)
27 {
28 dp.SetValue(PasswordProperty, value);
29 }
30 private static bool GetIsUpdating(DependencyObject dp)
31 {
32 return (bool)dp.GetValue(IsUpdatingProperty);
33 }
34 private static void SetIsUpdating(DependencyObject dp, bool value)
35 {
36 dp.SetValue(IsUpdatingProperty, value);
37 }
38 private static void OnPasswordPropertyChanged(DependencyObject sender,
39 DependencyPropertyChangedEventArgs e)
40 {
41 PasswordBox passwordBox = sender as PasswordBox;
42 passwordBox.PasswordChanged -= PasswordChanged;
43 if (!(bool)GetIsUpdating(passwordBox))
44 {
45 passwordBox.Password = (string)e.NewValue;
46 }
47 passwordBox.PasswordChanged += PasswordChanged;
48 }
49 private static void Attach(DependencyObject sender,
50 DependencyPropertyChangedEventArgs e)
51 {
52 PasswordBox passwordBox = sender as PasswordBox;
53 if (passwordBox == null)
54 return;
55 if ((bool)e.OldValue)
56 {
57 passwordBox.PasswordChanged -= PasswordChanged;
58 }
59 if ((bool)e.NewValue)
60 {
61 passwordBox.PasswordChanged += PasswordChanged;
62 }
63 }
64 private static void PasswordChanged(object sender, RoutedEventArgs e)
65 {
66 PasswordBox passwordBox = sender as PasswordBox;
67 SetIsUpdating(passwordBox, true);
68 SetPassword(passwordBox, passwordBox.Password);
69 SetIsUpdating(passwordBox, false);
70 }
71 }
ControlAttachProperty代码如下:
1 public class ControlAttachProperty
2 {
3 public static string GetPlaceHolder(DependencyObject obj)
4 {
5 return (string)obj.GetValue(PlaceHolderProperty);
6 }
7
8 public static void SetPlaceHolder(DependencyObject obj, string value)
9 {
10 obj.SetValue(PlaceHolderProperty, value);
11 }
12
13 // Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
14 public static readonly DependencyProperty PlaceHolderProperty =
15 DependencyProperty.RegisterAttached("PlaceHolder", typeof(string), typeof(ControlAttachProperty), new PropertyMetadata(string.Empty));
16 }
现在,需要把之前删掉的两句(Style="{StaticResource PasswordBoxStyle1}" 与 local:ControlAttachProperty.PlaceHolder="请输入密码" )加进来。 Style是设置PasswordBox的样式, 在样式里可以重构PasswordBox。local:ControlAttachProperty.PlaceHolder是为passwordbox新增的依赖属性。
运行代码即可获取实现的效果。
需要注意的是:
①代码中用了两个图片,分别时eye.png与eye_slash.png, 在写Demo的时候,是直接放在MainWindow.xaml同级文件夹里。
②xmlns:local="clr-namespace:WpfApp4" , 这里是创建wpf项目的命名空间,喜欢复制代码的兄弟,记得更改为当前创建项目的命名空间。
代码下载:密码明文显示/隐藏