[reflection] Coop handles icalls in System.Reflection and System.RuntimeTypeHandle...
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / Base / Core / PropertyEditing / PropertyValue.cs
1 namespace System.Activities.Presentation.PropertyEditing {
2     using System.Globalization;
3     using System;
4     using System.Collections;
5     using System.ComponentModel;
6     using System.Collections.Generic;
7     using System.Diagnostics.CodeAnalysis;
8     using System.Activities.Presentation.Internal.Properties;
9     using System.Activities.Presentation;
10
11     /// <summary>
12     /// This class provides a data model for the underlying property value.
13     /// </summary>
14     public abstract class PropertyValue : INotifyPropertyChanged {
15
16         private PropertyEntry _parentProperty;
17
18         /// <summary>
19         /// Creates a PropertyValue.  For host infrastructure.
20         /// </summary>
21         /// <param name="parentProperty">The PropertyEntry that corresponds to this PropertyValue</param>
22         /// <exception cref="ArgumentNullException">When parentProperty is null</exception>
23         protected PropertyValue(PropertyEntry parentProperty) {
24             if (parentProperty == null)
25                 throw FxTrace.Exception.ArgumentNull("parentProperty");
26
27             _parentProperty = parentProperty;
28         }
29
30         /// <summary>
31         /// Event that gets fired when any properties of the PropertyValue class change.
32         /// Note that a "Value" and "StringValue" property changed notification gets fired
33         /// either when the Value or StringValue get set to a new instance OR when any
34         /// sub-properties of this PropertyValue change.
35         /// </summary>
36         public event PropertyChangedEventHandler PropertyChanged;
37
38         /// <summary>
39         /// Event fired when the Value or StringValue properties of this class
40         /// get updated with new instances.
41         /// </summary>
42         public event EventHandler RootValueChanged;
43
44         /// <summary>
45         /// Event fired when the any of the sub-properties of this PropertyValue
46         /// or its sub-properties get updated with new value instances.
47         /// </summary>
48         public event EventHandler SubPropertyChanged;
49
50         /// <summary>
51         /// Gets the parent PropertyEntry.
52         /// </summary>
53         public PropertyEntry ParentProperty { get { return _parentProperty; } }
54
55         /// <summary>
56         /// Gets a PropertyValueSource that contains information 
57         /// about where this value is coming from.
58         /// </summary>
59         public abstract PropertyValueSource Source { get; }
60
61         /// <summary>
62         /// Returns true if Value is the default value for the property
63         /// </summary>
64         public abstract bool IsDefaultValue { get; }
65
66         /// <summary>
67         /// Returns true if this value represents a property for multiple objects with 
68         /// more than one value - for example 2 Buttons with different values for Background
69         /// If this property is true then Value will return null and and StringValue will return 
70         /// String.Empty.
71         /// </summary>
72         public abstract bool IsMixedValue { get; }
73
74         /// <summary>
75         /// Throws if the value is invalid
76         /// </summary>
77         /// <param name="valueToValidate">value to validate</param>
78         protected abstract void ValidateValue(object valueToValidate);
79
80         /// <summary>
81         /// Gets a flag indicating whether the underlying value can be converted from a string 
82         /// </summary>
83         public abstract bool CanConvertFromString { get; }
84
85         /// <summary>
86         /// Returns the given string as a value - used to convert StringValue to Value
87         /// Typical implementations would use the TypeConverter for the underlying property
88         /// This method should not catch exceptions, it should propagate them.
89         /// </summary>
90         protected abstract object ConvertStringToValue(string value);
91
92         /// <summary>
93         /// Returns the value as a String - used to convert Value to StringValue
94         /// Typical implementations would use the TypeConverter for the underlying property
95         /// </summary>
96         protected abstract string ConvertValueToString(object value);
97
98         /// <summary>
99         /// Gets the underlying property value.
100         /// </summary>
101         protected abstract object GetValueCore();
102
103         /// <summary>
104         /// Sets the underlying property value.  This method should not catch
105         /// exceptions, but allow them to propagate.
106         /// </summary>
107         protected abstract void SetValueCore(object value);
108
109         /// <summary>
110         /// Clears this value such that it is unset.
111         /// </summary>
112         public abstract void ClearValue();
113
114         // Value Property
115
116         /// <summary>
117         /// Gets or sets the underlying property value.  Both Value and StringValue
118         /// will raise the appropriate change notifications.
119         /// </summary>
120         [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Propagating the error might cause VS to crash")]
121         [SuppressMessage("Reliability", "Reliability108", Justification = "Propagating the error might cause VS to crash")]
122         public object Value
123         {
124             get {
125                 object returnValue = null;
126
127                 if (this.CatchExceptions) {
128                     try {
129                         returnValue = GetValueCore();
130                     }
131                     catch (Exception ex) {
132                         OnPropertyValueException(new PropertyValueExceptionEventArgs(
133                             string.Format(
134                                 CultureInfo.CurrentCulture,
135                                 Resources.Error_ValueGetFailed),
136                             this,
137                             PropertyValueExceptionSource.Get,
138                             ex));
139                     }
140                 }
141                 else {
142                     returnValue = GetValueCore();
143                 }
144
145                 return returnValue;
146             }
147             set 
148             {
149                 try 
150                 {
151                     SetValueImpl(value);
152                 }
153                 catch (Exception ex) 
154                 {
155                     bool isValidationException = ex is ValidationException;
156
157                     //show error message if we do catch exception or exception is ValidationException
158                     if (this.CatchExceptions || isValidationException)
159                     {
160                         OnPropertyValueException(new PropertyValueExceptionEventArgs(
161                             string.Format(CultureInfo.CurrentCulture, Resources.Error_ValueSetFailed),
162                             this,
163                             PropertyValueExceptionSource.Set,
164                             ex));
165                     }
166
167                     //rethrow if we do not catch exception or exception is ValidationException (it should be handled by the control)
168                     if (!this.CatchExceptions || isValidationException)
169                     {
170                         throw;
171                     }
172                 }
173             }
174         }
175
176         private void SetValueImpl(object value) {
177             ValidateValue(value);
178             SetValueCore(value);
179             NotifyValueChanged();
180             OnRootValueChanged();
181         }
182
183
184         // StringValue Property
185
186         /// <summary>
187         /// Gets or sets the underlying property value as a string.  Both Value and StringValue
188         /// will raise the appropriate change notifications.
189         /// </summary>
190         [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Propagating the error might cause VS to crash")]
191         [SuppressMessage("Reliability", "Reliability108", Justification = "Propagating the error might cause VS to crash")]
192         public string StringValue
193         {
194             get {
195                 string returnValue = string.Empty;
196
197                 //If there is an error event handler then use it otherwise let the exception
198                 //propogate
199                 if (this.CatchExceptions) {
200                     try {
201                         //Caching opportunity here
202                         returnValue = this.ConvertValueToString(this.Value);
203                     }
204                     catch (Exception ex) {
205                         OnPropertyValueException(new PropertyValueExceptionEventArgs(
206                             string.Format(
207                                 CultureInfo.CurrentCulture,
208                                 Resources.Error_CannotConvertValueToString),
209                             this,
210                             PropertyValueExceptionSource.Get,
211                             ex));
212                     }
213                 }
214                 else {
215                     //Caching opportunity here
216                     returnValue = this.ConvertValueToString(this.Value);
217                 }
218
219                 return returnValue;
220             }
221             set {
222                 //If there is an error event handler then use it otherwise let the exception
223                 //propogate
224                 if (CatchExceptions) {
225                     try {
226                         this.Value = this.ConvertStringToValue(value);
227                     }
228                     catch (Exception ex) {
229                         OnPropertyValueException(
230                             new PropertyValueExceptionEventArgs(
231                                 string.Format(
232                                     CultureInfo.CurrentCulture,
233                                     Resources.Error_CannotUpdateValueFromStringValue),
234                                 this,
235                                 PropertyValueExceptionSource.Set,
236                                 ex));
237                     }
238                 }
239                 else {
240                     this.Value = this.ConvertStringToValue(value);
241                 }
242             }
243         }
244
245
246         // SubProperties
247
248         /// <summary>
249         /// Gets a flag indicating whether the type of this property 
250         /// supports sub-properties.  Typical implementations will use a TypeConverter
251         /// to verify whether sub-properties exist.
252         /// </summary>
253         public abstract bool HasSubProperties { get; }
254         
255         /// <summary>
256         /// Gets a collection of sub-properties as PropertyEntry instances
257         /// </summary>
258         public abstract PropertyEntryCollection SubProperties { get; }
259
260
261         // Collections
262
263         /// <summary>
264         /// Gets a flag indicating whether this PropertyValue models a property
265         /// whose value is a collection.
266         /// </summary>
267         public abstract bool IsCollection { get; }
268
269         /// <summary>
270         /// Gets a collection of PropertyValue instances that correspond to the items
271         /// in the collection when IsCollection is true.
272         /// </summary>
273         public abstract PropertyValueCollection Collection { get; }
274
275
276         // Error Handling
277
278         /// <summary>
279         /// Event for host implementations to use for error handling.  Raised when StringValue or Value throws 
280         /// and CatchExceptions is true.  If CatchExceptions is false, the exception will be thrown up the stack.
281         /// </summary>
282         public event EventHandler<PropertyValueExceptionEventArgs> PropertyValueException;
283
284         /// <summary>
285         /// Gets a boolean indicating whether exceptions thrown during value gets and sets 
286         /// should be caught or propagated directly to the caller.  By default, exceptions
287         /// are caught if there is at least one subscriber to the PropertyValueException event.
288         /// </summary>
289         protected virtual bool CatchExceptions {
290             get {
291                 return PropertyValueException != null;
292             }
293         }
294
295         /// <summary>
296         /// Called when PropertyValue get or set fails.  Default implementation raises the
297         /// PropertyValueException event.
298         /// </summary>
299         /// <param name="e">PropertyValueExceptionEventArgs</param>
300         /// <exception cref="ArgumentNullException">When e is null</exception>
301         protected virtual void OnPropertyValueException(PropertyValueExceptionEventArgs e) {
302             if (e == null)
303                 throw FxTrace.Exception.ArgumentNull("e");
304
305             if (PropertyValueException != null)
306                 PropertyValueException(this, e);
307         }
308
309
310         // Notification Helpers
311
312         /// <summary>
313         /// Raises change notification for all properties.  This should be called when
314         /// the underlying object is changed externally (for example Button.Width is 
315         /// changed on the design surface)
316         /// </summary>
317         protected virtual void NotifyRootValueChanged() {
318             //When Value is updated or the model is reset we 
319             //need to fire an "everything has changed" notification
320
321             //This doesn't appear to work at all...
322             //PropertyChanged(this, new PropertyChangedEventArgs("")); 
323
324             //So notify these key changes individually
325             OnPropertyChanged("IsDefaultValue");
326             OnPropertyChanged("IsMixedValue");
327             OnPropertyChanged("IsCollection");
328             OnPropertyChanged("Collection");
329             OnPropertyChanged("HasSubProperties");
330             OnPropertyChanged("SubProperties");
331             OnPropertyChanged("Source");
332             OnPropertyChanged("CanConvertFromString");
333
334             NotifyValueChanged();
335             OnRootValueChanged();
336         }
337
338         /// <summary>
339         /// Called to raise the SubPropertyChanged event.  This method should be called
340         /// when one of the sub-properties of this property changes.  It raises changed
341         /// events for Value, StringValue, and SubProperty
342         /// </summary>
343         protected void NotifySubPropertyChanged() {
344             NotifyValueChanged();
345             OnSubPropertyChanged();
346         }
347
348         /// <summary>
349         /// Called to raise the changed events for Value and StringValue.  This method
350         /// should only be called to trigger the refresh of the visual representation
351         /// of this value.  If the value content actually changes, call NotifyRootValueChanged
352         /// instead.
353         /// </summary>
354         private void NotifyValueChanged() {
355             OnPropertyChanged("Value");
356             NotifyStringValueChanged();
357         }
358
359         /// <summary>
360         /// Raise change notification for StringValue
361         /// </summary>
362         private void NotifyStringValueChanged() {
363             OnPropertyChanged("StringValue");
364         }
365
366         /// <summary>
367         /// Called to raise the RootValueChanged event
368         /// </summary>
369         private void OnRootValueChanged() {
370             if (RootValueChanged != null)
371                 RootValueChanged(this, EventArgs.Empty);
372         }
373
374         /// <summary>
375         /// Called to raise the SubPropertyChanged event
376         /// </summary>
377         private void OnSubPropertyChanged() {
378             if (SubPropertyChanged != null)
379                 SubPropertyChanged(this, EventArgs.Empty);
380         }
381
382
383         // INotifyPropertyChanged
384
385         /// <summary>
386         /// Raises the PropertyChanged event.  Subclasses that override this method
387         /// should call the base class implementation.
388         /// </summary>
389         /// <param name="e"></param>
390         protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) {
391             if (PropertyChanged != null)
392                 PropertyChanged(this, e);
393         }
394
395         /// <summary>
396         /// Raises the PropertyChanged event.
397         /// </summary>
398         /// <param name="propertyName"></param>
399         protected virtual void OnPropertyChanged(string propertyName) {
400             if (PropertyChanged != null)
401                 OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
402         }
403
404     }
405 }
406