0

PasswordBox value is not updating in the viewModel. I tried following the solution stated in How to bind to a PasswordBox in MVVM But facing some issues

Off Below code stated , I'm able to trigger the password change event and see the password value in string AddTempPassword of viewModel before Button click OK. But once the button is clicked the AddUserRequest method within View Model contains null value for AddTempPassword.

For sure I am missing something, Will someone please help guide.

XAML Code

<Label Content="Username"/>
<TextBox Text="{Binding AddUsername}"/>
<Label Content="Temporary Password" />
<PasswordBox PasswordChanged="PasswordBox_PasswordChanged"/> 
<Button Content="OK" Command="{Binding AddUserCommand}"/>

Code Behind

public partial class SecurityWindow : Window
{
   public SecurityWindow()
   {
      InitializeComponent();
      SecurityViewModel vm = new SecurityViewModel();
      DataContext = vm;
    }

    private void PasswordBox_PasswordChanged(object sender, 
                                             RoutedEventArgs e)
    {
        if (this.DataContext != null)
        {
           ((dynamic)this.DataContext).AddTempPassword = 
           ((PasswordBox)sender).Password;
        }                    
    } 

View Model

public class SecurityViewModel : ObservableItem, INotifyPropertyChanged
{
   public SecurityViewModel()
   {           
          AddUserCommand = new RelayCommand(a => AddUserRequest());                         
   }

   public string AddUsername { get; set; }
   public ICommand AddUserCommand { get; set; }

   private string _addTempPassword;
   public string AddTempPassword
   {
      get { return _addTempPassword; }
      set
      {
          _addTempPassword = value;
          OnPropertyChanged(nameof(AddTempPassword));
       }
    }

    private void AddUserRequest()
    {
       try
       {
          bool flag = Framework.SecurityManager.AddUser(AddUsername, 
                        **AddTempPassword**, profileName);

          if (!flag)
          {
             MessageBox.Show(@"User was not able to be added.  Note: 
                             Usernames must be unique.");
          }
          else
          {
             MessageBox.Show(@"The user " + AddUsername + @" was 
                             added.");
          }

        }
        catch (Exception ex)
        {
             Logbook.WriteLog(Logbook.EventLevel.Error, ex);
         }
    }
} 

RelayCommand fetched from another class

    public class RelayCommand : ICommand
{
    #region Fields
    private readonly Action<object> _execute;
    private readonly Predicate<object> _canExecute;
    #endregion

    #region Constructors
    /// <summary>
    /// Initializes a new instance of <see cref="ICommand"/>.
    /// </summary>
    /// <param name="execute">Delegate to execute when Execute is called on the command.  This can be null to just hook up a CanExecute delegate.</param>
    /// <remarks><seealso cref="CanExecute"/> will always return true.</remarks>
    public RelayCommand(Action<object> execute)
        : this(execute, null)
    {
    }

    /// <summary>
    /// Creates a new command.
    /// </summary>
    /// <param name="execute">The execution logic.</param>
    /// <param name="canExecute">The execution status logic.</param>
    public RelayCommand(Action<object> execute, Predicate<object> canExecute)
    {
        _execute = execute ?? throw new ArgumentNullException(nameof(execute));

        _canExecute = canExecute;
    }
    #endregion

    #region ICommand Members
    ///<summary>
    ///Defines the method that determines whether the command can execute in its current state.
    ///</summary>
    ///<param name="parameter">Data used by the command.  If the command does not require data to be passed, this object can be set to null.</param>
    ///<returns>
    ///true if this command can be executed; otherwise, false.
    ///</returns>
    public bool CanExecute(object parameter) => _canExecute == null || _canExecute(parameter);

    /// <summary>
    /// Occurs when changes occur that affect whether or not the command should execute.
    /// </summary>
    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    ///<summary>
    ///Defines the method to be called when the command is invoked.
    ///</summary>
    ///<param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to <see langword="null" />.</param>
    public void Execute(object parameter) => _execute(parameter);
    #endregion
}

When OK Button is clicked I am expecting the AddTempPassword Value read to be in String when AddUserRequest method is called. I Have rest of the code taking care of encryption. I suspect, seeing some other object updated before button OK click i.e. the one via Datacontext behind the code.

AddTempPassword Actual value before AddUserRequest Method call

AddTempPassword Null Value at AddUserRequest Method

NewLearner
  • 25
  • 1
  • 5

1 Answers1

0
  1. I have no idea, how your RelayCommand is setup. Is this from some framework? or your custom implementation? I tried below way and it worked.

AddUserCommand = new RelayCommand(a => AddUserRequest(null),null);
  1. I see that you have setup AddTempPassword property in your viewmodel to save the password string (which is not a secure method btw) and named your password box as AddTempPassword as well. If you are planning to use the property from the viewmodel, then no need to bind your command parameter to the password box. You can directly use your property from the viewmodel. In this case, remove your command parameter.

Please see the image below which shows the result in my test case.

enter image description here

Please find the full codes below: --> Relaycommand & Observable model

using System;
using System.Collections.Generic;
using System.Linq;
using System.ComponentModel;
using System.Windows.Input;
using System.Reflection;
using System.Runtime.CompilerServices;

namespace SOF
{
    public class RelayCommand : ICommand
    {
        Action<object> MethodToExecute;
        Func<object, bool> FunctionToCheck;

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        public bool CanExecute(object parameter)
        {
            if (FunctionToCheck == null) return true;
            return FunctionToCheck.Invoke(parameter);
        }

        public void Execute(object parameter)
        {
            MethodToExecute.Invoke(parameter);
        }

        public RelayCommand(Action<object> ActualMethodToExecute, Func<object, bool> ActualFunctionToCheck)
        {
            MethodToExecute = ActualMethodToExecute;
            FunctionToCheck = ActualFunctionToCheck;
        }

        public RelayCommand(Action<object> ActualMethodToExecute)
        {
            new RelayCommand(ActualMethodToExecute, null);
        }
    }

    public abstract class ObservableModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged([CallerMemberName] string propname = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propname));
        }
        public ObservableModel() { }
    }
}

Mainwindow.cs

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            SecurityViewModel vm = new SecurityViewModel();
            DataContext = vm;
        }

        private void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
        {
            if (this.DataContext != null)
            {
                ((dynamic)this.DataContext).AddTempPassword =
                ((PasswordBox)sender).Password;
            }
        }
    }
}

MainWindow.XAML

<Window x:Class="SOF.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:SOF"
        mc:Ignorable="d"
        Title="MainWindow" Height="250" Width="350">
    <StackPanel>
        <Label Content="Username"/>
        <TextBox Text="{Binding AddUsername}"/>
        <Label Content="Temporary Password" />
        <PasswordBox Name= "AddTempPassword"                 
             PasswordChanged="PasswordBox_PasswordChanged"/>
        <Button Content="OK" Command="{Binding AddUserCommand}"/>
    </StackPanel>
</Window>

Security ViewModel

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Input;

namespace SOF
{
    public class SecurityViewModel : ObservableModel, INotifyPropertyChanged
    {
        public SecurityViewModel()
        {
            AddUserCommand = new RelayCommand(a => AddUserRequest(null),null);
        }

        public string AddUsername { get; set; }
        public ICommand AddUserCommand { get; set; }

        private string _addTempPassword;
        public string AddTempPassword
        {
            get { return _addTempPassword; }
            set
            {
                _addTempPassword = value;
                OnPropertyChanged(nameof(AddTempPassword));
            }
        }

        private void AddUserRequest(object parameter)
        {
            string test;
            test = AddTempPassword;
            //try
            //{
          //      **bool flag = Framework.SecurityManager.AddUser(AddUsername,
          //                    *AddTempPassword *, profileName); **
     
          //if (!flag)
          //      {
          //          MessageBox.Show(@"User was not able to be added.  Note: 
          //                   Usernames must be unique.");
          //      }
          //      else
          //      {
          //          MessageBox.Show(@"The user " + AddUsername + @" was 
          //                   added.");
          //      }

          //  }
          //  catch (Exception ex)
          //  {
          //      Logbook.WriteLog(Logbook.EventLevel.Error, ex);
          //  }
        }
    }
}
Lingam
  • 609
  • 5
  • 16
  • 1. I have updated the question to add the relay command code pieces. I did try your response but still face the same problem. 2. Thanks for share the piece of advise have updated the code. – NewLearner Jul 25 '19 at 04:20
  • Relaycommand looks for a Action . So, your AddUserRequest() should be able to accept an object argument. Something like this, private void AddUserRequest(object parameter) – Lingam Jul 25 '19 at 04:32
  • So are you saying the passed object parameter is null in this case? If so I did try that as well , like the one you mentioned. But I still face the same issue – NewLearner Jul 25 '19 at 04:35
  • Yes. Since you are not binding any value , the object is null. You are using the property present inside the viewmodel. I tried it again with a breakpoint setup for testing and it returns AddTempPassword value. Try to add a string before the actual code is executed and check if you are able to get the AddTempPassword value. private void AddUserRequest(object parameter) { string test; test = AddTempPassword; – Lingam Jul 25 '19 at 04:40
  • I tired, the value is still null. I suspect, seeing some other object updated for correct AddTempPassword before button OK click i.e. the one via Datacontext behind the code. – NewLearner Jul 25 '19 at 04:51
  • Well Mine is just the reverse. Could you please share your xaml and Code behind pieces as well – NewLearner Jul 25 '19 at 05:05
  • Surprisingly its just the same copy paste piece of code and still does not work :( – NewLearner Jul 25 '19 at 14:39