File: System.Activities.Presentation\System\Activities\Presentation\Base\Core\Internal\PropertyEditing\Model\ModelPropertyValue.cs
Project: ndp\cdf\src\NetFx40\Tools\System.Activities.Presentation.csproj (System.Activities.Presentation)
//----------------------------------------------------------------
// 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;
 
    // <summary>
    // Concrete implementation of PropertyValue that delegates to ModelPropertyEntryBase for
    // all actions.
    // </summary>
    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;
 
        // <summary>
        // Basic ctor
        // </summary>
        // <param name="parentProperty">Parent ModelPropertyEntryBase</param>
        public ModelPropertyValue(ModelPropertyEntryBase parentProperty) : base(parentProperty) 
        {
        }
 
        // <summary>
        // Returns the source of this property value
        // </summary>
        public override PropertyValueSource Source 
        {
            get {
                return ParentModelPropertyEntry.Source;
            }
        }
 
        // <summary>
        // Returns true if this value represents the default value of the property
        // </summary>
        public override bool IsDefaultValue 
        {
            get {
                return Source == DependencyPropertyValueSource.DefaultValue;
            }
        }
 
        // <summary>
        // Returns true if the value contained by this property is mixed
        // </summary>
        public override bool IsMixedValue 
        {
            get {
                return ParentModelPropertyEntry.IsMixedValue;
            }
        }
 
        // <summary>
        // Returns true if custom TypeConverter exists and if it can convert
        // the value from string.
        // </summary>
        public override bool CanConvertFromString 
        {
            get {
                return ParentModelPropertyEntry.Converter != null &&
                    ParentModelPropertyEntry.Converter.CanConvertFrom(typeof(string));
            }
        }
 
        // <summary>
        // Gets a flag indicating whether this PropertyValue has sub properties
        // </summary>
        public override bool HasSubProperties 
        {
            get {
                return ParentModelPropertyEntry.HasSubProperties;
            }
        }
 
        // <summary>
        // Gets the sub-properties of the PropertyValue
        // </summary>
        public override PropertyEntryCollection SubProperties 
        {
            get {
                return ParentModelPropertyEntry.SubProperties;
            }
        }
 
        // <summary>
        // Gets a flag indicating whether this PropertyValue represents a collection
        // </summary>
        public override bool IsCollection 
        {
            get {
                return ParentModelPropertyEntry.IsCollection;
            }
        }
 
        // <summary>
        // Gets the collection represented by this PropertyValue
        // </summary>
        public override PropertyValueCollection Collection 
        {
            get {
                return ParentModelPropertyEntry.Collection;
            }
        }
 
        // <summary>
        // 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.
        // </summary>
        [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
        public PropertyValue NameSensitiveInstance 
        {
            get {
                return this;
            }
        }
 
        // <summary>
        // Always catch exceptions
        // </summary>
        protected override bool CatchExceptions 
        {
            get {
                return true;
            }
        }
 
        // <summary>
        // Gets an en-us CultureInfo that ignores user-specified settings
        // </summary>
        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;
            }
        }
 
        // <summary>
        // Validates the value using the TypeConverter, if one exists
        // </summary>
        // <param name="valueToValidate">Value to validate</param>
        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");
        }
 
        // <summary>
        // Convert the specified string to a value
        // </summary>
        // <param name="stringToConvert"></param>
        // <returns></returns>
        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)));
        }
 
        // <summary>
        // Convert the specified value to a string
        // </summary>
        // <param name="valueToConvert"></param>
        // <returns></returns>
        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;
        }
 
 
        // <summary>
        // Redirect the call to parent PropertyEntry
        // </summary>
        // <returns></returns>
        protected override object GetValueCore() 
        {
            return ParentModelPropertyEntry.GetValueCore();
        }
 
        // <summary>
        // Redirect the call to parent PropertyEntry
        // </summary>
        // <param name="value"></param>
        protected override void SetValueCore(object value) 
        {
            if (value == ClearValueMarker)
            {
                ParentModelPropertyEntry.ClearValue();
            }
            else
            {
                ParentModelPropertyEntry.SetValueCore(value);
            }
        }
 
        // <summary>
        // Apply the FlowDirection to the resource.
        // </summary>
        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;
                }
            }
        }
 
        // <summary>
        // Redirect the call to parent PropertyEntry
        // </summary>
        public override void ClearValue() 
        {
            ParentModelPropertyEntry.ClearValue();
        }
 
        // <summary>
        // Fires the appropriate PropertyChanged events
        // </summary>
        public void OnUnderlyingModelChanged() 
        {
            CheckAndSetFlowDirectionResource();
            this.NotifyRootValueChanged();
        }
 
        // <summary>
        // Fires the appropriate PropertyChanged events
        // </summary>
        public void OnUnderlyingSubModelChanged() 
        {
            this.NotifySubPropertyChanged();
        }
 
        // <summary>
        // Called when there is an error setting or getting a PropertyValue.
        // Displays an error dialog.
        // </summary>
        // <param name="e"></param>
        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);
            }
        }
 
        // <summary>
        // Debuging-friendly ToString()
        // </summary>
        // <returns></returns>
        [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();
            }
        }
    }
}