3c1f479b9d
Former-commit-id: 806294f5ded97629b74c85c09952f2a74fe182d9
1026 lines
47 KiB
C#
1026 lines
47 KiB
C#
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<PropertyDescriptor> properties = new List<PropertyDescriptor>();
|
|
|
|
// 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<Attribute> attributes = new List<Attribute>();
|
|
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<MemberInfo> GetBindableMembers(object obj, ITypeDescriptorContext context)
|
|
{
|
|
List<MemberInfo> memberInfos = new List<MemberInfo>();
|
|
|
|
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
|
|
}
|
|
|