2014-04-11 79 views
2

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

基本上我加了一个属性到Behavior其中包含我的ViewModel的目标属性。

任何想法,为什么它不工作? 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<PasswordBox> { 

     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

<PasswordBox Grid.Column="1" Grid.Row="1" Margin="5" Width="300" MinWidth="200"> 
    <i:Interaction.Behaviors> 
     <behaviors:PasswordChangedBehavior TargetPassword="{Binding Password}" /> 
    </i:Interaction.Behaviors> 
</PasswordBox> 

而在去年,该我的的一部分。

private SecureString password; 

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

我希望任何人都可以帮忙,atm我使用codebehind版本,但我宁愿不。

EDIT 2

什么其实并不工作,该TargetPassword属性不更新我的ViewModel

+1

你的行为的一些代码将有助于了解您的问题;) – Dmitry

+1

我希望他是如期 – MUG4N

+0

就像我说的,在大约10分钟:) – Knerd

回答

1

我想我找到了一个有点奇怪的解决方案。请改善,如果有某物提高:)

我只是改变它像这样:

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<PasswordBox> { 

     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

<PasswordBox Grid.Column="1" Grid.Row="1" Margin="5" Width="300" MinWidth="200" DataContext="{Binding Password, Mode=TwoWay}"> 
    <i:Interaction.Behaviors> 
     <behaviors:PasswordChangedBehavior /> 
    </i:Interaction.Behaviors> 
</PasswordBox> 

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

2

属性创建一个附加属性

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

<Page xmlns:ff="clr-namespace:FunctionalFun.UI"> 
<!-- [Snip] --> 
    <PasswordBox x:Name="PasswordBox" 
     ff:PasswordBoxAssistant.BindPassword="true" ff:PasswordBoxAssistant.BoundPassword="{Binding Path=Password, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"> 

</Page> 

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

WPF/Silverlight PasswordBox不公开DP密码属性的原因是安全相关的。 如果WPF/Silverlight要保留一个DP密码,它需要框架保持密码本身在内存中不加密。这被认为是相当麻烦的安全攻击媒介。 PasswordBox使用加密的内存(各种),并且访问密码的唯一方法是通过CLR属性。

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

SecurePassword不能用绑定来完成。

。NET文档解释了为什么PasswordBox不能绑定在第一位。

另一种解决方案是把PasswordBoxViewModel公共类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... 
    } 
} 

是的,你在这里违反视图模型的最佳实践,但

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

所以基本上它不” t工作来绑定SecurePassword?甚至没有AttachedProperty或行为? – Knerd

+0

@Knerd是的,你不能。 –

+0

@III所以如果我用SecurePassword替换密码它应该工作,我猜。 – Knerd