How to bind to a PasswordBox in MVVM

前端 未结 30 1738
执念已碎
执念已碎 2020-11-22 11:50

I have come across a problem with binding to a PasswordBox. It seems it\'s a security risk but I am using the MVVM pattern so I wish to bypass this. I found som

相关标签:
30条回答
  • 2020-11-22 12:38

    I am using succinct MVVM-friendly solution that hasn't been mentioned yet. First, I name the PasswordBox in XAML:

    <PasswordBox x:Name="Password" />
    

    Then I add a single method call into view constructor:

    public LoginWindow()
    {
        InitializeComponent();
        ExposeControl<LoginViewModel>.Expose(this, view => view.Password,
            (model, box) => model.SetPasswordBox(box));
    }
    

    And that's it. View model will receive notification when it is attached to a view via DataContext and another notification when it is detached. The contents of this notification are configurable via the lambdas, but usually it's just a setter or method call on the view model, passing the problematic control as a parameter.

    It can be made MVVM-friendly very easily by having the view expose interface instead of child controls.

    The above code relies on helper class published on my blog.

    0 讨论(0)
  • 2020-11-22 12:38

    In windows universal app

    you can use this code with the property "Password" and binding with the modelView

     <PasswordBox x:Uid="PasswordBox" Password="{Binding Waiter.Password, Mode=TwoWay}" Name="txtPassword" HorizontalAlignment="Stretch" Margin="50,200,50,0" VerticalAlignment="Top"/>

    0 讨论(0)
  • 2020-11-22 12:38

    Its very simple . Create another property for password and Bind this with TextBox

    But all input operations perform with actual password property

    private string _Password;

        public string PasswordChar
        {
            get
            {
                string szChar = "";
    
                foreach(char szCahr in _Password)
                {
                    szChar = szChar + "*";
                }
    
                return szChar;
            }
    
            set
            {
                _PasswordChar = value; NotifyPropertyChanged();
            }
        }
    

    public string Password { get { return _Password; }

            set
            {
                _Password = value; NotifyPropertyChanged();
                PasswordChar = _Password;
            }
        }
    

    0 讨论(0)
  • 2020-11-22 12:39

    you can do it with attached property, see it.. PasswordBox with MVVM

    0 讨论(0)
  • 2020-11-22 12:39

    As mentioned before VM should be unaware of the View but passing whole PasswordBox looks like the simplest approach. So maybe instead of casting passed parameter to PasswordBox use Reflection to extract Password property from it. In this case VM expects some kind of Password Container with property Password(I'm ussing RelayCommands from MVMM Light-Toolkit):

    public RelayCommand<object> SignIn
    {
        get
        {
            if (this.signIn == null)
            {
                this.signIn = new RelayCommand<object>((passwordContainer) => 
                    {
                        var password = passwordContainer.GetType().GetProperty("Password").GetValue(passwordContainer) as string;
                        this.authenticationService.Authenticate(this.Login, password);
                    });
            }
    
            return this.signIn;
        }
    }
    

    It can be easily tested with anonymous class:

    var passwordContainer = new
        {
            Password = "password"
        };
    
    0 讨论(0)
  • 2020-11-22 12:42

    To me, both of these things feel wrong:

    • Implementing clear text password properties
    • Sending the PasswordBox as a command parameter to the ViewModel

    Transferring the SecurePassword (SecureString instance) as described by Steve in CO seems acceptable. I prefer Behaviors to code behind, and I also had the additional requirement of being able to reset the password from the viewmodel.

    Xaml (Password is the ViewModel property):

    <PasswordBox>
        <i:Interaction.Behaviors>
            <behaviors:PasswordBinding BoundPassword="{Binding Password, Mode=TwoWay}" />
        </i:Interaction.Behaviors>
    </PasswordBox>
    

    Behavior:

    using System.Security;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Interactivity;
    
    namespace Evidence.OutlookIntegration.AddinLogic.Behaviors
    {
        /// <summary>
        /// Intermediate class that handles password box binding (which is not possible directly).
        /// </summary>
        public class PasswordBoxBindingBehavior : Behavior<PasswordBox>
        {
            // BoundPassword
            public SecureString BoundPassword { get { return (SecureString)GetValue(BoundPasswordProperty); } set { SetValue(BoundPasswordProperty, value); } }
            public static readonly DependencyProperty BoundPasswordProperty = DependencyProperty.Register("BoundPassword", typeof(SecureString), typeof(PasswordBoxBindingBehavior), new FrameworkPropertyMetadata(OnBoundPasswordChanged));
    
            protected override void OnAttached()
            {
                this.AssociatedObject.PasswordChanged += AssociatedObjectOnPasswordChanged;
                base.OnAttached();
            }
    
            /// <summary>
            /// Link up the intermediate SecureString (BoundPassword) to the UI instance
            /// </summary>
            private void AssociatedObjectOnPasswordChanged(object s, RoutedEventArgs e)
            {
                this.BoundPassword = this.AssociatedObject.SecurePassword;
            }
    
            /// <summary>
            /// Reacts to password reset on viewmodel (ViewModel.Password = new SecureString())
            /// </summary>
            private static void OnBoundPasswordChanged(object s, DependencyPropertyChangedEventArgs e)
            {
                var box = ((PasswordBoxBindingBehavior)s).AssociatedObject;
                if (box != null)
                {
                    if (((SecureString)e.NewValue).Length == 0)
                        box.Password = string.Empty;
                }
            }
    
        }
    }
    
    0 讨论(0)
提交回复
热议问题