1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //----------------------------------------------------------------
4 namespace System.Activities.Presentation.Internal.PropertyEditing.Model
7 using System.Collections;
8 using System.Collections.Generic;
9 using System.ComponentModel;
11 using System.Diagnostics;
12 using System.Diagnostics.CodeAnalysis;
13 using System.Globalization;
15 using System.Windows.Input;
16 using System.Windows.Media;
17 using System.Windows.Media.Imaging;
20 using System.Activities.Presentation;
21 using System.Activities.Presentation.Model;
22 using System.Activities.Presentation.PropertyEditing;
24 using System.Activities.Presentation.Internal.PropertyEditing.Resources;
25 using System.Activities.Presentation.Internal.Properties;
26 using System.Activities.Presentation.Internal.PropertyEditing.Editors;
29 // Concrete implementation of PropertyValue that delegates to ModelPropertyEntryBase for
32 internal class ModelPropertyValue : PropertyValue
35 // Object used to mark a property value that should be cleared instead of set
36 private static readonly object ClearValueMarker = new object();
38 // CultureInfo instance we use for formatting values so that they reflect what is in Xaml
39 private static CultureInfo _xamlCultureInfo;
44 // <param name="parentProperty">Parent ModelPropertyEntryBase</param>
45 public ModelPropertyValue(ModelPropertyEntryBase parentProperty) : base(parentProperty)
50 // Returns the source of this property value
52 public override PropertyValueSource Source
55 return ParentModelPropertyEntry.Source;
60 // Returns true if this value represents the default value of the property
62 public override bool IsDefaultValue
65 return Source == DependencyPropertyValueSource.DefaultValue;
70 // Returns true if the value contained by this property is mixed
72 public override bool IsMixedValue
75 return ParentModelPropertyEntry.IsMixedValue;
80 // Returns true if custom TypeConverter exists and if it can convert
81 // the value from string.
83 public override bool CanConvertFromString
86 return ParentModelPropertyEntry.Converter != null &&
87 ParentModelPropertyEntry.Converter.CanConvertFrom(typeof(string));
92 // Gets a flag indicating whether this PropertyValue has sub properties
94 public override bool HasSubProperties
97 return ParentModelPropertyEntry.HasSubProperties;
102 // Gets the sub-properties of the PropertyValue
104 public override PropertyEntryCollection SubProperties
107 return ParentModelPropertyEntry.SubProperties;
112 // Gets a flag indicating whether this PropertyValue represents a collection
114 public override bool IsCollection
117 return ParentModelPropertyEntry.IsCollection;
122 // Gets the collection represented by this PropertyValue
124 public override PropertyValueCollection Collection
127 return ParentModelPropertyEntry.Collection;
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.
137 [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
138 public PropertyValue NameSensitiveInstance
146 // Always catch exceptions
148 protected override bool CatchExceptions
156 // Gets an en-us CultureInfo that ignores user-specified settings
158 private static CultureInfo XamlCultureInfo
161 if (_xamlCultureInfo == null)
163 _xamlCultureInfo = new CultureInfo("en-us", false);
166 return _xamlCultureInfo;
170 // Convenience accessor
171 private ModelPropertyEntryBase ParentModelPropertyEntry
174 return (ModelPropertyEntryBase)this.ParentProperty;
179 // Validates the value using the TypeConverter, if one exists
181 // <param name="valueToValidate">Value to validate</param>
182 protected override void ValidateValue(object valueToValidate)
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)
190 // Called when there exists a Name sub-property for this value and it changes
191 internal void OnNameSubPropertyChanged()
193 // Updates XAML bindings (collection editor item-display-name-template for one)
194 this.OnPropertyChanged("NameSensitiveInstance");
198 // Convert the specified string to a value
200 // <param name="stringToConvert"></param>
201 // <returns></returns>
202 protected override object ConvertStringToValue(string stringToConvert)
204 if (this.ParentProperty.PropertyType == typeof(string))
206 return stringToConvert;
208 else if (string.IsNullOrEmpty(stringToConvert))
211 // If the type of this property is string:
213 // StringValue of '' -> set Value to ''
214 // StringValue of null -> ClearValue()
218 // StringValue of '' -> ClearValue()
219 // StringValue of null -> ClearValue()
221 if (stringToConvert != null && typeof(string).Equals(this.ParentProperty.PropertyType))
227 return ClearValueMarker;
231 else if (EditorUtilities.IsNullableEnumType(this.ParentProperty.PropertyType) && stringToConvert.Equals(EditorUtilities.NullString, StringComparison.Ordinal))
233 // PS 107537: Special case handling when converting a string to a nullable enum type.
236 else if (this.ParentModelPropertyEntry.Converter != null &&
237 this.ParentModelPropertyEntry.Converter.CanConvertFrom(typeof(string)))
240 return this.ParentModelPropertyEntry.Converter.ConvertFromString(null, XamlCultureInfo, stringToConvert);
243 throw FxTrace.Exception.AsError(new InvalidOperationException(string.Format(
244 CultureInfo.CurrentCulture,
245 Resources.PropertyEditing_NoStringToValueConversion,
246 this.ParentProperty.DisplayName)));
250 // Convert the specified value to a string
252 // <param name="valueToConvert"></param>
253 // <returns></returns>
254 protected override string ConvertValueToString(object valueToConvert)
256 string stringValue = string.Empty;
258 if (valueToConvert == null)
260 if (typeof(IList).IsAssignableFrom(this.ParentProperty.PropertyType))
262 stringValue = Resources.PropertyEditing_DefaultCollectionStringValue;
264 else if (EditorUtilities.IsNullableEnumType(this.ParentProperty.PropertyType))
266 // PS 107537: Special case handling when converting a nullable enum type to a string.
267 return EditorUtilities.NullString;
271 else if ((stringValue = valueToConvert as string) != null)
276 TypeConverter typeConverter = this.ParentModelPropertyEntry.Converter;
277 if (valueToConvert is Array)
279 stringValue = Resources.PropertyEditing_DefaultArrayStringValue;
281 else if (valueToConvert is IList
282 || valueToConvert is ICollection
283 || ModelUtilities.ImplementsICollection(valueToConvert.GetType())
284 || ModelUtilities.ImplementsIList(valueToConvert.GetType()))
286 stringValue = Resources.PropertyEditing_DefaultCollectionStringValue;
288 else if (valueToConvert is IEnumerable)
290 stringValue = Resources.PropertyEditing_DefaultEnumerableStringValue;
292 else if (typeConverter != null && typeConverter.CanConvertTo(typeof(string)))
294 stringValue = typeConverter.ConvertToString(null, XamlCultureInfo, valueToConvert);
298 stringValue = valueToConvert.ToString();
301 return stringValue ?? string.Empty;
306 // Redirect the call to parent PropertyEntry
308 // <returns></returns>
309 protected override object GetValueCore()
311 return ParentModelPropertyEntry.GetValueCore();
315 // Redirect the call to parent PropertyEntry
317 // <param name="value"></param>
318 protected override void SetValueCore(object value)
320 if (value == ClearValueMarker)
322 ParentModelPropertyEntry.ClearValue();
326 ParentModelPropertyEntry.SetValueCore(value);
331 // Apply the FlowDirection to the resource.
333 private void CheckAndSetFlowDirectionResource()
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))
341 object value = Value;
344 PropertyInspectorResources.GetResources()["SelectedControlFlowDirectionRTL"] = value;
350 // Redirect the call to parent PropertyEntry
352 public override void ClearValue()
354 ParentModelPropertyEntry.ClearValue();
358 // Fires the appropriate PropertyChanged events
360 public void OnUnderlyingModelChanged()
362 CheckAndSetFlowDirectionResource();
363 this.NotifyRootValueChanged();
367 // Fires the appropriate PropertyChanged events
369 public void OnUnderlyingSubModelChanged()
371 this.NotifySubPropertyChanged();
375 // Called when there is an error setting or getting a PropertyValue.
376 // Displays an error dialog.
378 // <param name="e"></param>
379 protected override void OnPropertyValueException(PropertyValueExceptionEventArgs e)
381 if (e.Source == PropertyValueExceptionSource.Set)
383 if (e.Exception != null)
385 Debug.WriteLine(e.Exception.ToString());
388 ErrorReporting.ShowErrorMessage(e.Exception.Message);
390 base.OnPropertyValueException(e);
394 base.OnPropertyValueException(e);
399 // Debuging-friendly ToString()
401 // <returns></returns>
402 [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
403 public override string ToString()
408 if (this.Value == null)
414 value = this.StringValue;
415 if (string.IsNullOrEmpty(value))
421 return string.Format(CultureInfo.CurrentCulture, "{0} (PropertyValue)", value ?? "{null}");
425 return base.ToString();