//---------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //---------------------------------------------------------------- namespace System.Activities { using System.Activities.Debugger; using System.Activities.Validation; using System.Activities.XamlIntegration; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Runtime; using System.Windows.Markup; using System.Xaml; [ContentProperty("Implementation")] public sealed class ActivityBuilder : IDebuggableWorkflowTree { // define attached properties that will identify PropertyReferenceExtension-based // object properties static AttachableMemberIdentifier propertyReferencePropertyID = new AttachableMemberIdentifier(typeof(ActivityBuilder), "PropertyReference"); static AttachableMemberIdentifier propertyReferencesPropertyID = new AttachableMemberIdentifier(typeof(ActivityBuilder), "PropertyReferences"); KeyedCollection properties; Collection constraints; Collection attributes; public ActivityBuilder() { } public string Name { get; set; } [DependsOn("Name")] public Collection Attributes { get { if (this.attributes == null) { this.attributes = new Collection(); } return this.attributes; } } [Browsable(false)] [DependsOn("Attributes")] public KeyedCollection Properties { get { if (this.properties == null) { this.properties = new ActivityPropertyCollection(); } return this.properties; } } [DependsOn("Properties")] [Browsable(false)] public Collection Constraints { get { if (this.constraints == null) { this.constraints = new Collection(); } return this.constraints; } } [TypeConverter(typeof(ImplementationVersionConverter))] [DefaultValue(null)] [DependsOn("Name")] public Version ImplementationVersion { get; set; } [DefaultValue(null)] [Browsable(false)] [DependsOn("Constraints")] public Activity Implementation { get; set; } // Back-compat workaround: PropertyReference shipped in 4.0. PropertyReferences is new in 4.5. // // Requirements: // - Runtime compat: Get/SetPropertyReference needs to continue to work, both when set programatically // and when loading a doc which contains only one PropertyReference on an object. // - Serialization compat: If only one PropertyReference was set, we shouldn't serialize PropertyReferences. // (Only affects when ActivityBuilder is used directly with XamlServices, since ActivityXamlServices // will convert ActivityPropertyReference to PropertyReferenceExtension.) // - Usability: To avoid the designer needing to support two separate access methods, we want // the value from SetPropertyReference to also appear in the PropertyReferences collection. // activity property name public static ActivityPropertyReference GetPropertyReference(object target) { return GetPropertyReferenceCollection(target).SingleItem; } // activity property name public static void SetPropertyReference(object target, ActivityPropertyReference value) { GetPropertyReferenceCollection(target).SingleItem = value; } public static IList GetPropertyReferences(object target) { return GetPropertyReferenceCollection(target); } public static bool ShouldSerializePropertyReference(object target) { PropertyReferenceCollection propertyReferences = GetPropertyReferenceCollection(target); return propertyReferences.Count == 1 && propertyReferences.SingleItem != null; } public static bool ShouldSerializePropertyReferences(object target) { PropertyReferenceCollection propertyReferences = GetPropertyReferenceCollection(target); return propertyReferences.Count > 1 || propertyReferences.SingleItem == null; } internal static bool HasPropertyReferences(object target) { PropertyReferenceCollection propertyReferences; if (AttachablePropertyServices.TryGetProperty(target, propertyReferencesPropertyID, out propertyReferences)) { return propertyReferences.Count > 0; } return false; } static PropertyReferenceCollection GetPropertyReferenceCollection(object target) { PropertyReferenceCollection propertyReferences; if (!AttachablePropertyServices.TryGetProperty(target, propertyReferencesPropertyID, out propertyReferences)) { propertyReferences = new PropertyReferenceCollection(target); AttachablePropertyServices.SetProperty(target, propertyReferencesPropertyID, propertyReferences); } return propertyReferences; } Activity IDebuggableWorkflowTree.GetWorkflowRoot() { return this.Implementation; } internal static KeyedCollection CreateActivityPropertyCollection() { return new ActivityPropertyCollection(); } class ActivityPropertyCollection : KeyedCollection { protected override string GetKeyForItem(DynamicActivityProperty item) { return item.Name; } } // See back-compat requirements in comment above. Design is: // - First value added to collection when it is empty becomes the single PropertyReference value // - If the single value is removed, then PropertyReference AP is removed // - If PropertyReference AP is set to null, we remove the single value. // - If PropertyReference is set to non-null, we replace the existing single value if there // is one, or else add the new value to the collection. class PropertyReferenceCollection : Collection { WeakReference targetObject; int singleItemIndex = -1; public PropertyReferenceCollection(object target) { this.targetObject = new WeakReference(target); } public ActivityPropertyReference SingleItem { get { return this.singleItemIndex >= 0 ? this[this.singleItemIndex] : null; } set { if (this.singleItemIndex >= 0) { if (value != null) { SetItem(this.singleItemIndex, value); } else { RemoveItem(this.singleItemIndex); } } else if (value != null) { Add(value); if (Count > 1) { this.singleItemIndex = Count - 1; UpdateAttachedProperty(); } } } } protected override void ClearItems() { this.singleItemIndex = -1; UpdateAttachedProperty(); } protected override void InsertItem(int index, ActivityPropertyReference item) { base.InsertItem(index, item); if (index <= this.singleItemIndex) { this.singleItemIndex++; } else if (Count == 1) { Fx.Assert(this.singleItemIndex < 0, "How did we have an index if we were empty?"); this.singleItemIndex = 0; UpdateAttachedProperty(); } } protected override void RemoveItem(int index) { base.RemoveItem(index); if (index < this.singleItemIndex) { this.singleItemIndex--; } else if (index == this.singleItemIndex) { this.singleItemIndex = -1; UpdateAttachedProperty(); } } protected override void SetItem(int index, ActivityPropertyReference item) { base.SetItem(index, item); if (index == this.singleItemIndex) { UpdateAttachedProperty(); } } void UpdateAttachedProperty() { object target = this.targetObject.Target; if (target != null) { if (this.singleItemIndex >= 0) { AttachablePropertyServices.SetProperty(target, propertyReferencePropertyID, this[this.singleItemIndex]); } else { AttachablePropertyServices.RemoveProperty(target, propertyReferencePropertyID); } } } } } [ContentProperty("Implementation")] public sealed class ActivityBuilder : IDebuggableWorkflowTree { KeyedCollection properties; Collection constraints; Collection attributes; public ActivityBuilder() { } public string Name { get; set; } [DependsOn("Name")] public Collection Attributes { get { if (this.attributes == null) { this.attributes = new Collection(); } return this.attributes; } } [Browsable(false)] [DependsOn("Attributes")] public KeyedCollection Properties { get { if (this.properties == null) { this.properties = ActivityBuilder.CreateActivityPropertyCollection(); } return this.properties; } } [DependsOn("Properties")] [Browsable(false)] public Collection Constraints { get { if (this.constraints == null) { this.constraints = new Collection(); } return this.constraints; } } [TypeConverter(typeof(ImplementationVersionConverter))] [DefaultValue(null)] [DependsOn("Name")] public Version ImplementationVersion { get; set; } [DefaultValue(null)] [Browsable(false)] [DependsOn("Constraints")] public Activity Implementation { get; set; } Activity IDebuggableWorkflowTree.GetWorkflowRoot() { return this.Implementation; } } }