WPF Simple Commanding Example

前端 未结 2 1615
时光说笑
时光说笑 2021-01-26 01:12

I try not to post questions like this, but i\'ve really been struggling to find an answer or similar example. I have what I think is a really simple example I\'d like to setup.

相关标签:
2条回答
  • 2021-01-26 01:27

    Well, MVVM is just a pattern or philosophy, so I think your desire to avoid using a framework might be a little misguided. Even if you're not using one of those frameworks, you will essentially be writing your own framework in order to implement the MVVM pattern.

    That being said, probably what you want to use is a DelegateCommand or one of the similar implementations. See: http://www.wpftutorial.net/DelegateCommand.html. The important part that I think you are looking for is that the command that the WPF button is binding to must raise the CanExecuteChanged event whenever there is a change made in the view model which affects whether the command can or cannot be executed.

    So in your case, for example, you would want to add a call to the CanExecuteChanged of your AddPersonDelegateCommand to your OnPropertyChanged method (possibly filtered by the name of the property that was changed). This tells anything bound to the command to call CanExecute on the command, and then you would have your logic in that CanExecute that actually determines if a person with the entered name already exists.

    So to add some sample code, it might look something like this:

    class MainViewModel : INotifyPropertyChanged
    {
        public MainViewModel()
        {
            People = new ObservableCollection<Person>();
            People.Add( new Person {Name = "jimmy"});
            AddPersonDelegateCommand = new DelegateCommand(AddPerson, CanAddPerson);
        }
    
      // Your existing code here
    
      protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
      {
          if(propertyName == "NewNameTextBox") AddPersonDelegateCommand.RaiseCanExecuteChanged();
    
          PropertyChangedEventHandler handler = PropertyChanged;
          if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
      }
    
      public DelegateCommand AddPersonDelegateCommand { get; set; }
    
      public void AddPerson()
      { 
          // Code to add a person to the collection
      }
    
      public bool CanAddPerson()
      {
          return !People.Any(p=>p.Name == NewNameTextBox);
      }
    
      public string NewNameTextBox 
      { 
        get { return _newNameTextBox; }
        set 
        { 
           _newNameTextBox = value;
           OnPropertyChanged();
        }
      }
    }
    

    *Note: In this sample your <TextBox></TextBox> would need to be bound to the NewNameTextBox property on the view model.

    0 讨论(0)
  • 2021-01-26 01:34

    how would I implement commanding so that it uses the CanExecute to disable the Add button if the name is already there or the Name field is empty

    I will show how to do the add, the remove is similar and I leave that for you to figure out. First I will show the xaml changes with the button using an AddPerson command:

    <TextBox Text="{Binding CurrentPerson, 
                    Mode=TwoWay, 
                    UpdateSourceTrigger=PropertyChanged
                   }"/>
    <Button Command="{Binding AddPerson}">Add</Button>
    

    We have bound the current edited text to a new property on the View Model named CurrentPerson. This is done because we want to access what the person enters, but also we need the binding updated as the user types. To accomplish that updating, we specify that the binding updates by setting the UpdateSourceTrigger attribute to be PropertyChanged. Otherwise our the CurrentPerson string and ultimately the command Can operation would only fire when the edit text box lost focus.

    ViewModel The viewmodel will subscribe to the AddPerson command. Execution of that will add the user, but also check a can method which returns a boolean whether to enable the button or not. The can will be excecuted when the CurrentPerson property changes where we ultimately call RaiseCanExecuteChanged on the commanding class to have the button check the can method.

    (This VM is abbreviated for the example and based on your full VM)

    public OperationCommand AddPerson { get; set; }
    public string _currentPerson;
    
    public MainViewModel()
    {
     People = new ObservableCollection<Person>();
     People.Add(new Person { Name = "jimmy" });
    
                // First Lamda is where we execute the command to add,
                // The second lamda is the `Can` method to enable the button.
     AddPerson = new OperationCommand((o) => People.Add(new Person { Name = CurrentPerson }),
                                      (o) => (!string.IsNullOrWhiteSpace(CurrentPerson) && 
                                              !People.Any(per => per.Name == CurrentPerson)));
    
     // When the edit box text changes force a `Can` check.
     this.PropertyChanged += MainViewModel_PropertyChanged ;
    }
    
    void MainViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == "CurrentPerson")
            AddPerson.RaiseCanExecuteChanged();
    }
    

    Finally here is the commanding class used which is based on my blog article Xaml: ViewModel Main Page Instantiation and Loading Strategy for Easier Binding.:

    public class OperationCommand : ICommand
    {
    
    #region Variables
    
    Func<object, bool> canExecute;
    Action<object> executeAction;
    
    public event EventHandler CanExecuteChanged;
    
    #endregion
    
    #region Properties
    
    #endregion
    
    #region Construction/Initialization
    
    public OperationCommand(Action<object> executeAction)
        : this(executeAction, null)
    {
    }
    
    public OperationCommand(Action<object> executeAction, Func<object, bool> canExecute)
    {
        if (executeAction == null)
        {
            throw new ArgumentNullException("Execute Action was null for ICommanding Operation.");
        }
        this.executeAction = executeAction;
        this.canExecute = canExecute;
    }
    
    #endregion
    
    #region Methods
    
    public bool CanExecute(object parameter)
    {
        bool result = true;
        Func<object, bool> canExecuteHandler = this.canExecute;
        if (canExecuteHandler != null)
        {
            result = canExecuteHandler(parameter);
        }
    
        return result;
    }
    
    public void RaiseCanExecuteChanged()
    {
        EventHandler handler = this.CanExecuteChanged;
        if (handler != null)
        {
            handler(this, new EventArgs());
        }
    }
    
    public void Execute(object parameter)
    {
        this.executeAction(parameter);
    }
    
    #endregion
    
    }
    
    0 讨论(0)
提交回复
热议问题