1
+ // Licensed to the .NET Foundation under one or more agreements.
2
+ // The .NET Foundation licenses this file to you under the MIT license.
3
+ // See the LICENSE file in the project root for more information.
4
+
5
+ #pragma warning disable SA1512
6
+
7
+ // This file is inspired from the MvvmLight libray (lbugnion/mvvmlight),
8
+ // more info in ThirdPartyNotices.txt in the root of the project.
9
+
10
+ using System ;
11
+ using System . Collections . Generic ;
12
+ using System . Runtime . CompilerServices ;
13
+ using Microsoft . Toolkit . Mvvm . Messaging ;
14
+ using Microsoft . Toolkit . Mvvm . Messaging . Messages ;
15
+
16
+ namespace Microsoft . Toolkit . Mvvm . ComponentModel
17
+ {
18
+ /// <summary>
19
+ /// A base class for observable objects that also acts as recipients for messages. This class is an extension of
20
+ /// <see cref="ObservableObject"/> which also provides built-in support to use the <see cref="IMessenger"/> type.
21
+ /// </summary>
22
+ public abstract class ObservableRecipient : ObservableObject
23
+ {
24
+ /// <summary>
25
+ /// Initializes a new instance of the <see cref="ObservableRecipient"/> class.
26
+ /// </summary>
27
+ /// <remarks>
28
+ /// This constructor will produce an instance that will use the <see cref="Messaging.Messenger.Default"/> instance
29
+ /// to perform requested operations. It will also be available locally through the <see cref="Messenger"/> property.
30
+ /// </remarks>
31
+ protected ObservableRecipient ( )
32
+ : this ( Messaging . Messenger . Default )
33
+ {
34
+ }
35
+
36
+ /// <summary>
37
+ /// Initializes a new instance of the <see cref="ObservableRecipient"/> class.
38
+ /// </summary>
39
+ /// <param name="messenger">The <see cref="IMessenger"/> instance to use to send messages.</param>
40
+ protected ObservableRecipient ( IMessenger messenger )
41
+ {
42
+ Messenger = messenger ;
43
+ }
44
+
45
+ /// <summary>
46
+ /// Gets the <see cref="IMessenger"/> instance in use.
47
+ /// </summary>
48
+ protected IMessenger Messenger { get ; }
49
+
50
+ private bool isActive ;
51
+
52
+ /// <summary>
53
+ /// Gets or sets a value indicating whether the current view model is currently active.
54
+ /// </summary>
55
+ public bool IsActive
56
+ {
57
+ get => this . isActive ;
58
+ set
59
+ {
60
+ if ( SetProperty ( ref this . isActive , value , true ) )
61
+ {
62
+ if ( value )
63
+ {
64
+ OnActivated ( ) ;
65
+ }
66
+ else
67
+ {
68
+ OnDeactivated ( ) ;
69
+ }
70
+ }
71
+ }
72
+ }
73
+
74
+ /// <summary>
75
+ /// Raised whenever the <see cref="IsActive"/> property is set to <see langword="true"/>.
76
+ /// Use this method to register to messages and do other initialization for this instance.
77
+ /// </summary>
78
+ /// <remarks>
79
+ /// The base implementation registers all messages for this recipients that have been declared
80
+ /// explicitly through the <see cref="IRecipient{TMessage}"/> interface, using the default channel.
81
+ /// For more details on how this works, see the <see cref="MessengerExtensions.RegisterAll"/> method.
82
+ /// If you need more fine tuned control, want to register messages individually or just prefer
83
+ /// the lambda-style syntax for message registration, override this method and register manually.
84
+ /// </remarks>
85
+ protected virtual void OnActivated ( )
86
+ {
87
+ Messenger . RegisterAll ( this ) ;
88
+ }
89
+
90
+ /// <summary>
91
+ /// Raised whenever the <see cref="IsActive"/> property is set to <see langword="false"/>.
92
+ /// Use this method to unregister from messages and do general cleanup for this instance.
93
+ /// </summary>
94
+ /// <remarks>
95
+ /// The base implementation unregisters all messages for this recipient. It does so by
96
+ /// invoking <see cref="IMessenger.UnregisterAll"/>, which removes all registered
97
+ /// handlers for a given subscriber, regardless of what token was used to register them.
98
+ /// That is, all registered handlers across all subscription channels will be removed.
99
+ /// </remarks>
100
+ protected virtual void OnDeactivated ( )
101
+ {
102
+ Messenger . UnregisterAll ( this ) ;
103
+ }
104
+
105
+ /// <summary>
106
+ /// Broadcasts a <see cref="PropertyChangedMessage{T}"/> with the specified
107
+ /// parameters, without using any particular token (so using the default channel).
108
+ /// </summary>
109
+ /// <typeparam name="T">The type of the property that changed.</typeparam>
110
+ /// <param name="oldValue">The value of the property before it changed.</param>
111
+ /// <param name="newValue">The value of the property after it changed.</param>
112
+ /// <param name="propertyName">The name of the property that changed.</param>
113
+ /// <remarks>
114
+ /// You should override this method if you wish to customize the channel being
115
+ /// used to send the message (eg. if you need to use a specific token for the channel).
116
+ /// </remarks>
117
+ protected virtual void Broadcast < T > ( T oldValue , T newValue , string ? propertyName )
118
+ {
119
+ var message = new PropertyChangedMessage < T > ( this , propertyName , oldValue , newValue ) ;
120
+
121
+ Messenger . Send ( message ) ;
122
+ }
123
+
124
+ /// <summary>
125
+ /// Compares the current and new values for a given property. If the value has changed,
126
+ /// raises the <see cref="ObservableObject.PropertyChanging"/> event, updates the property with
127
+ /// the new value, then raises the <see cref="ObservableObject.PropertyChanged"/> event.
128
+ /// </summary>
129
+ /// <typeparam name="T">The type of the property that changed.</typeparam>
130
+ /// <param name="field">The field storing the property's value.</param>
131
+ /// <param name="newValue">The property's value after the change occurred.</param>
132
+ /// <param name="broadcast">If <see langword="true"/>, <see cref="Broadcast{T}"/> will also be invoked.</param>
133
+ /// <param name="propertyName">(optional) The name of the property that changed.</param>
134
+ /// <returns><see langword="true"/> if the property was changed, <see langword="false"/> otherwise.</returns>
135
+ /// <remarks>
136
+ /// This method is just like <see cref="ObservableObject.SetProperty{T}(ref T,T,string)"/>, just with the addition
137
+ /// of the <paramref name="broadcast"/> parameter. As such, following the behavior of the base method,
138
+ /// the <see cref="ObservableObject.PropertyChanging"/> and <see cref="ObservableObject.PropertyChanged"/> events
139
+ /// are not raised if the current and new value for the target property are the same.
140
+ /// </remarks>
141
+ protected bool SetProperty < T > ( ref T field , T newValue , bool broadcast , [ CallerMemberName ] string ? propertyName = null )
142
+ {
143
+ return SetProperty ( ref field , newValue , EqualityComparer < T > . Default , broadcast , propertyName ) ;
144
+ }
145
+
146
+ /// <summary>
147
+ /// Compares the current and new values for a given property. If the value has changed,
148
+ /// raises the <see cref="ObservableObject.PropertyChanging"/> event, updates the property with
149
+ /// the new value, then raises the <see cref="ObservableObject.PropertyChanged"/> event.
150
+ /// See additional notes about this overload in <see cref="SetProperty{T}(ref T,T,bool,string)"/>.
151
+ /// </summary>
152
+ /// <typeparam name="T">The type of the property that changed.</typeparam>
153
+ /// <param name="field">The field storing the property's value.</param>
154
+ /// <param name="newValue">The property's value after the change occurred.</param>
155
+ /// <param name="comparer">The <see cref="IEqualityComparer{T}"/> instance to use to compare the input values.</param>
156
+ /// <param name="broadcast">If <see langword="true"/>, <see cref="Broadcast{T}"/> will also be invoked.</param>
157
+ /// <param name="propertyName">(optional) The name of the property that changed.</param>
158
+ /// <returns><see langword="true"/> if the property was changed, <see langword="false"/> otherwise.</returns>
159
+ protected bool SetProperty < T > ( ref T field , T newValue , IEqualityComparer < T > comparer , bool broadcast , [ CallerMemberName ] string ? propertyName = null )
160
+ {
161
+ if ( ! broadcast )
162
+ {
163
+ return SetProperty ( ref field , newValue , comparer , propertyName ) ;
164
+ }
165
+
166
+ T oldValue = field ;
167
+
168
+ if ( SetProperty ( ref field , newValue , comparer , propertyName ) )
169
+ {
170
+ Broadcast ( oldValue , newValue , propertyName ) ;
171
+
172
+ return true ;
173
+ }
174
+
175
+ return false ;
176
+ }
177
+
178
+ /// <summary>
179
+ /// Compares the current and new values for a given property. If the value has changed,
180
+ /// raises the <see cref="ObservableObject.PropertyChanging"/> event, updates the property with
181
+ /// the new value, then raises the <see cref="ObservableObject.PropertyChanged"/> event. Similarly to
182
+ /// the <see cref="ObservableObject.SetProperty{T}(T,T,Action{T},string)"/> method, this overload should only be
183
+ /// used when <see cref="ObservableObject.SetProperty{T}(ref T,T,string)"/> can't be used directly.
184
+ /// </summary>
185
+ /// <typeparam name="T">The type of the property that changed.</typeparam>
186
+ /// <param name="oldValue">The current property value.</param>
187
+ /// <param name="newValue">The property's value after the change occurred.</param>
188
+ /// <param name="callback">A callback to invoke to update the property value.</param>
189
+ /// <param name="broadcast">If <see langword="true"/>, <see cref="Broadcast{T}"/> will also be invoked.</param>
190
+ /// <param name="propertyName">(optional) The name of the property that changed.</param>
191
+ /// <returns><see langword="true"/> if the property was changed, <see langword="false"/> otherwise.</returns>
192
+ /// <remarks>
193
+ /// This method is just like <see cref="ObservableObject.SetProperty{T}(T,T,Action{T},string)"/>, just with the addition
194
+ /// of the <paramref name="broadcast"/> parameter. As such, following the behavior of the base method,
195
+ /// the <see cref="ObservableObject.PropertyChanging"/> and <see cref="ObservableObject.PropertyChanged"/> events
196
+ /// are not raised if the current and new value for the target property are the same.
197
+ /// </remarks>
198
+ protected bool SetProperty < T > ( T oldValue , T newValue , Action < T > callback , bool broadcast , [ CallerMemberName ] string ? propertyName = null )
199
+ {
200
+ return SetProperty ( oldValue , newValue , EqualityComparer < T > . Default , callback , broadcast , propertyName ) ;
201
+ }
202
+
203
+ /// <summary>
204
+ /// Compares the current and new values for a given property. If the value has changed,
205
+ /// raises the <see cref="ObservableObject.PropertyChanging"/> event, updates the property with
206
+ /// the new value, then raises the <see cref="ObservableObject.PropertyChanged"/> event.
207
+ /// See additional notes about this overload in <see cref="SetProperty{T}(T,T,Action{T},bool,string)"/>.
208
+ /// </summary>
209
+ /// <typeparam name="T">The type of the property that changed.</typeparam>
210
+ /// <param name="oldValue">The current property value.</param>
211
+ /// <param name="newValue">The property's value after the change occurred.</param>
212
+ /// <param name="comparer">The <see cref="IEqualityComparer{T}"/> instance to use to compare the input values.</param>
213
+ /// <param name="callback">A callback to invoke to update the property value.</param>
214
+ /// <param name="broadcast">If <see langword="true"/>, <see cref="Broadcast{T}"/> will also be invoked.</param>
215
+ /// <param name="propertyName">(optional) The name of the property that changed.</param>
216
+ /// <returns><see langword="true"/> if the property was changed, <see langword="false"/> otherwise.</returns>
217
+ protected bool SetProperty < T > ( T oldValue , T newValue , IEqualityComparer < T > comparer , Action < T > callback , bool broadcast , [ CallerMemberName ] string ? propertyName = null )
218
+ {
219
+ if ( ! broadcast )
220
+ {
221
+ return SetProperty ( oldValue , newValue , comparer , callback , propertyName ) ;
222
+ }
223
+
224
+ if ( SetProperty ( oldValue , newValue , comparer , callback , propertyName ) )
225
+ {
226
+ Broadcast ( oldValue , newValue , propertyName ) ;
227
+
228
+ return true ;
229
+ }
230
+
231
+ return false ;
232
+ }
233
+ }
234
+ }
0 commit comments