本文介紹下PasswordBox進行數據綁定的方法,本文參考鏈接。
本文完整示例程序見GitHub。
問題描述
PasswordBox的Password屬性不是依賴屬性,因此無法進行數據綁定。
解決辦法
該問題的解決辦法有多種,本文介紹如何通過添加附加屬性解決該問題。
附加屬性是說一個屬性本不屬於某個對象,但由於某種需求附加到該對象上,通過附加屬性可以實現將屬性與宿主解耦的目的。附加屬性本質上就是依賴屬性,只是它們在屬性包裝器和注冊時有區別。注冊附加屬性使用RegisterAttached方法,注冊依賴屬性使用Register方法,這兩個方法的參數差別並不大。
首先添加一個PasswordBoxBindingHelper類,該類包含一個附加屬性(snippet:propa+兩次tab),通過設置該屬性的PropertyChangedCallback將改變通知到PasswordBox.Password,並通過添加對PasswordBox.PasswordChanged事件的響應來響應PasswordBox.Password的改變。有了該附加屬性,即可進行數據綁定。
public static string GetPasswordContent(DependencyObject obj) => (string)obj.GetValue(PasswordContentProperty);
public static void SetPasswordContent(DependencyObject obj, string value) => obj.SetValue(PasswordContentProperty, value);
public static readonly DependencyProperty PasswordContentProperty =
DependencyProperty.RegisterAttached("PasswordContent", typeof(string), typeof(PasswordBoxBindingHelper),
new PropertyMetadata(string.Empty, OnPasswordContentPropertyChanged));
private static void OnPasswordContentPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var box = d as PasswordBox;
box.PasswordChanged -= OnPasswordChanged;
var password = (string)e.NewValue;
if (box != null && box.Password != password)
box.Password = password;
box.PasswordChanged += OnPasswordChanged;
}
private static void OnPasswordChanged(object sender, RoutedEventArgs e)
{
var box = sender as PasswordBox;
SetPasswordContent(box, box.Password);
}
然后在View中使用該附加屬性進行數據綁定,本文示例中主窗口包含一個PasswordBox控件及一個Button按鈕:
// xaml 綁定附加屬性
<Window ...
xmlns:local="clr-namespace:PasswordBoxBinding"
Title="PasswordBoxBinding" Height="300" Width="450" WindowStartupLocation="CenterScreen">
<Grid>
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
<PasswordBox MinWidth="200" Height="30" BorderBrush="LightGray" BorderThickness="2"
local:PasswordBoxBindingHelper.PasswordContent="{Binding Password,Mode=TwoWay}"/>
<Rectangle Width="20"/>
<Button Width="80" Height="30" Content="查看密碼" Command="{Binding ClickedCommand}"/>
</StackPanel>
</Grid>
</Window>
//xaml.cs 設置綁定源
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainWindowViewModel();
}
最后創建ViewModel進行邏輯處理:
// ViewModel
public class MainWindowViewModel : INotifyPropertyChanged
{
public string Password
{
get => _password;
set
{
_password = value;
OnPropertyChanged();
}
}
public DelegateCommand ClickedCommand => _clickedCommand ?? (_clickedCommand = new DelegateCommand { ExecuteAction = OnClicked });
// 使用CallerMemberName特性簡化代碼,並可以避免手動輸入錯誤
public void OnPropertyChanged([CallerMemberName] string name = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
private void OnClicked(object o) => MessageBox.Show($"password: {Password}");
public event PropertyChangedEventHandler PropertyChanged;
private DelegateCommand _clickedCommand;
private string _password;
}
// 實現ICommand
public class DelegateCommand : ICommand
{
public bool CanExecute(object parameter) => CanExecuteAction?.Invoke(parameter) ?? true;
public void Execute(object parameter) => ExecuteAction?.Invoke(parameter);
public event EventHandler CanExecuteChanged;
public Action<object> ExecuteAction { get; set; }
public Func<object, bool> CanExecuteAction { get; set; }
}