//---------------------------------------------------------------- // 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.Diagnostics; using System.Text; using System.Activities.Presentation; using System.Activities.Presentation.Model; using System.Activities.Presentation.PropertyEditing; using System.Activities.Presentation.Services; // <summary> // Cider-specific base class for PropertyEntry. It is shared by both // ModelPropertyEntry and ModelPropertyIndexer. ModelPropertyEntry is used // to model regular properties that are backed by Cider's ModelProperty. // ModelPropertyIndexers are used to model items in collections that are // backed by Cider's ModelItems. // </summary> internal abstract class ModelPropertyEntryBase : PropertyEntry { // Cache the depth of this property because once a property entry is create // it doesn't jump levels. Depth is only used to track sub-property tree's // not collection trees. private int _depth; private string _propertyPath; protected ModelPropertyEntryBase() : this(null) { } protected ModelPropertyEntryBase(PropertyValue parentValue) : base(parentValue) { UpdateDepth(); } public abstract PropertyValueSource Source { get; } public abstract bool IsMixedValue { get; } public abstract Type CommonValueType { get; } public abstract TypeConverter Converter { get; } // <summary> // Gets a flag indicating whether this property exposes any sub-properties. // We rely on TypeConverter.GetPropertiesSupported() for this value. // </summary> public bool HasSubProperties { get { return this.PropertyValue.Value != null && this.Converter != null && this.Converter.GetPropertiesSupported() && !this.IsMarkupExtension; } } public abstract PropertyEntryCollection SubProperties { get; } // <summary> // Gets a flag indicating whether the type of the contained property // can be assigned to an IList // </summary> public bool IsCollection { get { return typeof(IList).IsAssignableFrom(this.PropertyType); } } public abstract PropertyValueCollection Collection { get; } // <summary> // Gets the depth of this property in the PI sub-property tree // </summary> public virtual int Depth { get { return _depth; } } // <summary> // Gets a ',' separated path of this property through its // sub-property hierarchy. // </summary> public string SubPropertyHierarchyPath { get { if (_propertyPath == null) { _propertyPath = ModelUtilities.GetSubPropertyHierarchyPath(this); } return _propertyPath; } } // <summary> // Checks to see if the value of this property comes from a MarkupExtension // </summary> internal bool IsMarkupExtension { get { DependencyPropertyValueSource source = this.Source as DependencyPropertyValueSource; return source != null && source.IsExpression; } } internal abstract bool CollectionInstanceExists { get; } // <summary> // Convenience accessor // </summary> protected ModelPropertyValue ModelPropertyValue { get { return (ModelPropertyValue)this.PropertyValue; } } public abstract object GetValueCore(); public abstract void SetValueCore(object value); public abstract void ClearValue(); // Calculate the depth of this property in the sub-property // hierarchy // private void UpdateDepth() { if (ParentValue != null) { _depth = ((ModelPropertyEntryBase)ParentValue.ParentProperty).Depth + 1; } } // <summary> // Called when one of the sub-properties exposed by this class changes. // There is a call to the concrete implementation of this class so that it // can do any internal cache clean up as needed, followed by the firing // of the appropriate changed events. // </summary> public void OnUnderlyingSubModelChanged() { OnUnderlyingSubModelChangedCore(); this.ModelPropertyValue.OnUnderlyingSubModelChanged(); } // <summary> // Called when one of the sub-properties exposed by this class changes // that allows the concrete implementation of this class to clean up // any internal state. // </summary> protected abstract void OnUnderlyingSubModelChangedCore(); // <summary> // Clears or updates any cached values - call this method // when the underlying ModelProperty changes and cached values // may have become invalid // </summary> protected virtual void RefreshCache() { UpdateDepth(); _propertyPath = null; } internal abstract ModelEditingScope BeginEdit(string description); } } |