Blade MVVMLight 10: In-depth Messenger, mvvmlightmessenger
1. Messager Interaction Structure and Message Type
In the previous article, Messeger is the meaning of a messenger. As the name suggests, Messeger is used to notify and receive messages between View and ViewModel and between ViewModel and ViewModel.
The Messenger class is used for application communication. The receiver can only accept the registered message type. In addition, the target type can be specified and implemented using the Send <TMessage, TTarget> (TMessage message, in this case, information can only be transmitted. If the recipient type matches the target parameter type,
Message can be any simple or complex object. You can use a specific message type or create your own type to inherit from them.
The interaction structure is as follows:
The following table lists the message types:
Message object type |
Description |
MessageBase |
A simple message class that carries optional information about the message publisher's |
GenericMessage <T> |
Generic message |
Icationicationmessage |
Sends a string-type notification to the recipient. |
Icationicationmessage <T> |
It is the same as above and has the generic Function |
Icationicationmessage |
Send a notification to the recipient, allowing the recipient to send messages to the sender |
NotificationMessageAction <T> |
NotificationMessage generic Mode |
DialogMessage |
The sender (usually View) displays the dialog and transmits the callback result (used for callback). The receiver can select how to display the dialog box, which can be a standard MessageBox or a custom pop-up window. |
PropertyChangedMessage <T> |
It is used to broadcast the change of an attribute in the sender, and has the goal of completely setting the PropertyChanged event, but it is a weak contact method. |
2. Message registration Mode
The previous article provides the registration method, but there are many registration methods. The most common method is the naming method call and Lambda expression call:
2.1 registration of basic naming methods
1 // use the naming method to register 2 Messenger. default. register <String> (this, HandleMessage); 3 4 // Uninstall all MVVMLight messages registered by the current (this) object 5 this. unloaded + = (sender, e) => Messenger. default. unregister (this); 6 7 8 private void HandleMessage (String msg) 9 {10 // Todo11}
2.2 register with Lambda
1 Messenger. default. register <String> (this, message => {2 // Todo 3 4}); 5 // Uninstall all MVVMLight messages registered by the current (this) object 6 this. unloaded + = (sender, e) => Messenger. default. unregister (this );
3. Cross-thread access
Previously, in Article 8th "use of blade MVVMLight 8: DispatchHelper in multithreading and scheduling,
We have discussed how to use events in asynchronous threads to execute and obtain related execution steps. However, if a method in an asynchronous thread needs to operate the UI of the main thread (when the UI thread is used.
Controls in Windows are bound to a specific UI thread (main thread). Other threads are not allowed to access them because they do not have thread security and standardization. Therefore, MVVM Light later had the scheduling help class (DispatchHelper) to handle the scheduling scheme that does not need to be in the thread.
This can be derived from the asynchronous thread to send and receive information about the UI thread (main thread. Therefore, the previous Code DispatchHelper can be modified as follows:
Registration Module (in ViewModel ):
1 public class MessengerForDispatchViewModel: ViewModelBase 2 {3 /// <summary> 4 /// constructor 5 /// </summary> 6 public MessengerForDispatchViewModel () 7 {8 InitData (); 9 DispatcherHelper. initialize (); 10 11 // Messenger: Messenger 12 // Recipient: Recipient 13 Messenger. default. register <TopUserInfo> (this, "UserMessenger", FeedBack); 14} 15}
Sending module (code in asynchronous thread ):
1 private void Start () 2 {3 TopUserInfo ui = new TopUserInfo (); 4 5 // ToDo: Write the DataAccess Code 6 for (Int32 idx = 1; idx <= 9; idx ++) 7 {8 Thread. sleep (1000); 9 ui = new TopUserInfo () {10 isFinish = false, 11 process = idx * 10, 12 userInfo = null13}; 14 DispatcherHelper. checkBeginInvokeOnUI () => 15 {16 Messenger. default. send <TopUserInfo> (ui, "UserMessenger"); 17}); 18} 19 Thread. sleep (1000); 20 ui = new TopUserInfo () 21 {22 isFinish = true, 23 process = 100,24 userInfo = up25}; 26 DispatcherHelper. checkBeginInvokeOnUI () => 27 {28 Messenger. default. send <TopUserInfo> (ui, "UserMessenger"); 29}); 30}
Result:
4. Release registration information:
4.1 release the UnRegister Based on The View Interface (this function is used to release registration information for the Unload event on the current View page ):
1 // Uninstall all MVVMLight messages registered with the current (this) object. 2 this. Unloaded + = (sender, e) => Messenger. Default. Unregister (this );
4.2 release based on the UnRegister in the ViewModel class (this method is called by a user when the user closes the page and releases the registration, which must be initiated when the developer closes the view model ):
1 // <summary> 2 // manually call to release registration information (all registration information in this view model is released) 3 /// </summary> 4 public void ReleaseRegister () 5 {6 Messenger. default. unregister (this); 7}
5. Release Registration Information and Memory Processing
To avoid unnecessary Memory leakage, the. Net Framework puts forward a more practical WeakReference (weak reference) object. This function allows weak storage of object references. If all references to this object are released, the garbage collector can recycle the object.
It is similar to storing all registration information in a weak reference storage area. Once the host (View or ViewModel) hosted by the registration information is released, the reference is cleared, the registration information will also be released within a certain period of time.
The following table is fromLaurent BugnionMVVM instructions:
Risk of Memory leakage when registration is not canceled:
Visibility |
WPF |
Silverlight |
Windows Phone 8 |
Windows Runtime |
Static |
No risk |
No risk |
No risk |
No risk |
Public |
No risk |
No risk |
No risk |
No risk |
Internal |
No risk |
Risks |
Risks |
No risk |
Dedicated |
No risk |
Risks |
Risks |
No risk |
Anonymous Lambda |
No risk |
Risks |
Risks |
No risk |
6. Dedicated channels and broadcast channels
6.1 filter the Messenger sender (check whether the sender is sent to the sender ):
1 public class ForSourceSenderViewModel: ViewModelBase 2 {3 public ForSourceSenderViewModel () {} 4 5 # region Global Command 6 private RelayCommand sendMsg; 7 /// <summary> 8 /// Send Message 9 /// </summary> 10 public RelayCommand SendMsg11 {12 get13 {14 if (sendMsg = null) sendMsg = new RelayCommand () => ExcuteSendMsh (); 15 return sendMsg; 16} 17 18 set19 {20 sendMsg = value; 21} 22} 23 24 # endregion25 26 # region affiliated Method 27 private void ExcuteSendMsh () 28 {29 icationicationmessage nm = new icationicationmessage (this, "Send source message"); 30 Messenger. default. send <icationicationmessage> (nm); 31} 32 # endregion33 34}
1 Messenger. default. register <icationicationmessage> (this, message => 2 {3 if (message. sender is ForSourceSenderViewModel) 4 {5 // determine the source to accept the message 6 MsgInfo = message. notification; 7} 8 });
6.2 open a dedicated Messenger channel:
1 private Messenger myMessenger; 2 public MessengerForSourceViewModel () 3 {4 // constructor 5 myMessenger = new Messenger (); 6 SimpleIoc. default. register () => myMessenger, "MyMessenger"); // inject a Messenger object whose Key is MyMessenger 7 8 myMessenger. register <icationicationmessage> (this, message => // Register myMessenger and enable listening 9 {10 // determine the source to accept message 11 MsgInfo = message. notification; 12}); 13}
1 # region Global Command 2 private RelayCommand sendMsg; 3 /// <summary> 4 /// send message 5 /// </summary> 6 public RelayCommand SendMsg 7 {8 get 9 {10 if (sendMsg = null) sendMsg = new RelayCommand () => ExcuteSendMsh (); 11 return sendMsg; 12} 13 set14 {15 sendMsg = value; 16} 17} 18 # endregion19 20 # region affiliated Method 21 private void ExcuteSendMsh () 22 {23 NotificationMessage nm = new NotificationMessage (this, String. format ("Send message: {0}", DateTime. now); 24 Messenger myMessenger = SimpleIoc. default. getInstance <Messenger> ("MyMessenger"); // obtain an existing Messenger instance 25 myMessenger. send <icationicationmessage> (nm); // message 26} 27 # endregion
6.3 using tokens to differentiate and use channels: This is the most common method. Dedicated tokens can be used to differentiate different channels and improve reusability.
Messenger contains a token parameter. the sender and the Registry use the same token to ensure that data is transmitted in this proprietary channel. Therefore, the token is the best way to filter and isolate messages.
1 // Send a message using the ViewAlert Tokon flag. 2 Messenger. Default. Send <String> ("ViewModel Notification View pop-up message box", "ViewAlert ");
1 public MessagerForView () 2 {3 InitializeComponent (); 4 5 // message flag token: ViewAlert, used to identify only reading messages sent by one or some Sender, and perform corresponding processing, therefore, the token on the Sender side must be consistent. 6 // The execution method Action: ShowReceiveInfo is used to execute subsequent work after the message is received. Note that this field supports the generic capability, therefore, it is convenient to pass parameters. 7. Messenger. default. register <String> (this, "ViewAlert", ShowReceiveInfo); 8 this. dataContext = new MessengerRegisterForVViewModel (); 9 // Uninstall all MVVMLight messages registered with the current (this) object 10 this. unloaded + = (sender, e) => Messenger. default. unregister (this); 11} 12 13 // <summary> 14 // subsequent work after receiving the message: pop-up message box 15 // </summary> 16 // <param name = "msg"> </param> 17 private void ShowReceiveInfo (String msg) based on the returned information) 18 {19 MessageBox. show (msg); 20}
7. Use built-in messages
For example, the icationicationmessage <T> and PropertyChanged Message <T> used above.
Notification is a Message Notification mechanism, while PropertyChanged Message mainly refers to the Notification operation when the attribute changes.
1 public class PropertyChangedViewModel: ViewModelBase 2 {3 public const string PropertyName = "UserName"; // register as this attribute, send a message when this attribute changes. 4 public PropertyChangedViewModel () {} 5 6 # region global variable 7 private String userName; 8 /// <summary> 9 /// user name 10 /// </summary> 11 public string UserName12 {13 get14 {15 return userName; 16} 17 18 set19 {20 String oldValue = userName; 21 userName = value; 22 RaisePropertyChanged () => UserName, oldValue, value, true ); // sending parameter 23} 24} 25 # endregion
1 Messenger. default. register <PropertyChangedMessage <String> (this, message => 2 {3 if (message. propertyName = PropertyChangedViewModel. propertyName) // receives the message 4 {5 PropertyChangedInfo = (message. oldValue + "-->" + message. newValue); // output content from the old value to the new value 6} 7 });
Result:
Download Sample Code
Reprinted please indicate the source, thank you