namespace System.Workflow.ComponentModel.Design { #region Imports using System; using System.IO; using System.ComponentModel; using System.ComponentModel.Design; using System.Collections; using System.Collections.Generic; using System.Reflection; using System.CodeDom; using System.Windows.Forms; using System.Windows.Forms.Design; using System.Workflow.ComponentModel.Compiler; using System.Workflow.ComponentModel.Serialization; using System.Drawing.Design; using System.Collections.Specialized; using System.Diagnostics; using System.Globalization; #endregion #region Class IDPropertyDescriptor internal sealed class IDPropertyDescriptor : DynamicPropertyDescriptor { internal IDPropertyDescriptor(IServiceProvider serviceProvider, PropertyDescriptor actualPropDesc) : base(serviceProvider, actualPropDesc) { } public override bool CanResetValue(object component) { return false; } public override void SetValue(object component, object value) { Activity activity = component as Activity; if (activity != null) { ISite site = PropertyDescriptorUtils.GetSite(ServiceProvider, component); if (site == null) throw new Exception(SR.GetString(SR.General_MissingService, typeof(ISite).FullName)); IIdentifierCreationService identifierCreationService = site.GetService(typeof(IIdentifierCreationService)) as IIdentifierCreationService; if (identifierCreationService == null) throw new Exception(SR.GetString(SR.General_MissingService, typeof(IIdentifierCreationService).FullName)); string newID = value as string; identifierCreationService.ValidateIdentifier(activity, newID); DesignerHelpers.UpdateSiteName(activity, newID); base.SetValue(component, value); } } } #endregion #region Class NamePropertyDescriptor internal sealed class NamePropertyDescriptor : DynamicPropertyDescriptor { internal NamePropertyDescriptor(IServiceProvider serviceProvider, PropertyDescriptor actualPropDesc) : base(serviceProvider, actualPropDesc) { } public override string Category { get { return SR.GetString(SR.Activity); } } public override string Description { get { return SR.GetString(SR.RootActivityNameDesc); } } public override void SetValue(object component, object value) { Activity activity = component as Activity; if (activity != null) { // validate the identifier IIdentifierCreationService identifierCreationService = activity.Site.GetService(typeof(IIdentifierCreationService)) as IIdentifierCreationService; if (identifierCreationService == null) throw new Exception(SR.GetString(SR.General_MissingService, typeof(IIdentifierCreationService).FullName)); string name = value as string; identifierCreationService.ValidateIdentifier(activity, name); bool isVB = (CompilerHelpers.GetSupportedLanguage(activity.Site) == SupportedLanguages.VB); Type designedType = Helpers.GetDataSourceClass(Helpers.GetRootActivity(activity), activity.Site); if (designedType != null) { MemberInfo matchingMember = ActivityBindPropertyDescriptor.FindMatchingMember(name, designedType, isVB); if (matchingMember != null) throw new ArgumentException(SR.GetString(SR.Error_ActivityNameExist, name)); } IMemberCreationService memberCreationService = activity.Site.GetService(typeof(IMemberCreationService)) as IMemberCreationService; if (memberCreationService == null) throw new InvalidOperationException(SR.GetString(SR.General_MissingService, typeof(IMemberCreationService).FullName)); IDesignerHost host = activity.Site.GetService(typeof(IDesignerHost)) as IDesignerHost; if (host == null) throw new InvalidOperationException(SR.GetString(SR.General_MissingService, typeof(IDesignerHost).FullName)); // We need to update the activityType's name before trying to update the type because // updating the type causes a flush, which access the custom activity's properties, and // doing so requires the new type name string newClassName = name; int indexOfDot = host.RootComponentClassName.LastIndexOf('.'); if (indexOfDot > 0) newClassName = host.RootComponentClassName.Substring(0, indexOfDot + 1) + name; // IMPORTANT: You must update the class name in code before renaming the site, since // VS's OnComponentRename updates the RootComponentClassName, so the flush code called // in our OnComponentRename tries to access the new class for information. memberCreationService.UpdateTypeName(((Activity)host.RootComponent).GetValue(WorkflowMarkupSerializer.XClassProperty) as string, newClassName); //((Activity)host.RootComponent).Name = name; ((Activity)host.RootComponent).SetValue(WorkflowMarkupSerializer.XClassProperty, newClassName); base.SetValue(component, value); // Update the site name so the component name shows up correctly in the designer DesignerHelpers.UpdateSiteName((Activity)host.RootComponent, name); } } public override bool CanResetValue(object component) { return false; } } #endregion #region Class PropertyDescriptorUtils internal static class PropertyDescriptorFilter { internal static PropertyDescriptorCollection FilterProperties(IServiceProvider serviceProvider, object propertyOwner, PropertyDescriptorCollection props) { Hashtable properties = new Hashtable(); foreach (PropertyDescriptor prop in props) { if (!properties.ContainsKey(prop.Name)) properties.Add(prop.Name, prop); } FilterProperties(serviceProvider, propertyOwner, properties); PropertyDescriptor[] returnProps = new PropertyDescriptor[properties.Count]; properties.Values.CopyTo(returnProps, 0); return new PropertyDescriptorCollection(returnProps); } internal static void FilterProperties(IServiceProvider serviceProvider, object propertyOwner, IDictionary props) { InternalFilterProperties(serviceProvider, propertyOwner, props); if (propertyOwner != null) { foreach (PropertyDescriptor property in GetPropertiesForEvents(serviceProvider, propertyOwner)) { if (!props.Contains(property.Name)) props.Add(property.Name, property); } } } private static void InternalFilterProperties(IServiceProvider serviceProvider, object propertyOwner, IDictionary properties) { // change property descriptors Hashtable newProperties = new Hashtable(); foreach (object key in properties.Keys) { PropertyDescriptor propDesc = properties[key] as PropertyDescriptor; if (string.Equals(propDesc.Name, "Name", StringComparison.Ordinal) && typeof(Activity).IsAssignableFrom(propDesc.ComponentType)) { //Activity id Activity activity = propertyOwner as Activity; if (activity != null && activity == Helpers.GetRootActivity(activity)) newProperties[key] = new NamePropertyDescriptor(serviceProvider, propDesc); else newProperties[key] = new IDPropertyDescriptor(serviceProvider, propDesc); } else if (!(propDesc is ActivityBindPropertyDescriptor) && ActivityBindPropertyDescriptor.IsBindableProperty(propDesc)) { if (typeof(Type).IsAssignableFrom(propDesc.PropertyType) && !(propDesc is ParameterInfoBasedPropertyDescriptor)) propDesc = new TypePropertyDescriptor(serviceProvider, propDesc); newProperties[key] = new ActivityBindPropertyDescriptor(serviceProvider, propDesc, propertyOwner); } else if (typeof(Type).IsAssignableFrom(propDesc.PropertyType)) { newProperties[key] = new TypePropertyDescriptor(serviceProvider, propDesc); } else { newProperties[key] = new DynamicPropertyDescriptor(serviceProvider, propDesc); } } foreach (object key in newProperties.Keys) { properties[key] = newProperties[key]; } } internal static PropertyDescriptorCollection GetPropertiesForEvents(IServiceProvider serviceProvider, object eventOwner) { //Now for each event we need to add properties List properties = new List(); // Find out if there is a data context. IEventBindingService eventBindingService = serviceProvider.GetService(typeof(IEventBindingService)) as IEventBindingService; if (eventBindingService != null) { foreach (EventDescriptor eventDesc in TypeDescriptor.GetEvents(eventOwner)) { if (eventDesc.IsBrowsable) { PropertyDescriptor propertyDescriptor = eventBindingService.GetEventProperty(eventDesc); if (!(propertyDescriptor is ActivityBindPropertyDescriptor) && ActivityBindPropertyDescriptor.IsBindableProperty(propertyDescriptor)) properties.Add(new ActivityBindPropertyDescriptor(serviceProvider, propertyDescriptor, eventOwner)); else properties.Add(propertyDescriptor); } } } return new PropertyDescriptorCollection(properties.ToArray()); } } #endregion #region ActivityBind PropertyBrowser Integration #region Class ActivityBindPropertyDescriptor /// Please note that ActivityBindPropertyDescriptor is now changed so that it can support MetaProperty binds too. /// Although this feature is not yet enabled. Please change code in the PropertyDescritorUtils.InternalFilterProperties to support it /// We need this property descriptor for following reason: /// When we are not in data context we support emission of events directly by using EventBindingService /// When we do this the events dont get stored in the DependencyObject rather than we store them in userdata /// Only the event property descriptor knows how to get it back. /// At the same time we also support Promotion of the events (ActivityBind) when using this the information gets /// stored directly in the DependencyObject which we fetch from the ActivityBindPropertyDescriptor base class /// Whenever the code in this class is changed please run the following test cases /// 1. In ActivityDesigner Drop Code and Promote Properties. (ExecuteCode should get promoted) /// 2. In ActivityDesigner, change the promoted property to event handler (Event needs to be emitted with += syntax) /// 3. In ActivityDesigner, Set the data context to true and try to generate the event for code. (Event should be emitted in DataContext) /// 4. In ActivityDesigner, make sure that when within data context the serialization works through the bind, when outside works through += /// Try all of the above cases in WorkflowDesigner where root supports datacontext, roots that dont support data context internal class ActivityBindPropertyDescriptor : DynamicPropertyDescriptor { private object propertyOwner = null; internal ActivityBindPropertyDescriptor(IServiceProvider serviceProvider, PropertyDescriptor realPropertyDescriptor, object propertyOwner) : base(serviceProvider, realPropertyDescriptor) { this.propertyOwner = propertyOwner; } public override bool IsReadOnly { get { return RealPropertyDescriptor.IsReadOnly; } } public override TypeConverter Converter { get { TypeConverter baseTypeConverter = base.Converter; if (typeof(ActivityBindTypeConverter).IsAssignableFrom(baseTypeConverter.GetType())) return baseTypeConverter; else return new ActivityBindTypeConverter(); } } public override AttributeCollection Attributes { get { List attributes = new List(); foreach (Attribute attribute in base.Attributes) attributes.Add(attribute); object uiTypeEditor = RealPropertyDescriptor.GetEditor(typeof(UITypeEditor)); object value = (PropertyOwner != null) ? GetValue(PropertyOwner) : null; bool propertiesSupported = RealPropertyDescriptor.Converter.GetPropertiesSupported((PropertyOwner != null) ? new TypeDescriptorContext(ServiceProvider, RealPropertyDescriptor, PropertyOwner) : null); if (((uiTypeEditor == null && !propertiesSupported) || value is ActivityBind) && !IsReadOnly) attributes.Add(new EditorAttribute(typeof(BindUITypeEditor), typeof(UITypeEditor))); return new AttributeCollection(attributes.ToArray()); } } public override object GetEditor(Type editorBaseType) { //If the converter is simple type converter and there is a simple UITypeEditor object editor = base.GetEditor(editorBaseType); if (editorBaseType == typeof(UITypeEditor) && !IsReadOnly) { object value = (PropertyOwner != null) ? GetValue(PropertyOwner) : null; bool propertiesSupported = RealPropertyDescriptor.Converter.GetPropertiesSupported((PropertyOwner != null) ? new TypeDescriptorContext(ServiceProvider, RealPropertyDescriptor, PropertyOwner) : null); if (value is ActivityBind || (editor == null && !propertiesSupported)) editor = new BindUITypeEditor(); } return editor; } public override object GetValue(object component) { object value = null; DependencyObject dependencyObj = component as DependencyObject; DependencyProperty dependencyProperty = DependencyProperty.FromName(Name, ComponentType); if (dependencyObj != null && dependencyProperty != null) { if (dependencyObj.IsBindingSet(dependencyProperty)) value = dependencyObj.GetBinding(dependencyProperty); } //We have to also call base's getvalue as Bindings are stored in MetaProperties collection of DependencyObject but //actual values are stored in DependencyValueProperties if (!(value is ActivityBind)) value = base.GetValue(component); return value; } public override void SetValue(object component, object value) { object oldValue = GetValue(component); ActivityBind activityBind = value as ActivityBind; DependencyObject dependencyObj = component as DependencyObject; DependencyProperty dependencyProperty = DependencyProperty.FromName(Name, ComponentType); if (dependencyObj != null && dependencyProperty != null && activityBind != null) { ComponentChangeDispatcher componentChangeDispatcher = new ComponentChangeDispatcher(ServiceProvider, dependencyObj, this); try { if (dependencyProperty.IsEvent && ServiceProvider != null) { IEventBindingService eventBindingService = ServiceProvider.GetService(typeof(IEventBindingService)) as IEventBindingService; if (eventBindingService != null) { EventDescriptor eventDescriptor = eventBindingService.GetEvent(RealPropertyDescriptor); if (eventDescriptor != null) RealPropertyDescriptor.SetValue(component, null); } } dependencyObj.SetBinding(dependencyProperty, activityBind); base.OnValueChanged(dependencyObj, EventArgs.Empty); } finally { componentChangeDispatcher.Dispose(); } } else { if (dependencyObj != null && dependencyProperty != null && dependencyObj.IsBindingSet(dependencyProperty)) { ComponentChangeDispatcher componentChangeDispatcher = new ComponentChangeDispatcher(ServiceProvider, dependencyObj, this); try { dependencyObj.RemoveProperty(dependencyProperty); // Need to fire component changed event because this means we're clearing // out a previously set Bind value. If the new value matches the old value stored in the user data, // base.SetValue will do nothing but return. When that happens, if we don't fire a change // event here, we'll still have the activity bind in the code or xoml file. base.OnValueChanged(dependencyObj, EventArgs.Empty); } finally { componentChangeDispatcher.Dispose(); } } base.SetValue(component, value); } //Following code is for making sure that when we change the value from activity bind to actual value //and from actual value to activity bind; we need to change the UITypeEditor associated with property //from data binding editor to the editor specified by the user if (oldValue != value && ((oldValue is ActivityBind && !(value is ActivityBind)) || (!(oldValue is ActivityBind) && value is ActivityBind))) { TypeDescriptor.Refresh(component); } } #region Helpers internal object PropertyOwner { get { return this.propertyOwner; } } #endregion #region Static Helpers internal static IList GetBindableMembers(object obj, ITypeDescriptorContext context) { List memberInfos = new List(); IDesignerHost designerHost = context.GetService(typeof(IDesignerHost)) as IDesignerHost; Activity rootActivity = (designerHost != null) ? designerHost.RootComponent as Activity : null; Type objectType = (obj == rootActivity) ? Helpers.GetDataSourceClass(rootActivity, context) : obj.GetType(); Type memberType = PropertyDescriptorUtils.GetBaseType(context.PropertyDescriptor, context.Instance, context); if (objectType != null && memberType != null) { DependencyProperty dependencyProperty = DependencyProperty.FromName(context.PropertyDescriptor.Name, context.PropertyDescriptor.ComponentType); bool includeEvents = (dependencyProperty != null && dependencyProperty.IsEvent); BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy; if (obj == rootActivity) bindingFlags |= BindingFlags.NonPublic; foreach (MemberInfo memberInfo in objectType.GetMembers(bindingFlags)) { //filter our methods with System.Diagnostics.DebuggerNonUserCodeAttribute object[] nonUserCodeAttributes = memberInfo.GetCustomAttributes(typeof(System.Diagnostics.DebuggerNonUserCodeAttribute), false); if (nonUserCodeAttributes != null && nonUserCodeAttributes.Length > 0 && nonUserCodeAttributes[0] is System.Diagnostics.DebuggerNonUserCodeAttribute) continue; object[] browsableAttributes = memberInfo.GetCustomAttributes(typeof(BrowsableAttribute), false); if (browsableAttributes.Length > 0) { bool browsable = false; BrowsableAttribute browsableAttribute = browsableAttributes[0] as BrowsableAttribute; if (browsableAttribute != null) { browsable = browsableAttribute.Browsable; } else { try { AttributeInfoAttribute attributeInfoAttribute = browsableAttributes[0] as AttributeInfoAttribute; if (attributeInfoAttribute != null && attributeInfoAttribute.AttributeInfo.ArgumentValues.Count > 0) browsable = (bool)attributeInfoAttribute.AttributeInfo.GetArgumentValueAs(context, 0, typeof(bool)); } catch { } } if (!browsable) continue; } if (memberInfo.DeclaringType == typeof(System.Object) && (string.Equals(memberInfo.Name, "Equals", StringComparison.Ordinal) || string.Equals(memberInfo.Name, "ReferenceEquals", StringComparison.Ordinal))) continue; bool addMember = false; bool isProtectedOrPublicMember = false; bool isInternalMember = false; if (includeEvents && memberInfo is EventInfo) { EventInfo eventInfo = memberInfo as EventInfo; MethodInfo addAccessor = eventInfo.GetAddMethod(); MethodInfo removeAccessor = eventInfo.GetRemoveMethod(); isProtectedOrPublicMember = ((addAccessor != null && addAccessor.IsFamily) || (removeAccessor != null && removeAccessor.IsFamily) || (addAccessor != null && addAccessor.IsPublic) || (removeAccessor != null && removeAccessor.IsPublic)); isInternalMember = ((addAccessor != null && addAccessor.IsAssembly) || (removeAccessor != null && removeAccessor.IsAssembly)); addMember = TypeProvider.IsAssignable(memberType, eventInfo.EventHandlerType); } else if (memberInfo is FieldInfo) { FieldInfo fieldInfo = memberInfo as FieldInfo; isProtectedOrPublicMember = (fieldInfo.IsFamily || fieldInfo.IsPublic); isInternalMember = fieldInfo.IsAssembly; addMember = TypeProvider.IsAssignable(memberType, fieldInfo.FieldType); } else if (memberInfo is PropertyInfo) { PropertyInfo propertyInfo = memberInfo as PropertyInfo; MethodInfo getAccessor = propertyInfo.GetGetMethod(); MethodInfo setAccessor = propertyInfo.GetSetMethod(); isProtectedOrPublicMember = ((getAccessor != null && getAccessor.IsFamily) || (setAccessor != null && setAccessor.IsFamily) || (getAccessor != null && getAccessor.IsPublic) || (setAccessor != null && setAccessor.IsPublic)); isInternalMember = ((getAccessor != null && getAccessor.IsAssembly) || (setAccessor != null && setAccessor.IsAssembly)); addMember = (getAccessor != null && TypeProvider.IsAssignable(memberType, propertyInfo.PropertyType)); } //We only want to allow binding to protected, public and internal members of baseType if (memberInfo.DeclaringType != objectType && !isProtectedOrPublicMember && !(memberInfo.DeclaringType.Assembly == null && isInternalMember)) addMember = false; if (addMember) memberInfos.Add(memberInfo); } } return memberInfos.AsReadOnly(); } internal static bool CreateField(ITypeDescriptorContext context, ActivityBind activityBind, bool throwOnError) { //Check if the activity is root activity and has valid design time type if (!String.IsNullOrEmpty(activityBind.Path)) { Type boundType = PropertyDescriptorUtils.GetBaseType(context.PropertyDescriptor, context.Instance, context); Activity activity = PropertyDescriptorUtils.GetComponent(context) as Activity; if (activity != null && boundType != null) { activity = Helpers.ParseActivityForBind(activity, activityBind.Name); if (activity == Helpers.GetRootActivity(activity)) { bool isVB = (CompilerHelpers.GetSupportedLanguage(context) == SupportedLanguages.VB); Type designedType = Helpers.GetDataSourceClass(activity, context); if (designedType != null) { //field path could be nested too. //need to find field only with the name up to the first dot (CimplexTypeField in the example below) //and the right type (that would be tricky if the field doesnt exist yet) //example: CimplexTypeField.myIDictionary_int_string[10].someOtherGood2 string fieldName = activityBind.Path; int indexOfDot = fieldName.IndexOfAny(new char[] { '.', '/', '[' }); if (indexOfDot != -1) fieldName = fieldName.Substring(0, indexOfDot); //path is a nested field access MemberInfo matchingMember = ActivityBindPropertyDescriptor.FindMatchingMember(fieldName, designedType, isVB); if (matchingMember != null) { Type memberType = null; bool isPrivate = false; if (matchingMember is FieldInfo) { isPrivate = ((FieldInfo)matchingMember).IsPrivate; memberType = ((FieldInfo)matchingMember).FieldType; } else if (matchingMember is PropertyInfo) { MethodInfo getMethod = ((PropertyInfo)matchingMember).GetGetMethod(); MethodInfo setMethod = ((PropertyInfo)matchingMember).GetSetMethod(); isPrivate = ((getMethod != null && getMethod.IsPrivate) || (setMethod != null && setMethod.IsPrivate)); } else if (matchingMember is MethodInfo) { isPrivate = ((MethodInfo)matchingMember).IsPrivate; } if (indexOfDot != -1) { //need to find the type of the member the path references (and if the path is valid at all) PathWalker pathWalker = new PathWalker(); PathMemberInfoEventArgs finalEventArgs = null; pathWalker.MemberFound += delegate(object sender, PathMemberInfoEventArgs eventArgs) { finalEventArgs = eventArgs; }; if (pathWalker.TryWalkPropertyPath(designedType, activityBind.Path)) { //successfully walked the entire path memberType = BindHelpers.GetMemberType(finalEventArgs.MemberInfo); } else { //the path is invalid if (throwOnError) throw new InvalidOperationException(SR.GetString(SR.Error_MemberWithSameNameExists, activityBind.Path, designedType.FullName)); return false; } } if ((matchingMember.DeclaringType == designedType || !isPrivate) && matchingMember is FieldInfo && TypeProvider.IsAssignable(boundType, memberType)) { return true; } else { if (throwOnError) throw new InvalidOperationException(SR.GetString(SR.Error_MemberWithSameNameExists, activityBind.Path, designedType.FullName)); return false; } } else { // Find out if the name conflicts with an existing activity that has not be flushed in to the // code beside. An activity bind can bind to this field only if the type of the property // is the assignable from the activity type. Activity matchingActivity = null; if (string.Compare(activity.Name, fieldName, isVB ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal) == 0) matchingActivity = activity; else if (activity is CompositeActivity) { if (activity is CompositeActivity) { foreach (Activity existingActivity in Helpers.GetAllNestedActivities(activity as CompositeActivity)) { if (string.Compare(existingActivity.Name, fieldName, isVB ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal) == 0) matchingActivity = existingActivity; } } } if (matchingActivity != null) { if (TypeProvider.IsAssignable(boundType, matchingActivity.GetType())) return true; else { if (throwOnError) throw new InvalidOperationException(SR.GetString(SR.Error_MemberWithSameNameExists, activityBind.Path, designedType.FullName)); return false; } } } IMemberCreationService memberCreationService = context.GetService(typeof(IMemberCreationService)) as IMemberCreationService; if (memberCreationService == null) { if (throwOnError) throw new InvalidOperationException(SR.GetString(SR.General_MissingService, typeof(IMemberCreationService).FullName)); } else { IDesignerHost designerHost = context.GetService(typeof(IDesignerHost)) as IDesignerHost; if (designerHost == null) { if (throwOnError) throw new InvalidOperationException(SR.GetString("General_MissingService", typeof(IDesignerHost).FullName)); } else { memberCreationService.CreateField(designerHost.RootComponentClassName, activityBind.Path, boundType, null, MemberAttributes.Public, null, false); return true; } } } } } else { if (activity == null && throwOnError) throw new InvalidOperationException(SR.GetString(SR.Error_InvalidActivityIdentifier, activityBind.Name)); if (boundType == null && throwOnError) throw new InvalidOperationException(SR.GetString(SR.Error_PropertyTypeNotDefined, context.PropertyDescriptor.Name, typeof(ActivityBind).Name, typeof(IDynamicPropertyTypeProvider).Name)); } } return false; } internal static bool IsBindableProperty(PropertyDescriptor propertyDescriptor) { //The property type itself is ActivityBind; we dont support such cases very well in componentmodel //but still we need to handle such cases as a fallback in ui if (propertyDescriptor.PropertyType == typeof(ActivityBind)) return true; //We check this condition so that we will make sure that ActivityBind UI infrastructure //kicks into action in cases of parameter properties. User might sometimes do this on their properties //but in such cases they need to write their custom property descriptors just as we have written //ParameterInfoBasedPropertyDescriptor if (propertyDescriptor.Converter is ActivityBindTypeConverter) return true; //For all the other cases DependencyProperty dependencyProperty = DependencyProperty.FromName(propertyDescriptor.Name, propertyDescriptor.ComponentType); if (typeof(DependencyObject).IsAssignableFrom(propertyDescriptor.ComponentType) && dependencyProperty != null && !dependencyProperty.DefaultMetadata.IsMetaProperty) return true; return false; } internal static MemberInfo FindMatchingMember(string name, Type ownerType, bool ignoreCase) { MemberInfo matchingMember = null; foreach (MemberInfo memberInfo in ownerType.GetMembers(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)) { if (memberInfo.Name.Equals(name, ((ignoreCase) ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal))) { matchingMember = memberInfo; break; } } return matchingMember; } #endregion } #endregion #region Class ActivityBindNamePropertyDescriptor internal sealed class ActivityBindNamePropertyDescriptor : DynamicPropertyDescriptor { private ITypeDescriptorContext context; public ActivityBindNamePropertyDescriptor(ITypeDescriptorContext context, PropertyDescriptor realPropertyDescriptor) : base(context, realPropertyDescriptor) { this.context = context; } public override object GetValue(object component) { object value = base.GetValue(component); string id = value as string; if (!String.IsNullOrEmpty(id)) { Activity activity = PropertyDescriptorUtils.GetComponent(this.context) as Activity; activity = (activity != null) ? Helpers.ParseActivityForBind(activity, id) : null; value = (activity != null) ? activity.QualifiedName : id; } return value; } public override void SetValue(object component, object value) { string id = value as string; if (String.IsNullOrEmpty(id)) throw new InvalidOperationException(SR.GetString(SR.Error_ActivityIdentifierCanNotBeEmpty)); Activity activity = PropertyDescriptorUtils.GetComponent(this.context) as Activity; if (activity != null) { if (Helpers.ParseActivityForBind(activity, id) == null) throw new InvalidOperationException(SR.GetString(SR.Error_InvalidActivityIdentifier, id)); } base.SetValue(component, value); } } #endregion #region Class ActivityBindPathPropertyDescriptor internal sealed class ActivityBindPathPropertyDescriptor : DynamicPropertyDescriptor { private ITypeDescriptorContext context; public ActivityBindPathPropertyDescriptor(ITypeDescriptorContext context, PropertyDescriptor realPropertyDescriptor) : base(context, realPropertyDescriptor) { this.context = context; } internal ITypeDescriptorContext OuterPropertyContext { get { return this.context; } } } #endregion #endregion #region ReadOnly Properties Integration #region Class ReadonlyTypeDescriptorProvider internal sealed class ReadonlyTypeDescriptonProvider : TypeDescriptionProvider { internal ReadonlyTypeDescriptonProvider(TypeDescriptionProvider realProvider) : base(realProvider) { } public override ICustomTypeDescriptor GetTypeDescriptor(Type type, object instance) { ICustomTypeDescriptor realTypeDescriptor = base.GetTypeDescriptor(type, instance); ICustomTypeDescriptor readonlyTypeDescriptor = new ReadonlyTypeDescriptor(realTypeDescriptor); return readonlyTypeDescriptor; } } #endregion #region Class ReadonlyTypeDescriptor internal sealed class ReadonlyTypeDescriptor : CustomTypeDescriptor { internal ReadonlyTypeDescriptor(ICustomTypeDescriptor realTypeDescriptor) : base(realTypeDescriptor) { } public override AttributeCollection GetAttributes() { ArrayList collection = new ArrayList(); foreach (Attribute attribute in base.GetAttributes()) { //should not have any editor attribute and only one readonly attribute if (!(attribute is EditorAttribute || attribute is ReadOnlyAttribute)) collection.Add(attribute); } collection.Add(new ReadOnlyAttribute(true)); AttributeCollection newCollection = new AttributeCollection((Attribute[])collection.ToArray(typeof(Attribute))); return newCollection; } public override PropertyDescriptorCollection GetProperties() { PropertyDescriptorCollection properties = base.GetProperties(); ArrayList readonlyProperties = new ArrayList(); foreach (PropertyDescriptor property in properties) { BrowsableAttribute browsable = property.Attributes[typeof(BrowsableAttribute)] as BrowsableAttribute; if (browsable != null && browsable.Browsable && !(property is ReadonlyPropertyDescriptor)) readonlyProperties.Add(new ReadonlyPropertyDescriptor(property)); else readonlyProperties.Add(property); } return new PropertyDescriptorCollection((PropertyDescriptor[])readonlyProperties.ToArray(typeof(PropertyDescriptor))); } public override EventDescriptorCollection GetEvents(Attribute[] attributes) { EventDescriptorCollection events = base.GetEvents(attributes); ArrayList readonlyEvents = new ArrayList(); foreach (EventDescriptor e in events) { BrowsableAttribute browsable = e.Attributes[typeof(BrowsableAttribute)] as BrowsableAttribute; if (browsable != null && browsable.Browsable) readonlyEvents.Add(new ReadonlyEventDescriptor(e)); else readonlyEvents.Add(e); } return new EventDescriptorCollection((EventDescriptor[])readonlyEvents.ToArray(typeof(EventDescriptor))); } } #endregion #region Class ReadonlyPropertyDescriptor internal sealed class ReadonlyPropertyDescriptor : PropertyDescriptor { private PropertyDescriptor realPropertyDescriptor; internal ReadonlyPropertyDescriptor(PropertyDescriptor descriptor) : base(descriptor, null) { this.realPropertyDescriptor = descriptor; } public override string Category { get { return this.realPropertyDescriptor.Category; } } public override AttributeCollection Attributes { get { ArrayList collection = new ArrayList(); foreach (Attribute attribute in this.realPropertyDescriptor.Attributes) { //should not have any editor attribute and only one readonly attribute if (!(attribute is EditorAttribute || attribute is ReadOnlyAttribute)) collection.Add(attribute); } collection.Add(new ReadOnlyAttribute(true)); AttributeCollection newCollection = new AttributeCollection((Attribute[])collection.ToArray(typeof(Attribute))); return newCollection; } } public override TypeConverter Converter { get { return this.realPropertyDescriptor.Converter; } } public override string Description { get { return this.realPropertyDescriptor.Description; } } public override Type ComponentType { get { return this.realPropertyDescriptor.ComponentType; } } public override Type PropertyType { get { return this.realPropertyDescriptor.PropertyType; } } public override bool IsReadOnly { get { return true; } } public override void ResetValue(object component) { this.realPropertyDescriptor.ResetValue(component); } public override bool CanResetValue(object component) { return false; } public override bool ShouldSerializeValue(object component) { return this.realPropertyDescriptor.ShouldSerializeValue(component); } public override object GetValue(object component) { return this.realPropertyDescriptor.GetValue(component); } public override void SetValue(object component, object value) { //This is readonly property descriptor Debug.Assert(false, "SetValue should not be called on readonly property!"); } } #endregion #region Class ReadonlyEventDescriptor internal sealed class ReadonlyEventDescriptor : EventDescriptor { private EventDescriptor realEventDescriptor; internal ReadonlyEventDescriptor(EventDescriptor e) : base(e, null) { this.realEventDescriptor = e; } public override string Category { get { return this.realEventDescriptor.Category; } } public override AttributeCollection Attributes { get { ArrayList collection = new ArrayList(); foreach (Attribute attribute in this.realEventDescriptor.Attributes) { //should not have any editor attribute and only one readonly attribute if (!(attribute is EditorAttribute || attribute is ReadOnlyAttribute)) collection.Add(attribute); } collection.Add(new ReadOnlyAttribute(true)); AttributeCollection newCollection = new AttributeCollection((Attribute[])collection.ToArray(typeof(Attribute))); return newCollection; } } public override string Description { get { return this.realEventDescriptor.Description; } } public override Type ComponentType { get { return this.realEventDescriptor.ComponentType; } } public override Type EventType { get { return this.realEventDescriptor.EventType; } } public override bool IsMulticast { get { return this.realEventDescriptor.IsMulticast; } } public override void AddEventHandler(object component, Delegate value) { //This is readonly event descriptor } public override void RemoveEventHandler(object component, Delegate value) { //This is readonly event descriptor } } #endregion #endregion }