将SecurePassword绑定到ViewModel

我尝试使用自定义BehaviorPasswordBoxSecurePassword属性绑定到我的ViewModel 。 可悲的是,它无法正常工作。

基本上我向Behavior添加了一个属性,其中包含我的ViewModel的target属性。

任何想法为什么它不起作用?

PS:我目前正在回家的路上没有笔记本电脑,我将在大约15分钟内用我的代码更新问题。 但如果有人发表想法或某事,那会很好。

编辑

正如我所承诺的,这里有一些代码:)

Behavior第一:

 using System; using System.Collections.Generic; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; using System.Windows.Interactivity; using System.Security; namespace Knerd.Behaviors { public class PasswordChangedBehavior : Behavior { protected override void OnAttached() { AssociatedObject.PasswordChanged += AssociatedObject_PasswordChanged; base.OnAttached(); } private void AssociatedObject_PasswordChanged(object sender, RoutedEventArgs e) { if (AssociatedObject.Password != null) TargetPassword = AssociatedObject.SecurePassword; } protected override void OnDetaching() { AssociatedObject.PasswordChanged -= AssociatedObject_PasswordChanged; base.OnDetaching(); } public SecureString TargetPassword { get { return (SecureString)GetValue(TargetPasswordProperty); } set { SetValue(TargetPasswordProperty, value); } } // Using a DependencyProperty as the backing store for TargetPassword. This enables animation, styling, binding, etc... public static readonly DependencyProperty TargetPasswordProperty = DependencyProperty.Register("TargetPassword", typeof(SecureString), typeof(PasswordChangedBehavior), new PropertyMetadata(default(SecureString))); } } 

PasswordBox

      

最后,我的ViewModel的一部分。

 private SecureString password; public SecureString Password { get { return password; } set { if (password != value) { password = value; OnPropertyChanged("Password"); } } } 

我希望任何人都可以提供帮助,因为我使用的是代码隐藏版本,但我宁愿也不会。

编辑2

实际上不起作用的是, TargetPassword属性不会更新我的ViewModel的属性

创建附加属性

 public static class PasswordBoxAssistant { public static readonly DependencyProperty BoundPassword = DependencyProperty.RegisterAttached("BoundPassword", typeof(string), typeof(PasswordBoxAssistant), new PropertyMetadata(string.Empty, OnBoundPasswordChanged)); public static readonly DependencyProperty BindPassword = DependencyProperty.RegisterAttached( "BindPassword", typeof (bool), typeof (PasswordBoxAssistant), new PropertyMetadata(false, OnBindPasswordChanged)); private static readonly DependencyProperty UpdatingPassword = DependencyProperty.RegisterAttached("UpdatingPassword", typeof(bool), typeof(PasswordBoxAssistant), new PropertyMetadata(false)); private static void OnBoundPasswordChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { PasswordBox box = d as PasswordBox; // only handle this event when the property is attached to a PasswordBox // and when the BindPassword attached property has been set to true if (d == null || !GetBindPassword(d)) { return; } // avoid recursive updating by ignoring the box's changed event box.PasswordChanged -= HandlePasswordChanged; string newPassword = (string)e.NewValue; if (!GetUpdatingPassword(box)) { box.Password = newPassword; } box.PasswordChanged += HandlePasswordChanged; } private static void OnBindPasswordChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e) { // when the BindPassword attached property is set on a PasswordBox, // start listening to its PasswordChanged event PasswordBox box = dp as PasswordBox; if (box == null) { return; } bool wasBound = (bool)(e.OldValue); bool needToBind = (bool)(e.NewValue); if (wasBound) { box.PasswordChanged -= HandlePasswordChanged; } if (needToBind) { box.PasswordChanged += HandlePasswordChanged; } } private static void HandlePasswordChanged(object sender, RoutedEventArgs e) { PasswordBox box = sender as PasswordBox; // set a flag to indicate that we're updating the password SetUpdatingPassword(box, true); // push the new password into the BoundPassword property SetBoundPassword(box, box.Password); SetUpdatingPassword(box, false); } public static void SetBindPassword(DependencyObject dp, bool value) { dp.SetValue(BindPassword, value); } public static bool GetBindPassword(DependencyObject dp) { return (bool)dp.GetValue(BindPassword); } public static string GetBoundPassword(DependencyObject dp) { return (string)dp.GetValue(BoundPassword); } public static void SetBoundPassword(DependencyObject dp, string value) { dp.SetValue(BoundPassword, value); } private static bool GetUpdatingPassword(DependencyObject dp) { return (bool)dp.GetValue(UpdatingPassword); } private static void SetUpdatingPassword(DependencyObject dp, bool value) { dp.SetValue(UpdatingPassword, value); } } 

在你的XAML

     

你可能不想这样做,但如果你真的想要继续。

WPF / Silverlight PasswordBox不为Password属性公开DP的原因与安全性有关。 如果WPF / Silverlight要保留DP for Password,则需要框架将密码本身保持在内存中未加密。 这被认为是一个非常麻烦的安全攻击媒介。 PasswordBox使用加密内存(各种类型),访问密码的唯一方法是通过CLR属性。

我建议在访问PasswordBox.Password CLR属性时,不要将其放在任何变量中或作为任何属性的值。 在客户机RAM上以明文forms保存密码是一种安全禁忌。

SecurePassword无法通过绑定完成。

.NET文档解释了为什么PasswordBox首先不能绑定。

另一种解决方案是将PasswordBox放在ViewModel公共类LoginViewModel中

 public class LoginViewModel { // other properties here public PasswordBox Password { get { return m_passwordBox; } } // Executed when the Login button is clicked. private void LoginExecute() { var password = Password.SecurePassword; // do more stuff... } } 

是的,您违反了ViewModel最佳做法,但是

  1. 最佳做法是“在大多数情况下运作良好的建议”,而不是严格的规则和
  2. 编写简单,易于阅读,可维护的代码并避免不必要的复杂性也是“最佳实践”规则之一(“附加属性”解决方法可能会略微违反)。

我想我发现了一种奇怪的解决方案。 如果有改善的话请改进:)

我只是改变它:

Behavior

 using System; using System.Collections.Generic; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; using System.Windows.Interactivity; using System.Security; namespace Knerd.Behaviors { public class PasswordChangedBehavior : Behavior { protected override void OnAttached() { AssociatedObject.PasswordChanged += AssociatedObject_PasswordChanged; base.OnAttached(); } private void AssociatedObject_PasswordChanged(object sender, RoutedEventArgs e) { if (AssociatedObject.SecurePassword != null) AssociatedObject.DataContext = AssociatedObject.SecurePassword.Copy(); } protected override void OnDetaching() { AssociatedObject.PasswordChanged -= AssociatedObject_PasswordChanged; base.OnDetaching(); } // Using a DependencyProperty as the backing store for TargetPassword. This enables animation, styling, binding, etc... public static readonly DependencyProperty TargetPasswordProperty = DependencyProperty.Register("TargetPassword", typeof(SecureString), typeof(PasswordChangedBehavior), new PropertyMetadata(default(SecureString))); } } 

ViewModel根本没有改变,但这是我的View

      

这是完美的,没有暴露明文密码。