//---------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //---------------------------------------------------------------- namespace System.Activities.Presentation.Internal.PropertyEditing.Model { using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Windows; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Text; using System.Activities.Presentation; using System.Activities.Presentation.Model; using System.Activities.Presentation.PropertyEditing; using System.Activities.Presentation.Internal.PropertyEditing.Resources; using System.Activities.Presentation.Internal.Properties; using System.Activities.Presentation.Internal.PropertyEditing.Editors; // // Concrete implementation of PropertyValue that delegates to ModelPropertyEntryBase for // all actions. // internal class ModelPropertyValue : PropertyValue { // Object used to mark a property value that should be cleared instead of set private static readonly object ClearValueMarker = new object(); // CultureInfo instance we use for formatting values so that they reflect what is in Xaml private static CultureInfo _xamlCultureInfo; // // Basic ctor // // Parent ModelPropertyEntryBase public ModelPropertyValue(ModelPropertyEntryBase parentProperty) : base(parentProperty) { } // // Returns the source of this property value // public override PropertyValueSource Source { get { return ParentModelPropertyEntry.Source; } } // // Returns true if this value represents the default value of the property // public override bool IsDefaultValue { get { return Source == DependencyPropertyValueSource.DefaultValue; } } // // Returns true if the value contained by this property is mixed // public override bool IsMixedValue { get { return ParentModelPropertyEntry.IsMixedValue; } } // // Returns true if custom TypeConverter exists and if it can convert // the value from string. // public override bool CanConvertFromString { get { return ParentModelPropertyEntry.Converter != null && ParentModelPropertyEntry.Converter.CanConvertFrom(typeof(string)); } } // // Gets a flag indicating whether this PropertyValue has sub properties // public override bool HasSubProperties { get { return ParentModelPropertyEntry.HasSubProperties; } } // // Gets the sub-properties of the PropertyValue // public override PropertyEntryCollection SubProperties { get { return ParentModelPropertyEntry.SubProperties; } } // // Gets a flag indicating whether this PropertyValue represents a collection // public override bool IsCollection { get { return ParentModelPropertyEntry.IsCollection; } } // // Gets the collection represented by this PropertyValue // public override PropertyValueCollection Collection { get { return ParentModelPropertyEntry.Collection; } } // // This is an internal helper to which we can bind and on which we fire PropertyChanged // event when the Name sub-property (if one exists) changes. More-specifically, // we bind to this property in CollectionEditor to display the Type as well as the // Name of the items in the collection. This property is accessed from XAML. // [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public PropertyValue NameSensitiveInstance { get { return this; } } // // Always catch exceptions // protected override bool CatchExceptions { get { return true; } } // // Gets an en-us CultureInfo that ignores user-specified settings // private static CultureInfo XamlCultureInfo { get { if (_xamlCultureInfo == null) { _xamlCultureInfo = new CultureInfo("en-us", false); } return _xamlCultureInfo; } } // Convenience accessor private ModelPropertyEntryBase ParentModelPropertyEntry { get { return (ModelPropertyEntryBase)this.ParentProperty; } } // // Validates the value using the TypeConverter, if one exists // // Value to validate protected override void ValidateValue(object valueToValidate) { // Noop. We used to rely on TypeConverter.IsValid() here, but it turns out // that a bunch of standard TypeConverters don't really work (eg. Int32TypeConverter // returns true for IsValid("abc") and EnumConverter returns false for // IsValid(MyEnum.One | MyEnum.Two) even if MyEnum if adorned with FlagsAttribute) } // Called when there exists a Name sub-property for this value and it changes internal void OnNameSubPropertyChanged() { // Updates XAML bindings (collection editor item-display-name-template for one) this.OnPropertyChanged("NameSensitiveInstance"); } // // Convert the specified string to a value // // // protected override object ConvertStringToValue(string stringToConvert) { if (this.ParentProperty.PropertyType == typeof(string)) { return stringToConvert; } else if (string.IsNullOrEmpty(stringToConvert)) { // If the type of this property is string: // // StringValue of '' -> set Value to '' // StringValue of null -> ClearValue() // // Otherwise // // StringValue of '' -> ClearValue() // StringValue of null -> ClearValue() // if (stringToConvert != null && typeof(string).Equals(this.ParentProperty.PropertyType)) { return null; } else { return ClearValueMarker; } } else if (EditorUtilities.IsNullableEnumType(this.ParentProperty.PropertyType) && stringToConvert.Equals(EditorUtilities.NullString, StringComparison.Ordinal)) { // PS 107537: Special case handling when converting a string to a nullable enum type. return null; } else if (this.ParentModelPropertyEntry.Converter != null && this.ParentModelPropertyEntry.Converter.CanConvertFrom(typeof(string))) { return this.ParentModelPropertyEntry.Converter.ConvertFromString(null, XamlCultureInfo, stringToConvert); } throw FxTrace.Exception.AsError(new InvalidOperationException(string.Format( CultureInfo.CurrentCulture, Resources.PropertyEditing_NoStringToValueConversion, this.ParentProperty.DisplayName))); } // // Convert the specified value to a string // // // protected override string ConvertValueToString(object valueToConvert) { string stringValue = string.Empty; if (valueToConvert == null) { if (typeof(IList).IsAssignableFrom(this.ParentProperty.PropertyType)) { stringValue = Resources.PropertyEditing_DefaultCollectionStringValue; } else if (EditorUtilities.IsNullableEnumType(this.ParentProperty.PropertyType)) { // PS 107537: Special case handling when converting a nullable enum type to a string. return EditorUtilities.NullString; } return stringValue; } else if ((stringValue = valueToConvert as string) != null) { return stringValue; } TypeConverter typeConverter = this.ParentModelPropertyEntry.Converter; if (valueToConvert is Array) { stringValue = Resources.PropertyEditing_DefaultArrayStringValue; } else if (valueToConvert is IList || valueToConvert is ICollection || ModelUtilities.ImplementsICollection(valueToConvert.GetType()) || ModelUtilities.ImplementsIList(valueToConvert.GetType())) { stringValue = Resources.PropertyEditing_DefaultCollectionStringValue; } else if (valueToConvert is IEnumerable) { stringValue = Resources.PropertyEditing_DefaultEnumerableStringValue; } else if (typeConverter != null && typeConverter.CanConvertTo(typeof(string))) { stringValue = typeConverter.ConvertToString(null, XamlCultureInfo, valueToConvert); } else { stringValue = valueToConvert.ToString(); } return stringValue ?? string.Empty; } // // Redirect the call to parent PropertyEntry // // protected override object GetValueCore() { return ParentModelPropertyEntry.GetValueCore(); } // // Redirect the call to parent PropertyEntry // // protected override void SetValueCore(object value) { if (value == ClearValueMarker) { ParentModelPropertyEntry.ClearValue(); } else { ParentModelPropertyEntry.SetValueCore(value); } } // // Apply the FlowDirection to the resource. // private void CheckAndSetFlowDirectionResource() { // Check if the value being edited is FlowDirection // and if so, reset the resource to the current value that the user is setting. // This will refresh the property inspector and all the string editors, showing "string" properties, // would have their FlowDirection set to the current value. if (ParentModelPropertyEntry.PropertyName.Equals(FrameworkElement.FlowDirectionProperty.Name)) { object value = Value; if (value != null) { PropertyInspectorResources.GetResources()["SelectedControlFlowDirectionRTL"] = value; } } } // // Redirect the call to parent PropertyEntry // public override void ClearValue() { ParentModelPropertyEntry.ClearValue(); } // // Fires the appropriate PropertyChanged events // public void OnUnderlyingModelChanged() { CheckAndSetFlowDirectionResource(); this.NotifyRootValueChanged(); } // // Fires the appropriate PropertyChanged events // public void OnUnderlyingSubModelChanged() { this.NotifySubPropertyChanged(); } // // Called when there is an error setting or getting a PropertyValue. // Displays an error dialog. // // protected override void OnPropertyValueException(PropertyValueExceptionEventArgs e) { if (e.Source == PropertyValueExceptionSource.Set) { if (e.Exception != null) { Debug.WriteLine(e.Exception.ToString()); } ErrorReporting.ShowErrorMessage(e.Exception.Message); base.OnPropertyValueException(e); } else { base.OnPropertyValueException(e); } } // // Debuging-friendly ToString() // // [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] public override string ToString() { try { string value; if (this.Value == null) { value = "{null}"; } else { value = this.StringValue; if (string.IsNullOrEmpty(value)) { value = "{empty}"; } } return string.Format(CultureInfo.CurrentCulture, "{0} (PropertyValue)", value ?? "{null}"); } catch { return base.ToString(); } } } }