笔记来源:
WPF系统化学习-工控上位机之路-Prism-动画-ToolKit-模板-样式

1. PasswordBox 控件基础介绍
1.1 什么是 PasswordBox?
PasswordBox(密码框)是 WPF 中专门用于接收密码输入的控件,它与普通的 TextBox 最大的区别在于会隐藏用户输入的内容,以确保密码安全。
1.2 PasswordBox 的核心特点
- 内容隐藏:输入的文字显示为圆点或星号
- 安全性:不直接暴露密码内容
- 专用性:专门为密码输入场景设计
- 限制性:密码字段不是依赖属性,不能直接数据绑定
1.3 PasswordBox 与 TextBox 的对比
| 特性 | TextBox | PasswordBox |
|---|---|---|
| 显示内容 | 明文显示 | 隐藏显示(●或*) |
| 数据绑定 | 支持直接绑定 | 需要特殊处理 |
| 适用场景 | 普通文本输入 | 密码等敏感信息输入 |
| 安全性 | 较低 | 较高 |
2. PasswordBox 基本使用与布局
2.1 基础 PasswordBox 创建
🔹 最简单的密码框
<PasswordBox />
🔹 带提示文本的密码框
<PasswordBox PasswordChar="*" />
2.2 样式和布局设置
🔹 基本样式配置
<StackPanel Margin="20" Width="300">
<!-- 基础密码框样式 -->
<PasswordBox Width="200"
Height="35"
HorizontalAlignment="Left"
Margin="0,5"
PasswordChar="●"/>
</StackPanel>
🔹 完整登录表单布局
<Grid Margin="20">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- 用户名标签 -->
<TextBlock Grid.Row="0" Grid.Column="0"
Text="用户名:"
VerticalAlignment="Center"
Margin="0,0,10,10"/>
<!-- 用户名输入框 -->
<TextBox Grid.Row="0" Grid.Column="1"
Height="35"
VerticalAlignment="Center"
Margin="0,0,0,10"/>
<!-- 密码标签 -->
<TextBlock Grid.Row="1" Grid.Column="0"
Text="密码:"
VerticalAlignment="Center"
Margin="0,0,10,10"/>
<!-- 密码输入框 -->
<PasswordBox Grid.Row="1" Grid.Column="1"
Name="LoginPasswordBox"
Height="35"
VerticalAlignment="Center"
PasswordChar="•"
Margin="0,0,0,10"/>
<!-- 登录按钮 -->
<Button Grid.Row="2" Grid.Column="1"
Content="登录"
Width="80"
Height="30"
HorizontalAlignment="Right"
Click="LoginButton_Click"/>
</Grid>
🔹 美化样式设计
<StackPanel Margin="20" Width="250">
<!-- 现代风格的密码框 -->
<PasswordBox Name="StyledPasswordBox"
Height="40"
PasswordChar="●"
VerticalAlignment="Center"
FontSize="14"
Padding="10,8">
<PasswordBox.Style>
<Style TargetType="PasswordBox">
<Setter Property="BorderBrush" Value="#DDD"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Background" Value="White"/>
<Setter Property="Foreground" Value="#333"/>
</Style>
</PasswordBox.Style>
</PasswordBox>
</StackPanel>
3. PasswordBox 核心属性详解
3.1 PasswordChar 属性
🔹 设置密码显示字符
<StackPanel Margin="20">
<TextBlock Text="不同密码显示字符:" FontWeight="Bold" Margin="0,0,0,10"/>
<!-- 星号显示 -->
<PasswordBox PasswordChar="*"
Height="35"
Margin="0,5"
ToolTip="星号显示"/>
<!-- 圆点显示 -->
<PasswordBox PasswordChar="●"
Height="35"
Margin="0,5"
ToolTip="圆点显示"/>
<!-- 自定义字符显示 -->
<PasswordBox PasswordChar="★"
Height="35"
Margin="0,5"
ToolTip="星形显示"/>
<!-- 无字符显示(显示空白) -->
<PasswordBox PasswordChar=" "
Height="35"
Margin="0,5"
ToolTip="无显示字符"/>
</StackPanel>
3.2 MaxLength 属性
🔹 限制密码最大长度
<StackPanel Margin="20" Width="300">
<TextBlock Text="密码长度限制示例:" FontWeight="Bold" Margin="0,0,0,10"/>
<!-- 限制6位密码(常用) -->
<PasswordBox Name="SixDigitPassword"
MaxLength="6"
Height="35"
PasswordChar="●"
Margin="0,5"
ToolTip="最多6位字符"/>
<!-- 限制8位密码 -->
<PasswordBox Name="EightDigitPassword"
MaxLength="8"
Height="35"
PasswordChar="●"
Margin="0,5"
ToolTip="最多8位字符"/>
<!-- 限制16位密码 -->
<PasswordBox Name="LongPassword"
MaxLength="16"
Height="35"
PasswordChar="●"
Margin="0,5"
ToolTip="最多16位字符"/>
<!-- 实时显示输入长度 -->
<TextBlock Name="PasswordLengthDisplay"
Text="已输入: 0/8 位"
HorizontalAlignment="Right"
Foreground="Gray"
Margin="0,5,0,0"/>
</StackPanel>
// 后台代码:实时监控密码长度
private void EightDigitPassword_PasswordChanged(object sender, RoutedEventArgs e)
{
int currentLength = EightDigitPassword.Password.Length;
PasswordLengthDisplay.Text = $"已输入: {currentLength}/8 位";
// 根据长度改变颜色提示
if (currentLength >= 8)
{
PasswordLengthDisplay.Foreground = Brushes.Red;
}
else if (currentLength >= 6)
{
PasswordLengthDisplay.Foreground = Brushes.Orange;
}
else
{
PasswordLengthDisplay.Foreground = Brushes.Gray;
}
}
3.3 Password 属性
🔹 获取和设置密码值
// 获取密码值(在后台代码中)
string password = passwordBox.Password;
// 设置密码值(通常用于重置或测试)
passwordBox.Password = "defaultPassword";
4. PasswordBox 的数据绑定问题与解决方案
4.1 为什么不能直接数据绑定?
🔹 问题根源
PasswordBox.Password 属性不是依赖属性,这意味着:
- 不能使用
{Binding}直接绑定 - 不能自动更新数据源
- 需要手动处理数据同步
🔹 错误示例
<!-- 这样是行不通的! -->
<PasswordBox Password="{Binding UserPassword}" />
4.2 解决方案一:使用 PasswordChanged 事件
🔹 基本事件处理
<PasswordBox Name="EventPasswordBox"
PasswordChanged="EventPasswordBox_PasswordChanged"
Height="35"
Margin="10"/>
// 后台代码处理密码变化
private void EventPasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
{
string password = EventPasswordBox.Password;
// 更新ViewModel或执行其他逻辑
if (viewModel != null)
{
viewModel.Password = password;
}
// 可以添加其他逻辑,如密码强度检查
CheckPasswordStrength(password);
}
private void CheckPasswordStrength(string password)
{
// 简单的密码强度检查逻辑
if (password.Length == 0)
{
strengthDisplay.Text = "请输入密码";
strengthDisplay.Foreground = Brushes.Gray;
}
else if (password.Length < 6)
{
strengthDisplay.Text = "密码太短";
strengthDisplay.Foreground = Brushes.Red;
}
else if (password.Length < 8)
{
strengthDisplay.Text = "密码强度一般";
strengthDisplay.Foreground = Brushes.Orange;
}
else
{
strengthDisplay.Text = "密码强度足够";
strengthDisplay.Foreground = Brushes.Green;
}
}
4.3 解决方案二:使用附加属性实现绑定
🔹 创建附加属性辅助类
public static class PasswordBoxHelper
{
public static readonly DependencyProperty PasswordProperty =
DependencyProperty.RegisterAttached(
"Password",
typeof(string),
typeof(PasswordBoxHelper),
new FrameworkPropertyMetadata(string.Empty, OnPasswordPropertyChanged));
public static readonly DependencyProperty AttachProperty =
DependencyProperty.RegisterAttached(
"Attach",
typeof(bool),
typeof(PasswordBoxHelper),
new PropertyMetadata(false, Attach));
private static readonly DependencyProperty IsUpdatingProperty =
DependencyProperty.RegisterAttached(
"IsUpdating",
typeof(bool),
typeof(PasswordBoxHelper));
public static void SetAttach(DependencyObject dp, bool value)
{
dp.SetValue(AttachProperty, value);
}
public static bool GetAttach(DependencyObject dp)
{
return (bool)dp.GetValue(AttachProperty);
}
public static string GetPassword(DependencyObject dp)
{
return (string)dp.GetValue(PasswordProperty);
}
public static void SetPassword(DependencyObject dp, string value)
{
dp.SetValue(PasswordProperty, value);
}
private static bool GetIsUpdating(DependencyObject dp)
{
return (bool)dp.GetValue(IsUpdatingProperty);
}
private static void SetIsUpdating(DependencyObject dp, bool value)
{
dp.SetValue(IsUpdatingProperty, value);
}
private static void OnPasswordPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
PasswordBox passwordBox = sender as PasswordBox;
passwordBox.PasswordChanged -= PasswordChanged;
if (!GetIsUpdating(passwordBox))
{
passwordBox.Password = (string)e.NewValue;
}
passwordBox.PasswordChanged += PasswordChanged;
}
private static void Attach(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
PasswordBox passwordBox = sender as PasswordBox;
if (passwordBox == null)
return;
if ((bool)e.OldValue)
{
passwordBox.PasswordChanged -= PasswordChanged;
}
if ((bool)e.NewValue)
{
passwordBox.PasswordChanged += PasswordChanged;
}
}
private static void PasswordChanged(object sender, RoutedEventArgs e)
{
PasswordBox passwordBox = sender as PasswordBox;
SetIsUpdating(passwordBox, true);
SetPassword(passwordBox, passwordBox.Password);
SetIsUpdating(passwordBox, false);
}
}
🔹 在 XAML 中使用附加属性绑定
<PasswordBox Name="BindablePasswordBox"
Height="35"
Margin="10"
local:PasswordBoxHelper.Attach="True"
local:PasswordBoxHelper.Password="{Binding UserPassword, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
4.4 解决方案三:使用行为(Behavior)
🔹 需要安装 Microsoft.Xaml.Behaviors.Wpf
<PasswordBox Height="35" Margin="10">
<i:Interaction.Behaviors>
<local:PasswordBoxBindingBehavior Password="{Binding UserPassword, Mode=TwoWay}"/>
</i:Interaction.Behaviors>
</PasswordBox>
public class PasswordBoxBindingBehavior : Behavior<PasswordBox>
{
public static readonly DependencyProperty PasswordProperty =
DependencyProperty.Register(
"Password",
typeof(string),
typeof(PasswordBoxBindingBehavior),
new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnPasswordPropertyChanged));
public string Password
{
get { return (string)GetValue(PasswordProperty); }
set { SetValue(PasswordProperty, value); }
}
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.PasswordChanged += PasswordBox_PasswordChanged;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.PasswordChanged -= PasswordBox_PasswordChanged;
}
private void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
{
Password = AssociatedObject.Password;
}
private static void OnPasswordPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var behavior = d as PasswordBoxBindingBehavior;
if (behavior != null && behavior.AssociatedObject != null)
{
if (!behavior.AssociatedObject.Password.Equals(e.NewValue))
{
behavior.AssociatedObject.Password = e.NewValue as string;
}
}
}
}
5. 完整登录系统实现
5.1 完整的登录界面
<Window x:Class="WpfApp.LoginWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="用户登录"
Height="400"
Width="350"
WindowStartupLocation="CenterScreen"
ResizeMode="NoResize">
<Grid Background="#F5F5F5">
<Border Background="White"
Margin="30"
CornerRadius="10"
BorderBrush="#E0E0E0"
BorderThickness="1"
Padding="30">
<StackPanel>
<!-- 标题 -->
<TextBlock Text="用户登录"
FontSize="24"
FontWeight="Bold"
HorizontalAlignment="Center"
Margin="0,0,0,30"
Foreground="#333"/>
<!-- 用户名输入 -->
<TextBlock Text="用户名"
FontWeight="SemiBold"
Margin="0,0,0,5"
Foreground="#666"/>
<TextBox Name="UsernameTextBox"
Height="40"
Padding="10"
FontSize="14"
BorderBrush="#DDD"
BorderThickness="1"
Background="#FAFAFA"/>
<!-- 密码输入 -->
<TextBlock Text="密码"
FontWeight="SemiBold"
Margin="0,15,0,5"
Foreground="#666"/>
<PasswordBox Name="LoginPasswordBox"
Height="40"
PasswordChar="●"
Padding="10"
FontSize="14"
BorderBrush="#DDD"
BorderThickness="1"
Background="#FAFAFA"
PasswordChanged="LoginPasswordBox_PasswordChanged"/>
<!-- 密码强度提示 -->
<TextBlock Name="PasswordStrengthText"
FontSize="11"
Margin="0,5,0,0"
HorizontalAlignment="Right"/>
<!-- 记住密码选项 -->
<CheckBox Name="RememberPasswordCheckBox"
Content="记住密码"
Margin="0,20,0,10"
Foreground="#666"/>
<!-- 登录按钮 -->
<Button Content="登录"
Height="45"
Background="#2196F3"
Foreground="White"
FontSize="16"
FontWeight="SemiBold"
BorderThickness="0"
Cursor="Hand"
Click="LoginButton_Click"
Margin="0,10,0,0"/>
<!-- 注册链接 -->
<TextBlock Text="没有账号?立即注册"
HorizontalAlignment="Center"
Margin="0,20,0,0"
Foreground="#2196F3"
Cursor="Hand"
TextDecorations="Underline"
MouseLeftButtonDown="RegisterText_MouseLeftButtonDown"/>
</StackPanel>
</Border>
</Grid>
</Window>
5.2 后台逻辑实现
public partial class LoginWindow : Window
{
public LoginWindow()
{
InitializeComponent();
// 设置焦点到用户名输入框
UsernameTextBox.Focus();
}
private void LoginPasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
{
UpdatePasswordStrength(LoginPasswordBox.Password);
}
private void UpdatePasswordStrength(string password)
{
if (string.IsNullOrEmpty(password))
{
PasswordStrengthText.Text = "";
return;
}
// 简单的密码强度评估
int strength = 0;
// 长度检查
if (password.Length >= 8) strength++;
if (password.Length >= 12) strength++;
// 复杂度检查
if (password.Any(char.IsUpper)) strength++;
if (password.Any(char.IsLower)) strength++;
if (password.Any(char.IsDigit)) strength++;
if (password.Any(ch => !char.IsLetterOrDigit(ch))) strength++;
// 更新显示
switch (strength)
{
case 0:
case 1:
PasswordStrengthText.Text = "密码强度: 弱";
PasswordStrengthText.Foreground = Brushes.Red;
break;
case 2:
case 3:
PasswordStrengthText.Text = "密码强度: 中";
PasswordStrengthText.Foreground = Brushes.Orange;
break;
case 4:
case 5:
PasswordStrengthText.Text = "密码强度: 强";
PasswordStrengthText.Foreground = Brushes.Green;
break;
default:
PasswordStrengthText.Text = "密码强度: 很强";
PasswordStrengthText.Foreground = Brushes.DarkGreen;
break;
}
}
private void LoginButton_Click(object sender, RoutedEventArgs e)
{
string username = UsernameTextBox.Text;
string password = LoginPasswordBox.Password;
// 输入验证
if (string.IsNullOrWhiteSpace(username))
{
MessageBox.Show("请输入用户名", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
UsernameTextBox.Focus();
return;
}
if (string.IsNullOrEmpty(password))
{
MessageBox.Show("请输入密码", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
LoginPasswordBox.Focus();
return;
}
// 模拟登录验证
bool loginSuccess = AuthenticateUser(username, password);
if (loginSuccess)
{
// 登录成功处理
MessageBox.Show("登录成功!", "成功", MessageBoxButton.OK, MessageBoxImage.Information);
// 如果选择了记住密码,保存相关信息
if (RememberPasswordCheckBox.IsChecked == true)
{
SaveLoginInfo(username);
}
// 打开主窗口
MainWindow mainWindow = new MainWindow();
mainWindow.Show();
this.Close();
}
else
{
// 登录失败处理
MessageBox.Show("用户名或密码错误", "登录失败", MessageBoxButton.OK, MessageBoxImage.Error);
LoginPasswordBox.Password = ""; // 清空密码框
LoginPasswordBox.Focus();
}
}
private bool AuthenticateUser(string username, string password)
{
// 这里应该是实际的用户验证逻辑
// 例如:数据库查询、API调用等
// 模拟验证 - 实际项目中需要替换为真实逻辑
return username == "admin" && password == "123456";
}
private void SaveLoginInfo(string username)
{
// 保存登录信息的逻辑
// 注意:实际项目中密码不应该明文保存
Properties.Settings.Default.LastUsername = username;
Properties.Settings.Default.RememberPassword = true;
Properties.Settings.Default.Save();
}
private void RegisterText_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
// 打开注册窗口
RegisterWindow registerWindow = new RegisterWindow();
registerWindow.ShowDialog();
}
}
6. 安全最佳实践
6.1 密码安全处理
🔹 不要在代码中硬编码密码
// 错误做法
string hardcodedPassword = "myPassword123";
// 正确做法 - 从安全配置中获取
string passwordFromConfig = ConfigurationManager.AppSettings["EncryptedPassword"];
🔹 使用安全字符串
using System.Security;
private SecureString GetSecurePassword(PasswordBox passwordBox)
{
return passwordBox.SecurePassword;
}
private void ProcessSecurePassword(PasswordBox passwordBox)
{
SecureString securePassword = passwordBox.SecurePassword;
// 使用安全字符串进行处理
IntPtr unmanagedString = IntPtr.Zero;
try
{
unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(securePassword);
string password = Marshal.PtrToStringUni(unmanagedString);
// 处理密码...
}
finally
{
Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString);
}
}
6.2 输入验证
🔹 客户端验证
private bool ValidatePasswordInput(string password)
{
if (string.IsNullOrEmpty(password))
{
ShowValidationError("密码不能为空");
return false;
}
if (password.Length < 6)
{
ShowValidationError("密码长度至少6位");
return false;
}
if (password.Length > 20)
{
ShowValidationError("密码长度不能超过20位");
return false;
}
// 更多验证规则...
return true;
}
private void ShowValidationError(string message)
{
// 显示验证错误信息
ValidationTextBlock.Text = message;
ValidationTextBlock.Foreground = Brushes.Red;
ValidationTextBlock.Visibility = Visibility.Visible;
}
7. 高级功能扩展
7.1 显示/隐藏密码功能
<Grid Width="300" Height="40" Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<PasswordBox Name="TogglePasswordBox"
Grid.Column="0"
PasswordChar="●"
Padding="10,8"/>
<Button Grid.Column="1"
Content="👁️"
Width="40"
Background="Transparent"
BorderThickness="0"
Click="TogglePasswordVisibility_Click"
ToolTip="显示/隐藏密码"/>
</Grid>
private void TogglePasswordVisibility_Click(object sender, RoutedEventArgs e)
{
// 切换密码显示/隐藏的逻辑
// 注意:实际实现需要更复杂的处理
}
7.2 密码生成器
<StackPanel Margin="20" Width="300">
<PasswordBox Name="GeneratedPasswordBox"
Height="35"
PasswordChar="●"
Margin="0,0,0,10"/>
<Button Content="生成随机密码"
Click="GenerateRandomPassword_Click"
Height="30"/>
</StackPanel>
private void GenerateRandomPassword_Click(object sender, RoutedEventArgs e)
{
string randomPassword = GenerateSecurePassword(12);
GeneratedPasswordBox.Password = randomPassword;
}
private string GenerateSecurePassword(int length)
{
const string validChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*";
StringBuilder result = new StringBuilder();
Random random = new Random();
for (int i = 0; i < length; i++)
{
result.Append(validChars[random.Next(validChars.Length)]);
}
return result.ToString();
}
WPF PasswordBox 使用与安全实践
85

被折叠的 条评论
为什么被折叠?



