2014-04-11 123 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