C# 将SecurePassword绑定到ViewModel

C# 将SecurePassword绑定到ViewModel,c#,wpf,mvvm,C#,Wpf,Mvvm,我尝试使用自定义的行为将PasswordBox的SecurePassword属性绑定到我的ViewModel。遗憾的是,它不能正常工作 基本上,我在行为中添加了一个属性,其中包含我的ViewModel的目标属性 private SecureString password; public SecureString Password { get { return password; } set { if (password != value) {

我尝试使用自定义的
行为将
PasswordBox
SecurePassword
属性绑定到我的
ViewModel
。遗憾的是,它不能正常工作

基本上,我在
行为
中添加了一个属性,其中包含我的
ViewModel
的目标属性

private SecureString password;

public SecureString Password {
    get { return password; }
    set {
        if (password != value) {
            password = value;
            OnPropertyChanged("Password");
        }
    }
}
你知道为什么它不起作用吗

PS:我现在在回家的路上没有笔记本电脑,我会在15分钟内用我的代码更新这个问题。但如果有人能发表想法或其他东西,那就太好了

编辑

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

行为首先:

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)));
    }
}
最后是我的
ViewModel
部分

private SecureString password;

public SecureString Password {
    get { return password; }
    set {
        if (password != value) {
            password = value;
            OnPropertyChanged("Password");
        }
    }
}
我希望任何人都能帮助我,我使用代码隐藏版本,但我宁愿不这样做

编辑2


实际上不起作用的是,
TargetPassword
属性不会更新my
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

<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>
是的,您在这里违反了ViewModel最佳实践,但是

  • 最佳实践是“在大多数情况下效果良好的建议” 而不是严格的规章制度
  • 编写简单、易读、可维护的代码并避免 不必要的复杂性也是“最佳实践”规则之一 (可能会被“附属财产”轻微违反) 解决方法)

  • 我想我找到了一个奇怪的解决办法。如果有需要改进的地方,请改进:)

    我只是把它改成这样:

    行为

    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)));
        }
    }
    

    这非常好,不会暴露明文密码。

    您的一些行为代码将有助于理解您的问题;)我希望他能像我说的那样,在大约10分钟内准时到达:)@Didier代码已经添加,伙计们;)所以基本上绑定SecurePassword不起作用?即使使用AttachedProperty或Behavior也不行?@Knerd是的,你不能。@III所以如果我用SecurePassword替换密码,我想它应该可以工作。@Knerd否,你创建了一个包装属性,它是
    BoundPassword
    。好的,我试过了,但它在
    SecurePassword
    上不起作用。我有点不想使用纯文本版本,但似乎我必须这样做。或者有没有办法用
    SecurePassword
    解决这个问题?
    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)));
        }
    }
    
    <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>