using System;
using System.Collections.Generic;
using System.Globalization;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Workflow.ComponentModel;
using System.ComponentModel.Design;
using System.Workflow.ComponentModel.Compiler;
using System.Reflection;
using System.Diagnostics;
using System.Collections.ObjectModel;
using System.Windows.Forms.Design;
using System.Diagnostics.CodeAnalysis;
namespace System.Workflow.ComponentModel.Design
{
internal sealed partial class ActivityBindForm : Form
{
//ui control
private ActivityBindFormWorkflowOutline workflowOutline = null;
private IServiceProvider serviceProvider = null;
private ITypeDescriptorContext context = null;
private Type boundType = null;
//returns
private ActivityBind binding = null;
private bool createNew = false;//create new field or use existing
private bool createNewProperty = false;//create property or field
private string newMemberName = string.Empty;
private const string MemberTypeFormat = "MemberType#{0}";//non-localiazable, used for image list keys
private string ActivityBindDialogTitleFormat;// = "Bind '{0}' to Activity Property";
private string PropertyAssignableFormat;// = "Selected property of type '{0}' is assignable to the target property type '{1}'.";
private string DescriptionFormat;// = " It has description '{0}'.";
private string EditIndex;// = " You may change index(es) on the selected tree item either by clicking on a node with the left mouse button or by pressing Alt-I.";
private string PleaseSelectCorrectActivityProperty;// = "Select a property of type '{0}'. Currently selected property of type \"{1}\" isn't assignable to the target type.";
private string PleaseSelectActivityProperty;// = "Select a property of type \"{0}\" on an activity from the workflow activity tree.";
private string IncorrectIndexChange;// = "New index expression \"{0}\" is incorrect.";
private string CreateNewMemberHelpFormat;// = "Enter new member name you want to be created on the root activity for property promotion, then choose the kind of member between either a field or a property.\nNew member will be of type '{0}'.";
System.Windows.Forms.ImageList memberTypes = null;
List properties;
public ActivityBindForm(IServiceProvider serviceProvider, ITypeDescriptorContext context)
{
this.context = context;
this.serviceProvider = serviceProvider;
InitializeComponent();
this.createProperty.Checked = true;//make the property to be the default emitted entity
this.helpTextBox.Multiline = true;
//Set dialog fonts
IUIService uisvc = (IUIService)this.serviceProvider.GetService(typeof(IUIService));
if (uisvc != null)
this.Font = (Font)uisvc.Styles["DialogFont"];
//add images to the tree-view's imagelist
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ActivityBindForm));
ActivityBindDialogTitleFormat = resources.GetString("ActivityBindDialogTitleFormat");
PropertyAssignableFormat = resources.GetString("PropertyAssignableFormat");
DescriptionFormat = resources.GetString("DescriptionFormat");
EditIndex = resources.GetString("EditIndex");
PleaseSelectCorrectActivityProperty = resources.GetString("PleaseSelectCorrectActivityProperty");
PleaseSelectActivityProperty = resources.GetString("PleaseSelectActivityProperty");
IncorrectIndexChange = resources.GetString("IncorrectIndexChange");
CreateNewMemberHelpFormat = resources.GetString("CreateNewMemberHelpFormat");
this.memberTypes = new System.Windows.Forms.ImageList();
this.memberTypes.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("memberTypes.ImageStream")));
this.memberTypes.TransparentColor = AmbientTheme.TransparentColor;
//this.memberTypes.Images.SetKeyName(0, "Field_Public");
//this.memberTypes.Images.SetKeyName(1, "Field_Internal");
//this.memberTypes.Images.SetKeyName(2, "Field_Protected");
//this.memberTypes.Images.SetKeyName(3, "Field_Private");
//this.memberTypes.Images.SetKeyName(4, "Property_Public");
//this.memberTypes.Images.SetKeyName(5, "Property_Internal");
//this.memberTypes.Images.SetKeyName(6, "Property_Protected");
//this.memberTypes.Images.SetKeyName(7, "Property_Private");
//this.memberTypes.Images.SetKeyName(8, "Constant_Public");
//this.memberTypes.Images.SetKeyName(9, "Constant_Internal");
//this.memberTypes.Images.SetKeyName(10, "Constant_Protected");
//this.memberTypes.Images.SetKeyName(11, "Constant_Private");
//this.memberTypes.Images.SetKeyName(12, "Event_Public");
//this.memberTypes.Images.SetKeyName(13, "Event_Internal");
//this.memberTypes.Images.SetKeyName(14, "Event_Protected");
//this.memberTypes.Images.SetKeyName(15, "Event_Private");
//this.memberTypes.Images.SetKeyName(16, "Delegate_Public");
//this.memberTypes.Images.SetKeyName(17, "Delegate_Internal");
//this.memberTypes.Images.SetKeyName(18, "Delegate_Protected");
//this.memberTypes.Images.SetKeyName(19, "Delegate_Private");
//this.memberTypes.Images.SetKeyName(20, "Index_Public");
//this.memberTypes.Images.SetKeyName(21, "Index_Internal");
//this.memberTypes.Images.SetKeyName(22, "Index_Protected");
//this.memberTypes.Images.SetKeyName(23, "Index_Private");
//preload custom properties before getting type from the type provider (as it would refresh the types)
this.properties = CustomActivityDesignerHelper.GetCustomProperties(context);
}
#region return properties
public ActivityBind Binding
{
get
{
return this.binding;
}
}
public bool CreateNew
{
get
{
return this.createNew;
}
}
public bool CreateNewProperty
{
get
{
return this.createNewProperty;
}
}
public string NewMemberName
{
get
{
return this.newMemberName;
}
}
#endregion
private void ActivityBindForm_Load(object sender, EventArgs e)
{
this.Text = string.Format(CultureInfo.CurrentCulture, ActivityBindDialogTitleFormat, context.PropertyDescriptor.Name);
if (this.context.PropertyDescriptor is DynamicPropertyDescriptor)
this.boundType = PropertyDescriptorUtils.GetBaseType(this.context.PropertyDescriptor, PropertyDescriptorUtils.GetComponent(context), serviceProvider);
if (this.boundType != null)
{
//lets get the same type through the type provider (otherwise this type may mismatch with the one obtained from the design time types)
ITypeProvider typeProvider = this.serviceProvider.GetService(typeof(ITypeProvider)) as ITypeProvider;
if (typeProvider != null)
{
Type designTimeType = typeProvider.GetType(this.boundType.FullName, false);
this.boundType = (designTimeType != null) ? designTimeType : this.boundType;
}
}
//create outline control
this.workflowOutline = new ActivityBindFormWorkflowOutline(this.serviceProvider, this);
this.dummyPanel.BorderStyle = BorderStyle.None;
this.dummyPanel.SuspendLayout();
this.dummyPanel.Controls.Add(this.workflowOutline);
this.workflowOutline.Location = new Point(3, 3);
this.workflowOutline.Size = new Size(199, 351);
this.workflowOutline.Dock = DockStyle.Fill;
this.dummyPanel.ResumeLayout(false);
this.workflowOutline.AddMemberKindImages(this.memberTypes);
//make the outline view load initial state
this.workflowOutline.ReloadWorkflowOutline();
//expand just the root node
this.workflowOutline.ExpandRootNode();
//now we need to select the activity/path which was previously set
//NOTE: we would have to expand all nodes on the way to make doc outline control populate their children
Activity activity = PropertyDescriptorUtils.GetComponent(context) as Activity;
if (activity == null)
{
IReferenceService rs = this.context.GetService(typeof(IReferenceService)) as IReferenceService;
if (rs != null)
activity = rs.GetComponent(this.context.Instance) as Activity;
}
ActivityBind previousBinding = context.PropertyDescriptor.GetValue(context.Instance) as ActivityBind;
if (activity != null && previousBinding != null)
{
Activity previousBindActivity = Helpers.ParseActivity(Helpers.GetRootActivity(activity), previousBinding.Name);
if (previousBindActivity != null)
this.workflowOutline.SelectActivity(previousBindActivity, ParseStringPath(GetActivityType(previousBindActivity), previousBinding.Path));
}
if (this.properties != null)
{
List customPropertyNames = new List();
foreach (CustomProperty customProperty in this.properties)
customPropertyNames.Add(customProperty.Name);
// set default name
this.memberNameTextBox.Text = DesignerHelpers.GenerateUniqueIdentifier(this.serviceProvider, activity.Name + "_" + context.PropertyDescriptor.Name, customPropertyNames.ToArray());
}
this.newMemberHelpTextBox.Lines = string.Format(CultureInfo.CurrentCulture, CreateNewMemberHelpFormat, GetSimpleTypeFullName(this.boundType)).Split(new char[] { '\n' });
}
private void OKButton_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.None;
this.createNew = (this.bindTabControl.SelectedIndex != this.bindTabControl.TabPages.IndexOf(this.existingMemberPage));
if (this.createNew)
{
//
this.createNewProperty = this.createProperty.Checked;
this.newMemberName = this.memberNameTextBox.Text;
//validate name based on the prop promotion dialog
this.DialogResult = ValidateNewMemberBind(this.newMemberName);
}
else
{
this.DialogResult = ValidateExistingPropertyBind();
}
}
private DialogResult ValidateExistingPropertyBind()
{
Activity activity = this.workflowOutline.SelectedActivity;
PathInfo member = this.workflowOutline.SelectedMember;
string propertyPath = this.workflowOutline.PropertyPath;//the path on the PathInfo will be incorrect if user had changed indexes
if (activity == null || member == null)
{
string message = SR.GetString(SR.Error_BindDialogNoValidPropertySelected, GetSimpleTypeFullName(this.boundType));
DesignerHelpers.ShowError(this.serviceProvider, message);
return DialogResult.None;
}
Type parsedPropertyType = member.PropertyType;
//lets get the same type through the type provider (otherwise this type may mismatch with the one obtained from the design time types)
ITypeProvider typeProvider = this.serviceProvider.GetService(typeof(ITypeProvider)) as ITypeProvider;
if (typeProvider != null && parsedPropertyType != null)
{
Type designTimeParsedType = typeProvider.GetType(parsedPropertyType.FullName, false);
parsedPropertyType = (designTimeParsedType != null) ? designTimeParsedType : parsedPropertyType;
}
if (this.boundType != parsedPropertyType && !TypeProvider.IsAssignable(this.boundType, parsedPropertyType))
{
string message = SR.GetString(SR.Error_BindDialogWrongPropertyType, GetSimpleTypeFullName(parsedPropertyType), GetSimpleTypeFullName(this.boundType));
DesignerHelpers.ShowError(this.serviceProvider, message);
return DialogResult.None;
}
//this is the selected activity which property is being bound
Activity bindingActivity = PropertyDescriptorUtils.GetComponent(this.context) as Activity;
//this is the name of the property we are binding
string propertyName = context.PropertyDescriptor.Name;
if (bindingActivity == activity && member != null && member.Path.Equals(propertyName, StringComparison.Ordinal))
{
DesignerHelpers.ShowError(this.serviceProvider, SR.GetString(SR.Error_BindDialogCanNotBindToItself));
return DialogResult.None;
}
if (activity != null && member != null)
{
//
ActivityBind bind = new ActivityBind(activity.QualifiedName, propertyPath);
ValidationManager manager = new ValidationManager(this.serviceProvider);
PropertyValidationContext propertyValidationContext = new PropertyValidationContext(this.context.Instance, DependencyProperty.FromName(this.context.PropertyDescriptor.Name, this.context.Instance.GetType()));
manager.Context.Append(this.context.Instance);//
ValidationErrorCollection errors;
using (WorkflowCompilationContext.CreateScope(manager))
{
errors = ValidationHelpers.ValidateProperty(manager, bindingActivity, bind, propertyValidationContext);
}
if (errors != null && errors.Count > 0 && errors.HasErrors)
{
string message = string.Empty;
for (int i = 0; i < errors.Count; i++)
{
ValidationError error = errors[i];
message += error.ErrorText + ((i == errors.Count - 1) ? string.Empty : "; ");
}
message = SR.GetString(SR.Error_BindDialogBindNotValid) + message;
DesignerHelpers.ShowError(this.serviceProvider, message);
return DialogResult.None;
}
else
{
this.binding = bind;
return DialogResult.OK;
}
}
return DialogResult.None;
}
[SuppressMessage("Microsoft.Globalization", "CA130:UseOrdinalStringComparison", MessageId="System.String.Compare(System.String,System.String,System.Boolean,System.Globalization.CultureInfo)", Justification="This is a design time method and so there is no security issue")]
private DialogResult ValidateNewMemberBind(string newMemberName)
{
Activity activity = PropertyDescriptorUtils.GetComponent(context) as Activity;
if (activity == null)
{
IReferenceService rs = this.context.GetService(typeof(IReferenceService)) as IReferenceService;
if (rs != null)
activity = rs.GetComponent(this.context.Instance) as Activity;
}
string errorMsg = null;
try
{
ValidationHelpers.ValidateIdentifier(context, newMemberName);
}
catch
{
errorMsg = SR.GetString(SR.Error_InvalidLanguageIdentifier, newMemberName);
}
// get all the members of the custom activity to ensure uniqueness
Type customActivityType = CustomActivityDesignerHelper.GetCustomActivityType(context);
SupportedLanguages language = CompilerHelpers.GetSupportedLanguage(context);
foreach (MemberInfo memberInfo in customActivityType.GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static))
{
if (string.Compare(memberInfo.Name, newMemberName, language == SupportedLanguages.VB, CultureInfo.InvariantCulture) == 0)
{
errorMsg = SR.GetString(SR.Failure_FieldAlreadyExist);
break;
}
}
// ctor name should be checked separately
if (errorMsg == null && string.Compare(customActivityType.Name, newMemberName, language == SupportedLanguages.VB, CultureInfo.InvariantCulture) == 0)
errorMsg = SR.GetString(SR.Failure_FieldAlreadyExist);
if (errorMsg == null)
{
ActivityBind newBind = new ActivityBind(ActivityBind.GetRelativePathExpression(Helpers.GetRootActivity(activity), activity), newMemberName);
IDesignerHost host = this.context.GetService(typeof(IDesignerHost)) as IDesignerHost;
if (host == null)
throw new InvalidOperationException(SR.GetString(SR.General_MissingService, typeof(IDesignerHost).FullName));
this.binding = newBind;
}
else
{
DesignerHelpers.ShowError(context, errorMsg);
}
return ((errorMsg == null) ? DialogResult.OK : DialogResult.None);
}
private void cancelButton_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Cancel;
}
private void SelectedActivityChanged(Activity activity, PathInfo memberPathInfo, string path)
{
string helpMessage = string.Empty;
string desiredType = GetSimpleTypeFullName(this.boundType);
if (memberPathInfo != null)
{
if (path == null || path.Length == 0)
{
helpMessage = string.Format(CultureInfo.CurrentCulture, PleaseSelectActivityProperty, desiredType);
}
else
{
string memberName = MemberActivityBindTreeNode.MemberName(memberPathInfo.Path);
string memberType = GetSimpleTypeFullName(memberPathInfo.PropertyType);
string memberDescription = GetMemberDescription(memberPathInfo.MemberInfo);
if (TypeProvider.IsAssignable(this.boundType, memberPathInfo.PropertyType))
helpMessage = string.Format(CultureInfo.CurrentCulture, PropertyAssignableFormat, memberType, desiredType) + ((memberDescription.Length > 0) ? string.Format(CultureInfo.CurrentCulture, DescriptionFormat, memberDescription) : string.Empty);
else
helpMessage = string.Format(CultureInfo.CurrentCulture, PleaseSelectCorrectActivityProperty, desiredType, memberType);
helpMessage += ((MemberActivityBindTreeNode.MemberName(path).IndexOfAny(new char[] { '[', ']' }) != -1) ? EditIndex : string.Empty);
}
}
else
{
helpMessage = string.Format(CultureInfo.CurrentCulture, PleaseSelectActivityProperty, desiredType);
}
this.helpTextBox.Lines = helpMessage.Split(new char[]{'\n'});
}
private List PopulateAutoCompleteList(Activity activity, PathInfo path)
{
List currentPropertyList = new List();
Type activityType = GetActivityType(activity);
PathInfo[] subProps = (activityType != null) ? ProcessPaths(activityType, path) : null;
if (subProps != null)
currentPropertyList.AddRange(subProps);
return currentPropertyList;
}
private Type GetActivityType(Activity activity)
{
Type activityType = null;
IDesignerHost designerHost = this.serviceProvider.GetService(typeof(IDesignerHost)) as IDesignerHost;
WorkflowDesignerLoader loader = this.serviceProvider.GetService(typeof(WorkflowDesignerLoader)) as WorkflowDesignerLoader;
if (designerHost != null && loader != null && activity.Parent == null)
{
ITypeProvider typeProvider = this.serviceProvider.GetService(typeof(ITypeProvider)) as ITypeProvider;
if (typeProvider != null)
activityType = typeProvider.GetType(designerHost.RootComponentClassName, false);
}
if (activityType == null)
activityType = activity.GetType();
return activityType;
}
#region help - related
private void ActivityBindForm_HelpButtonClicked(object sender, CancelEventArgs e)
{
e.Cancel = true;
GetHelp();
}
protected override void OnHelpRequested(HelpEventArgs e)
{
e.Handled = true;
GetHelp();
}
private void GetHelp()
{
DesignerHelpers.ShowHelpFromKeyword(this.serviceProvider, typeof(ActivityBindForm).FullName + ".UI");
}
#endregion
//given activity and current path, process all immediate children properties of the selected property
private PathInfo[] ProcessPaths(Type activityType, PathInfo topProperty)
{
List paths = new List();
if (topProperty == null)
{
paths.AddRange(GetSubPropertiesOnType(activityType, string.Empty));
}
else //topProperty != null
{
//sub properties on activity properties
paths.AddRange(GetSubPropertiesOnType(topProperty.PropertyType, topProperty.Path));
}
return paths.ToArray();
}
private PathInfo[] GetArraySubProperties(Type propertyType, string currentPath)//(PathInfo pathInfo)
{
List paths = new List();
if (propertyType != typeof(string))//ignore char item[int] on the string
{
List getterMethodInfos = new List();
MemberInfo[] arrayMembers = null;
try
{
arrayMembers = propertyType.GetDefaultMembers();
}
catch (NotImplementedException)
{
//Even if we encounted a RTTTypeWrapper that doesnt implement GetDefaultMemebers dont crash.
//we should atleast be able to continue to bind to other members of the type.
}
catch (ArgumentException)
{
// This is a work-around for DevDiv Bugs 109401. Type.GetDefaultMembers() can throw
// ArgumentException in certain circumstances. In order to avoid crashing the designer host
// (typically VS), we must handle the exception and ignore the offending type.
}
if (arrayMembers != null && arrayMembers.Length > 0)
{
foreach (MemberInfo member in arrayMembers)
{
if (member is PropertyInfo)
getterMethodInfos.Add((member as PropertyInfo).GetGetMethod());
}
}
if (propertyType.IsArray)
{
MemberInfo[] getMembers = propertyType.GetMember("Get");//arrays will always implement that
if (getMembers != null && getMembers.Length > 0)
{
foreach (MemberInfo member in getMembers)
if (member is MethodInfo)
getterMethodInfos.Add(member as MethodInfo);
}
}
foreach (MethodInfo info in getterMethodInfos)
{
string indexString = ConstructIndexString(info);
if (indexString != null)
{
//add array accessor
paths.Add(new PathInfo(currentPath + indexString, info, info.ReturnType));
}
}
}
return paths.ToArray();
}
private string ConstructIndexString(MethodInfo getterMethod)
{
StringBuilder indexString = new StringBuilder();
ParameterInfo[] parameters = getterMethod.GetParameters();
if (parameters != null && parameters.Length > 0)
{
indexString.Append("[");
for (int i = 0; i < parameters.Length; i++)
{
ParameterInfo parameter = parameters[i];
string subIndex = GetIndexerString(parameter.ParameterType);
if (subIndex == null)
return null;
indexString.Append(subIndex);
if (i < parameters.Length - 1)
indexString.Append(",");
}
indexString.Append("]");
}
return indexString.ToString();
}
private string GetIndexerString(Type indexType)
{
//The primitive types are Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Char, Double, and Single.
object defaultIndexerInstance = null;
if (IsTypePrimitive(indexType))
{
try
{
//we'll just new the instance and get the string value out of the default one
defaultIndexerInstance = Activator.CreateInstance(indexType);
}
catch
{
defaultIndexerInstance = null;
}
}
else if (indexType == typeof(string))
{
defaultIndexerInstance = "\"\"";
}
return (defaultIndexerInstance != null) ? defaultIndexerInstance.ToString() : null;
}
PropertyInfo[] GetProperties(Type type)
{
List members = new List();
members.AddRange(type.GetProperties(BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance | BindingFlags.FlattenHierarchy));
if (type.IsInterface)
{
Type[] interfaces = type.GetInterfaces();
foreach (Type implementedInterface in interfaces)
{
members.AddRange(implementedInterface.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy));
}
}
return members.ToArray();
}
//quite expensive - uses reflection to go over all public properties/field/events
private PathInfo[] GetSubPropertiesOnType(Type typeToGetPropertiesOn, string currentPath)
{
List paths = new List();
if (typeToGetPropertiesOn == typeof(string) || (TypeProvider.IsAssignable(typeof(System.Delegate), typeToGetPropertiesOn) && !this.boundType.IsSubclassOf(typeof(Delegate))))//ignore char item[int] on the string
return paths.ToArray();
currentPath = (string.IsNullOrEmpty(currentPath)) ? string.Empty : currentPath + ".";
ITypeProvider typeProvider = this.serviceProvider.GetService(typeof(ITypeProvider)) as ITypeProvider;
foreach (PropertyInfo property in GetProperties(typeToGetPropertiesOn))
{
MethodInfo getterMethod = property.GetGetMethod();
Type memberType = BindHelpers.GetMemberType(property);
if (memberType == null)
continue;
if (typeProvider != null)
{
Type designTimeMemberType = typeProvider.GetType(memberType.FullName, false);
memberType = (designTimeMemberType != null) ? designTimeMemberType : memberType;
}
//if (memberType == typeof(WorkflowParameterBindingCollection) && string.IsNullOrEmpty(currentPath))
//{
// //special case for the parameters collection on an activity itself (when path is empty)
// Activity activity = this.workflowOutline.SelectedActivity;
// if(getterMethod != null && typeToGetPropertiesOn == activity.GetType())
// {
// WorkflowParameterBindingCollection collection = getterMethod.Invoke(activity, null) as WorkflowParameterBindingCollection;
// if (collection != null)
// {
// foreach (WorkflowParameterBinding parameterBinding in collection)
// {
// //note that the currentPath is always empty
// paths.Add(new PathInfo(property.Name + "[\"" + parameterBinding.ParameterName + "\"].Value", typeof(object)));
// }
// }
// }
//}
//if it's a primitive and not equal to the desired type, skip it.
//skip properties of type object if the target property is not object
if (IsPropertyBrowsable(property) &&
getterMethod != null && memberType != null &&
(!IsTypePrimitive(memberType) || TypeProvider.IsAssignable(this.boundType, memberType)) &&
!((this.boundType != typeof(object) && memberType == typeof(object))))
{
//some properties are indexers... analyze the parameters on the getter method
// C#: at design time indexer property is called "this" while at runtime it gets renamed to "Item"
// VB: indexer is called Item at design and runtime .
string propertyName = property.Name;
propertyName = currentPath + propertyName + ConstructIndexString(getterMethod);
paths.Add(new PathInfo(propertyName, property, memberType));
paths.AddRange(GetArraySubProperties(memberType, propertyName));
}
}
//
foreach (FieldInfo field in typeToGetPropertiesOn.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy))//BindingFlags.Static is needed for the const fields
{
Type fieldType = BindHelpers.GetMemberType(field);
if (fieldType == null)
continue;
if (TypeProvider.IsAssignable(typeof(DependencyProperty), fieldType))
continue;//dont want to show all static public dependency properties fields
if (typeProvider != null)
{
Type designTimeFieldType = typeProvider.GetType(fieldType.FullName, false);
fieldType = (designTimeFieldType != null) ? designTimeFieldType : fieldType;
}
//if it's a primitive and not equal to the desired type, skip it.
//
if (IsPropertyBrowsable(field) && fieldType != null &&
(!IsTypePrimitive(fieldType) || TypeProvider.IsAssignable(this.boundType, fieldType)) && //primitive fields should only be shown for primitive properties
!(this.boundType != typeof(object) && fieldType == typeof(object)) && //fields of type object should only be shown for properties of type object
!(!TypeProvider.IsAssignable(typeof(Delegate), this.boundType) && TypeProvider.IsAssignable(typeof(Delegate), fieldType)))//fields of type delegate should only be shown for delegate properties
{
string fieldName = currentPath + field.Name;
paths.Add(new PathInfo(fieldName, field, BindHelpers.GetMemberType(field)));
paths.AddRange(GetArraySubProperties(fieldType, fieldName));
}
}
//we will populate events only if the target type is event (since it is always going to be the last valid entry in the path)
if (this.boundType.IsSubclassOf(typeof(Delegate)))//System.MulticastDelegate ???
{
foreach (EventInfo eventInfo in typeToGetPropertiesOn.GetEvents(BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy))
{
Type eventType = BindHelpers.GetMemberType(eventInfo);
if (eventType == null)
continue;
if (typeProvider != null)
{
Type designTimeEventType = typeProvider.GetType(eventType.FullName, false);
eventType = (designTimeEventType != null) ? designTimeEventType : eventType;
}
if (IsPropertyBrowsable(eventInfo) && eventType != null && TypeProvider.IsAssignable(this.boundType, eventType))
paths.Add(new PathInfo(currentPath + eventInfo.Name, eventInfo, eventType));
}
}
return paths.ToArray();
}
private string GetMemberDescription(MemberInfo member)
{
object[] descriptions = member.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (descriptions != null && descriptions.Length > 0)
{
DescriptionAttribute description = descriptions[0] as DescriptionAttribute;
return (description != null) ? description.Description : string.Empty;
}
return string.Empty;
}
//given user typed path, find all properties along it and return them in the list
private List ParseStringPath(Type activityType, string path)
{
if (string.IsNullOrEmpty(path))
return null;
List pathInfoList = new List();
PathWalker pathWalker = new PathWalker();
PathMemberInfoEventArgs finalEventArgs = null;
PathErrorInfoEventArgs errorEventArgs = null;
pathWalker.MemberFound += delegate(object sender, PathMemberInfoEventArgs eventArgs)
{
finalEventArgs = eventArgs; //store the latest args
pathInfoList.Add(new PathInfo(eventArgs.Path, eventArgs.MemberInfo, BindHelpers.GetMemberType(eventArgs.MemberInfo)));
};
pathWalker.PathErrorFound += delegate(object sender, PathErrorInfoEventArgs eventArgs)
{
errorEventArgs = eventArgs; //store the error args
};
pathWalker.TryWalkPropertyPath(activityType, path);
return pathInfoList;
}
private bool IsPropertyBrowsable(MemberInfo property)
{
object[] attributes = property.GetCustomAttributes(typeof(BrowsableAttribute), false);
if (attributes.Length > 0)
{
BrowsableAttribute attribute = attributes[0] as BrowsableAttribute;
if (attribute != null)
return attribute.Browsable;
else
{
AttributeInfoAttribute attributeInfoAttribute = attributes[0] as AttributeInfoAttribute;
if (attributeInfoAttribute != null)
{
ReadOnlyCollection