[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 / Internal / PropertyEditing / Model / ModelPropertyValue.cs
1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //----------------------------------------------------------------
4 namespace System.Activities.Presentation.Internal.PropertyEditing.Model 
5 {
6     using System;
7     using System.Collections;
8     using System.Collections.Generic;
9     using System.ComponentModel;
10     using System.Data;
11     using System.Diagnostics;
12     using System.Diagnostics.CodeAnalysis;
13     using System.Globalization;
14     using System.Windows;
15     using System.Windows.Input;
16     using System.Windows.Media;
17     using System.Windows.Media.Imaging;
18     using System.Text;
19
20     using System.Activities.Presentation;
21     using System.Activities.Presentation.Model;
22     using System.Activities.Presentation.PropertyEditing;
23
24     using System.Activities.Presentation.Internal.PropertyEditing.Resources;
25     using System.Activities.Presentation.Internal.Properties;
26     using System.Activities.Presentation.Internal.PropertyEditing.Editors;
27
28     // <summary>
29     // Concrete implementation of PropertyValue that delegates to ModelPropertyEntryBase for
30     // all actions.
31     // </summary>
32     internal class ModelPropertyValue : PropertyValue 
33     {
34
35         // Object used to mark a property value that should be cleared instead of set
36         private static readonly object ClearValueMarker = new object();
37
38         // CultureInfo instance we use for formatting values so that they reflect what is in Xaml
39         private static CultureInfo _xamlCultureInfo;
40
41         // <summary>
42         // Basic ctor
43         // </summary>
44         // <param name="parentProperty">Parent ModelPropertyEntryBase</param>
45         public ModelPropertyValue(ModelPropertyEntryBase parentProperty) : base(parentProperty) 
46         {
47         }
48
49         // <summary>
50         // Returns the source of this property value
51         // </summary>
52         public override PropertyValueSource Source 
53         {
54             get {
55                 return ParentModelPropertyEntry.Source;
56             }
57         }
58
59         // <summary>
60         // Returns true if this value represents the default value of the property
61         // </summary>
62         public override bool IsDefaultValue 
63         {
64             get {
65                 return Source == DependencyPropertyValueSource.DefaultValue;
66             }
67         }
68
69         // <summary>
70         // Returns true if the value contained by this property is mixed
71         // </summary>
72         public override bool IsMixedValue 
73         {
74             get {
75                 return ParentModelPropertyEntry.IsMixedValue;
76             }
77         }
78
79         // <summary>
80         // Returns true if custom TypeConverter exists and if it can convert
81         // the value from string.
82         // </summary>
83         public override bool CanConvertFromString 
84         {
85             get {
86                 return ParentModelPropertyEntry.Converter != null &&
87                     ParentModelPropertyEntry.Converter.CanConvertFrom(typeof(string));
88             }
89         }
90
91         // <summary>
92         // Gets a flag indicating whether this PropertyValue has sub properties
93         // </summary>
94         public override bool HasSubProperties 
95         {
96             get {
97                 return ParentModelPropertyEntry.HasSubProperties;
98             }
99         }
100
101         // <summary>
102         // Gets the sub-properties of the PropertyValue
103         // </summary>
104         public override PropertyEntryCollection SubProperties 
105         {
106             get {
107                 return ParentModelPropertyEntry.SubProperties;
108             }
109         }
110
111         // <summary>
112         // Gets a flag indicating whether this PropertyValue represents a collection
113         // </summary>
114         public override bool IsCollection 
115         {
116             get {
117                 return ParentModelPropertyEntry.IsCollection;
118             }
119         }
120
121         // <summary>
122         // Gets the collection represented by this PropertyValue
123         // </summary>
124         public override PropertyValueCollection Collection 
125         {
126             get {
127                 return ParentModelPropertyEntry.Collection;
128             }
129         }
130
131         // <summary>
132         // This is an internal helper to which we can bind and on which we fire PropertyChanged
133         // event when the Name sub-property (if one exists) changes.  More-specifically,
134         // we bind to this property in CollectionEditor to display the Type as well as the
135         // Name of the items in the collection.  This property is accessed from XAML.
136         // </summary>
137         [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
138         public PropertyValue NameSensitiveInstance 
139         {
140             get {
141                 return this;
142             }
143         }
144
145         // <summary>
146         // Always catch exceptions
147         // </summary>
148         protected override bool CatchExceptions 
149         {
150             get {
151                 return true;
152             }
153         }
154
155         // <summary>
156         // Gets an en-us CultureInfo that ignores user-specified settings
157         // </summary>
158         private static CultureInfo XamlCultureInfo 
159         {
160             get {
161                 if (_xamlCultureInfo == null)
162                 {
163                     _xamlCultureInfo = new CultureInfo("en-us", false);
164                 }
165
166                 return _xamlCultureInfo;
167             }
168         }
169
170         // Convenience accessor
171         private ModelPropertyEntryBase ParentModelPropertyEntry 
172         {
173             get {
174                 return (ModelPropertyEntryBase)this.ParentProperty;
175             }
176         }
177
178         // <summary>
179         // Validates the value using the TypeConverter, if one exists
180         // </summary>
181         // <param name="valueToValidate">Value to validate</param>
182         protected override void ValidateValue(object valueToValidate) 
183         {
184             // Noop.  We used to rely on TypeConverter.IsValid() here, but it turns out
185             // that a bunch of standard TypeConverters don't really work (eg. Int32TypeConverter
186             // returns true for IsValid("abc") and EnumConverter returns false for
187             // IsValid(MyEnum.One | MyEnum.Two) even if MyEnum if adorned with FlagsAttribute)
188         }
189
190         // Called when there exists a Name sub-property for this value and it changes
191         internal void OnNameSubPropertyChanged() 
192         {
193             // Updates XAML bindings (collection editor item-display-name-template for one)
194             this.OnPropertyChanged("NameSensitiveInstance");
195         }
196
197         // <summary>
198         // Convert the specified string to a value
199         // </summary>
200         // <param name="stringToConvert"></param>
201         // <returns></returns>
202         protected override object ConvertStringToValue(string stringToConvert) 
203         {
204             if (this.ParentProperty.PropertyType == typeof(string)) 
205             {
206                 return stringToConvert;
207             }
208             else if (string.IsNullOrEmpty(stringToConvert)) 
209             {
210
211                 // If the type of this property is string:
212                 //
213                 //      StringValue of ''   -> set Value to ''
214                 //      StringValue of null -> ClearValue()
215                 //
216                 // Otherwise
217                 //
218                 //      StringValue of ''   -> ClearValue()
219                 //      StringValue of null -> ClearValue()
220                 //
221                 if (stringToConvert != null && typeof(string).Equals(this.ParentProperty.PropertyType))
222                 {
223                     return null;
224                 }
225                 else
226                 {
227                     return ClearValueMarker;
228                 }
229
230             }
231             else if (EditorUtilities.IsNullableEnumType(this.ParentProperty.PropertyType) && stringToConvert.Equals(EditorUtilities.NullString, StringComparison.Ordinal))
232             {
233                 // PS 107537: Special case handling when converting a string to a nullable enum type.
234                 return null;
235             }
236             else if (this.ParentModelPropertyEntry.Converter != null &&
237                 this.ParentModelPropertyEntry.Converter.CanConvertFrom(typeof(string)))
238             {
239
240                 return this.ParentModelPropertyEntry.Converter.ConvertFromString(null, XamlCultureInfo, stringToConvert);
241             }
242
243             throw FxTrace.Exception.AsError(new InvalidOperationException(string.Format(
244                 CultureInfo.CurrentCulture,
245                 Resources.PropertyEditing_NoStringToValueConversion,
246                 this.ParentProperty.DisplayName)));
247         }
248
249         // <summary>
250         // Convert the specified value to a string
251         // </summary>
252         // <param name="valueToConvert"></param>
253         // <returns></returns>
254         protected override string ConvertValueToString(object valueToConvert) 
255         {
256             string stringValue = string.Empty;
257
258             if (valueToConvert == null) 
259             {
260                 if (typeof(IList).IsAssignableFrom(this.ParentProperty.PropertyType)) 
261                 {
262                     stringValue = Resources.PropertyEditing_DefaultCollectionStringValue;
263                 }
264                 else if (EditorUtilities.IsNullableEnumType(this.ParentProperty.PropertyType))
265                 {
266                     // PS 107537: Special case handling when converting a nullable enum type to a string.
267                     return EditorUtilities.NullString;
268                 }
269                 return stringValue;
270             }
271             else if ((stringValue = valueToConvert as string) != null) 
272             {
273                 return stringValue;
274             }
275
276             TypeConverter typeConverter = this.ParentModelPropertyEntry.Converter;
277             if (valueToConvert is Array) 
278             {
279                 stringValue = Resources.PropertyEditing_DefaultArrayStringValue;
280             }
281             else if (valueToConvert is IList
282                 || valueToConvert is ICollection
283                 || ModelUtilities.ImplementsICollection(valueToConvert.GetType())
284                 || ModelUtilities.ImplementsIList(valueToConvert.GetType())) 
285             {
286                 stringValue = Resources.PropertyEditing_DefaultCollectionStringValue;
287             }
288             else if (valueToConvert is IEnumerable) 
289             {
290                 stringValue = Resources.PropertyEditing_DefaultEnumerableStringValue;
291             }
292             else if (typeConverter != null && typeConverter.CanConvertTo(typeof(string))) 
293             {
294                 stringValue = typeConverter.ConvertToString(null, XamlCultureInfo, valueToConvert);
295             }
296             else 
297             {
298                 stringValue = valueToConvert.ToString();
299             }
300
301             return stringValue ?? string.Empty;
302         }
303
304
305         // <summary>
306         // Redirect the call to parent PropertyEntry
307         // </summary>
308         // <returns></returns>
309         protected override object GetValueCore() 
310         {
311             return ParentModelPropertyEntry.GetValueCore();
312         }
313
314         // <summary>
315         // Redirect the call to parent PropertyEntry
316         // </summary>
317         // <param name="value"></param>
318         protected override void SetValueCore(object value) 
319         {
320             if (value == ClearValueMarker)
321             {
322                 ParentModelPropertyEntry.ClearValue();
323             }
324             else
325             {
326                 ParentModelPropertyEntry.SetValueCore(value);
327             }
328         }
329
330         // <summary>
331         // Apply the FlowDirection to the resource.
332         // </summary>
333         private void CheckAndSetFlowDirectionResource() 
334         {
335             // Check if the value being edited is FlowDirection
336             // and if so, reset the resource to the current value that the user is setting.
337             // This will refresh the property inspector and all the string editors, showing "string" properties,
338             // would have their FlowDirection set to the current value.
339             if (ParentModelPropertyEntry.PropertyName.Equals(FrameworkElement.FlowDirectionProperty.Name)) 
340             {
341                 object value = Value;
342                 if (value != null) 
343                 {
344                     PropertyInspectorResources.GetResources()["SelectedControlFlowDirectionRTL"] = value;
345                 }
346             }
347         }
348
349         // <summary>
350         // Redirect the call to parent PropertyEntry
351         // </summary>
352         public override void ClearValue() 
353         {
354             ParentModelPropertyEntry.ClearValue();
355         }
356
357         // <summary>
358         // Fires the appropriate PropertyChanged events
359         // </summary>
360         public void OnUnderlyingModelChanged() 
361         {
362             CheckAndSetFlowDirectionResource();
363             this.NotifyRootValueChanged();
364         }
365
366         // <summary>
367         // Fires the appropriate PropertyChanged events
368         // </summary>
369         public void OnUnderlyingSubModelChanged() 
370         {
371             this.NotifySubPropertyChanged();
372         }
373
374         // <summary>
375         // Called when there is an error setting or getting a PropertyValue.
376         // Displays an error dialog.
377         // </summary>
378         // <param name="e"></param>
379         protected override void OnPropertyValueException(PropertyValueExceptionEventArgs e) 
380         {
381             if (e.Source == PropertyValueExceptionSource.Set) 
382             {
383                 if (e.Exception != null) 
384                 {
385                     Debug.WriteLine(e.Exception.ToString());
386                 }
387
388                 ErrorReporting.ShowErrorMessage(e.Exception.Message);
389
390                 base.OnPropertyValueException(e);
391             }
392             else 
393             {
394                 base.OnPropertyValueException(e);
395             }
396         }
397
398         // <summary>
399         // Debuging-friendly ToString()
400         // </summary>
401         // <returns></returns>
402         [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
403         public override string ToString() 
404         {
405             try 
406             {
407                 string value;
408                 if (this.Value == null)
409                 {
410                     value = "{null}";
411                 }
412                 else 
413                 {
414                     value = this.StringValue;
415                     if (string.IsNullOrEmpty(value))
416                     {
417                         value = "{empty}";
418                     }
419                 }
420
421                 return string.Format(CultureInfo.CurrentCulture, "{0} (PropertyValue)", value ?? "{null}");
422             }
423             catch 
424             {
425                 return base.ToString();
426             }
427         }
428     }
429 }