DataBinding(3)

References:

https://intellitect.com/getting-started-model-view-viewmodel-mvvm-pattern-using-windows-presentation-framework-wpf/

Commands

Bindings are a great way to move data from your view model into your view, but we also need to allow our view model to respond to user interaction. Most user controls that have a default user interaction, like a button click, are handled by commands. All user controls that implement the ICommandSource interface support a Command property that will be invoked when the control’s default action occurs. There are many controls that implement this interface such as Buttons, MenuItems, CheckBoxes, RadioButtons, Hyperlinks, etc.
Commands are simply objects that implement the ICommand interface. Or, put another way, Commands are messages from the View to your View Model. When the control’s default event occurs, such as button click, the Execute method on the command is invoked. Commands can also indicate when they are able to execute. This allows the control to enable or disable itself based on whether its command can be executed.

In our very simple example, we will change the value of the first name when a button is clicked.

First, we need to add a command property in our view model:

public class ViewModel : ViewModelBase
{
    public ICommand ChangeNameCommand { get; }
    ...
}

We will then add a button to the MainWindow and use a Binding to set its Command property to be the command in our view model.

<Button Content="Change Name" Command="{Binding Path=ChangeNameCommand}" VerticalAlignment="Bottom" HorizontalAlignment="Center" />

Now we just need to assign a new command object to our ChangeNameCommand property in our view model. Unfortunately WPF does not come with a default ICommand implementation suitable for use in a view model, however the interface is simple enough to implement:

public class DelegateCommand : ICommand
{
    private readonly Action<object> _executeAction;

    public DelegateCommand(Action<object> executeAction)
    {
        _executeAction = executeAction;
    }

    public void Execute(object parameter) => _executeAction(parameter);

    public bool CanExecute(object parameter) => true;

    public event EventHandler CanExecuteChanged;
}

In this very simple implementation, an Action delegate is invoked when the command is executed. For now we will ignore the CanExecute parts of the interface and always allow the command to always be executed.

We can now finish the code in the view model.

public class ViewModel : ViewModelBase
{
    ...
    
    private readonly DelegateCommand _changeNameCommand;
    public ICommand ChangeNameCommand => _changeNameCommand;
 
    public ViewModel()
    {
        _changeNameCommand = new DelegateCommand(OnChangeName);
    }
 
    private void OnChangeName(object commandParameter)
    {
        FirstName = "Walter";
    }
}

If we run our simple application we can see that clicking the button does change the name.

Let’s go back and implement the CanExecute portions of the ICommand interface.

public class DelegateCommand : ICommand
{
    private readonly Action<object> _executeAction;
    private readonly Func<object, bool> _canExecuteAction;
 
    public DelegateCommand(Action<object> executeAction, Func<object, bool> canExecuteAction)
    {
        _executeAction = executeAction;
        _canExecuteAction = canExecuteAction;
    }
 
    public void Execute(object parameter) => _executeAction(parameter);
 
    public bool CanExecute(object parameter) => _canExecuteAction?.Invoke(parameter) ?? true;
 
    public event EventHandler CanExecuteChanged;
 
    public void InvokeCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}

Just like the execute method, this command will also take in a CanExecute delegate. The CanExecuteChanged event is also exposed with a public method so we can raise it anytime the return value of CanExecute delegate has changed.

Back in our view model we will make the following additions.

public ViewModel()
{
    _changeNameCommand = new DelegateCommand(OnChangeName, CanChangeName);
}

private void OnChangeName(object commandParameter)
{
    FirstName = "Walter";
    _changeNameCommand.InvokeCanExecuteChanged();
}

private bool CanChangeName(object commandParameter)
{
    return FirstName != "Walter";
}

CanChangeName will be invoked to determine if the command can be executed. In this case we will simply prevent the command from executing once the name has changed to “Walter”. Finally after changing the name in the OnChangeName method, the command notifies the button that its CanExecute state has changed by raising its event.

Running the application we can see that the button properly disables after the name is changed.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章