INotifyPropertyChanged, ICommand, and icommand
INotifyPropertyChanged and ICommand are two important interfaces in the MVVM design mode in Windows 8 and Windows Phone development. They have a deep understanding of the principles of these two interfaces and have a correct understanding of how to use them, it is of great benefit to be proficient in using the MVVM mode.
The biggest benefit of the MVVM mode is the separation of the presentation layer and the logic layer. This benefits from the binding mechanism of the Microsoft XAML platform. The two interfaces that play an important role in the binding mechanism are INotifyPropertyChanged and ICommand. The View layer is the top layer of the logic layer (ViewModel layer). Therefore, the presentation layer depends on the logic layer through binding, but this dependency is weak, because the BIND is passed in all strings, you can use the reflection mechanism to search for attributes at runtime to assign values or take values. There is no strong type or interface dependency, so you can freely use other ViewModel types, as long as the attribute name is the same. The logic layer needs to call the logic of the presentation layer, which belongs to the underlying module to call the high-level module. This requires a way to return. The INotifyPropertyChanged interface plays this role.
Next, let's take a look at this interface,
namespace System.ComponentModel{ public interface INotifyPropertyChanged { event PropertyChangedEventHandler PropertyChanged; }}
There is only one event PropertyChanged in the interface. What does this mean?
The interface is a contract, and the contract specifies what to do. The event PropertyChanged indicates the logic in the event processing function called during attribute change, that is, the property change notification. The event parameter has a changed attribute name.SoINotifyPropertyChanged indicates that the class implementing this interface has the capability of property change notification.
If the ViewModel class implements the INotifyPropertyChanged interface, it has the property change notification capability. If it is not implemented, it does not. What is the difference? As you may know, if the interface is implemented and the event is correctly triggered in the Setter accessors of the attribute, the ViewModel data is modified in the logic layer, the interface of the presentation layer changes synchronously. If this interface is not implemented, the interface does not change. During binding, the underlying logic of the binding determines whether the bound source object has implemented the INotifyPropertyChanged interface. If so, the PropertyChanged event is registered, the event processing function contains the logic for updating the interface control status. In this way, you can synchronously update the page when changing the data in the ViewModel layer.
The control at the View layer is bound to the data at the ViewModel layer. Manually modifying the data at the ViewModel layer also changes the status of the control at the View layer through the property change notification mechanism of the INotifyPropertyChanged interface, this achieves logical separation between the presentation layer and the logic layer and two-way automatic data synchronization. This is the core value of the Microsoft XAML platform and the MVVM mode.
Every time you manually implement the INotifyPropertyChanged interface, it is troublesome. You can use the MVVM framework, such as the ViewModelBase base class provided in MVVMLight. The base class implements the INotifyPropertyChanged interface and encapsulates the method for triggering events, for example, RaisePropertyChanged. Inherit ViewModelBase and call RaisePropertyChanged In the Setter accessors of the property to trigger the property change event. RaisePropertyChanged does not need to pass the character string name of the property, but passes in a Lambda that obtains the property, the attribute name is obtained using the expression tree internally. Although there is a slight loss in performance, intelligent sensing can be used to ensure the security of refactoring, reducing the possibility of errors and making it worthwhile. If the nameof operator in C #6.0 is used, both security and performance can be ensured.
Two-way automatic data synchronization is not enough. You can also use the control on the presentation layer to perform operations, such as clicking a button to execute an operation. You can directly use the Click Event of a button to achieve this requirement, but the unreasonable combination depends on the Use scenario.
1. if this operation is a pure presentation layer operation, rather than executing business logic such as data processing, it is relatively simple and common, such as executing an animation effect. Use triggers and actions in XAML. The following code runs a Storyboard when you click the button.
<Button Content ="Button" HorizontalAlignment="Left" Height="50" Margin ="50,30,0,0" VerticalAlignment="Top" Width="116"> <i:Interaction.Triggers> <i:EventTrigger EventName="Click"> <ei:ControlStoryboardAction Storyboard="{StaticResource Storyboard1}"/> </i:EventTrigger></i:Interaction.Triggers></Button>
2. if the logic is complex, but it is also pure presentation layer logic, processing the performance Layer Effect does not matter with the business logic of data processing, you can register the Click Event of the button, in. xaml. write the logic of the presentation layer in cs. You can use the control of the presentation layer and add x: Name To The XAML. xaml. use this control in cs.
3. For business logic such as data processing, if it is still written in. xaml. cs, it is not the MVVM mode. This logic should be written in ViewModel. How to Write it? Write a method in ViewModel and call it in View? The correct method is to use the Command mechanism.
Note that this logic should be the business logic of Data Processing. How can this sentence be understood? This statement means that the logic written in the ViewModel layer processes data, instead of the View-layer controls. Therefore, it is wrong to bind the View-layer control to the ViewModel layer. No control should appear in the ViewModel layer. The correct method is to bind the data attribute of the control in the View layer to the attribute of the Data class in the ViewModel layer. For example, if the Text attribute of TextBox is bound to the Name attribute of Person, it is automatically updated in two directions.
The ICommand interface is the core interface of the Command mechanism.
See this interface below,
namespace System.Windows.Input{ public interface ICommand { bool CanExecute(object parameter); void Execute(object parameter); event EventHandler CanExecuteChanged; }}
This interface contains two methods and an event. From the perspective of name and signature, the CanExecute method should be used to determine whether the command can be executed, and the Execute method is the real execution logic of the command. What about the CanExecuteChanged event? Compared with the INotifyPropertyChanged interface, we can understand that the role of the CanExecuteChanged event is actually a notification of whether the execution status changes.
Buttons and other controls have the Command and CommandParameter attributes used to implement the Command mechanism. The Command attribute is bound to the object that implements the ICommand interface in the ViewModel layer.This achievesThe ICommand interface object puts the real execution logic of the command into the Execute method, and puts the logic that determines whether the command can be executed into the CanExecute method to stimulate the CanExecuteChanged event, sends a notification to the outside world about whether the command can execute status changes.
Each command object writes a class to implement the ICommand interface, which also includes the logic for stimulating CanExecuteChanged. It is possible that a member in ViewModel is also used in the Command Execution logic, therefore, it is difficult to establish the relationship between the Command object and the ViewModel object. So what is a better method? Duplicate logic should be extracted. Therefore, a base class of a command should be extracted to implement the ICommand interface. It is better to put the specific command execution logic and the logic to determine whether the command can be executed into the ViewModel. This leads to the RelayCommand. The following is a simple implementation of RelayCommand. for better implementation, refer to the source code of MVVMLight.
public class RelayCommand : ICommand { private readonly Action _execute; private readonly Func<bool> _canExecute; public RelayCommand(Action execute) : this(execute, null) { } public RelayCommand(Action execute, Func<bool> canExecute) { if (execute == null) { throw new ArgumentNullException("execute" ); } _execute = execute; if (canExecute != null) { _canExecute = canExecute; } } public event EventHandler CanExecuteChanged; public void RaiseCanExecuteChanged() { var handler = CanExecuteChanged; if (handler != null) { handler(this, EventArgs.Empty); } } public bool CanExecute(object parameter) { return _canExecute == null || _canExecute(); } public virtual void Execute(object parameter) { if (CanExecute(parameter) && _execute != null) { _execute(); } } }
The RelayCommand class contains the method RaiseCanExecuteChanged that can trigger command execution status change notifications. It allows you to pass in the Command Execution logic and determine whether the command can be executed, and use the passed logic to implement the Execute and CanExecute methods required by the interface.
Let's take a look at ViewModel writing, including the use of RelayCommand,
class PersonViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { var propertyChanged = PropertyChanged; if (propertyChanged != null) { propertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } private string name; public string Name { get { return name; } set { if (name != value) { name = value; OnPropertyChanged("Name"); AddPersonCommand.RaiseCanExecuteChanged(); } } } private RelayCommand addPersonCommand; public RelayCommand AddPersonCommand { get { return addPersonCommand ?? (addPersonCommand = new RelayCommand(() => { AddPerson(); }, () => !string.IsNullOrWhiteSpace(Name))); } } public void AddPerson() { } }
The INotifyPropertyChanged interface is directly implemented here. The ViewModelBase base class is not used. You need to write the events in the implementation interface and the logic for stimulating the event. In actual projects, you can inherit the ViewModelBase base class provided by the MVVM framework. If you need to inherit from other existing classes, you can implement the interface yourself like the above Code.
In the Setter accessors of the Name attribute, a property change notification is triggered to update the interface. AddPersonCommand uses a tip ,?? Operators to achieve delayed creation and improve memory usage during performance optimization. The two Lambda statements are the command execution logic and the logic for determining whether a command can be executed. The command execution logic calls a method in ViewModel, because there may be more logic. The logic for determining whether a command can be executed is directly placed in Lambda. The Name attribute cannot be blank. It is not enough to do this, but also to send a notification when the command execution status changes. Therefore, the RaiseCanExecuteChanged method of the AddPersonCommand command is called in the Setter accesser of the Name attribute.
The above example is an ideal way to use Command.Some people use Command, but do not use CommandThe CanExecute mechanism is used to specify the IsEnabled attribute in ViewModel and bind it to the IsEnabled attribute of the Button to control whether the Button can be executed. This method loses half of the meaning of using Command, and the logic is redundant and confusing. It is obviously not a good method.
The View Layer Code is as follows,
<Window x: Class = "ICommandResearch. 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 "mc: ignorable = "d" Title = "MainWindow" Height = "350" Width = "525"> <Grid> <Button Content = "add personnel" HorizontalAlignment = "Left" verticalignment =" top "Width =" 100 "Margin =, "Height =" 30 "Command =" {Binding AddPersonCommand} "/> <TextBox HorizontalAlignment =" Left "Height =" 23 "Margin =, 120 "TextWrapping =" Wrap "VerticalAlignment =" Top "Width =" "Text =" {Binding Name, updateSourceTrigger = PropertyChanged} "/> <TextBlock HorizontalAlignment =" Left "Margin =" 25, 37, 0, 0 "TextWrapping =" Wrap "Text =" name "VerticalAlignment =" Top "/> </Grid> </Window>
Simply bind the Text attribute of TextBox to the Name attribute of ViewModel, and bind the Command attribute of Button to the AddPersonCommand attribute of ViewModel. Note that UpdateSourceTrigger = PropertyChanged is set for Name binding, so that the Name attribute of ViewModel is set for TextBox each time you type a character, which contains the logic for triggering Command Execution status change notifications, to control the availability of buttons on the interface. Is it simple and simple.
This article analyzes the principles of INotifyPropertyChanged and ICommand interfaces, and shows the correct method of use, hoping to help you.