Introduction
This article will be useful for those who are having trouble using custom editor to create and edit bindings in workflow activities. The tricky situation here is to use Designers instead of editor tags. Designer classes (used with attribute [Designer]
) make it possible we use any class to edit bindings, even if we do not want to use a screen.
Background
Recently we (my work team and I) faced a big problem when we were developing a functionality required by our client. The idea was to create a new window that would be used, instead of the one provided by Microsoft, to manage bindings in workflow activities, i.e., a CustomPropertyBindingEditor
useful to set new bindings and handle properties that already have a binding set to it.
The idea was quite simple considering that a lot of developers have achieved this task many times until that day. So let’s go! we thought. First try, nothing, second try, nothing, third, fourth, fifth, twentieth and then we thought: “Ok, it’s not as simple as we thought”.
The big problem is that it is impossible to use the same window (editor) to set a new binding and to edit an existent one because when a property does not have a binding set to it the type of the property is used to call the editor, but when a property has already a binding set to it the type used to call the editor is typeof(ActivityBind)
and not the property type, so the editor from Microsoft is called instead of the custom editor.
After that, we searched on the internet for everything that could help us. Yes, we found a lot of sites and forums; but full of questions about how to use
CustomPropertyBindingEditor
when a property has and when it does not have a binding set to it and no good answers. Just one helped me to work out how to do it and solve all our problems (at that time).
One very important thing that I want to mention is that our time requested support from Microsoft to help us solve this issue and after more than one month, they answered us saying that it is impossible to develop and even the team that developed the WWF did not manage to develop something to work around this issue.
Well, it is not impossible, I can assure you!
The Solution
The solution is not that easy as I and a lot of people wanted, but the problem is solved, that is important!
To start with, there are twelve classes that must be used in addition to the custom property binding editor that you may want to use instead of the one from Microsoft.
In the sample solution, there are two projects. The first one has in it the classes mentioned earlier, the custompropertybindingeditor
screen and one sample activity. The second project has a sample sequentialworkflow
to test the designer class.
The solution consist of use the [Designer]
and [TypeConverter]
attributes to indicate which classes and properties we want the custom property binding editor to be used.
The sample activity in the project looks like this:
namespace WFProjectCustomPropertyBindingEditor
{
[Designer( typeof( CustomActivityBindDesigner ), typeof( IDesigner ) )]
public partial class CustomPropertyBindingActivityTest: SequenceActivity
{
public CustomPropertyBindingActivityTest()
{
InitializeComponent();
}
public static DependencyProperty PropertyStringProperty =
DependencyProperty.Register( "PropertyString", typeof( string ),
typeof( CustomPropertyBindingActivityTest ) );
[Description( "Description" )]
[Category( "Category" )]
[Browsable( true )]
[DesignerSerializationVisibility( DesignerSerializationVisibility.Visible )]
[TypeConverter( typeof( CustomTypeConverter ) )]
public string PropertyString
{
get
{
return ( (string)( base.GetValue
( CustomPropertyBindingActivityTest.PropertyStringProperty ) ) );
}
set
{
base.SetValue( CustomPropertyBindingActivityTest.PropertyStringProperty,
value );
}
}
public static DependencyProperty PropertyListStringProperty =
DependencyProperty.Register( "PropertyListString",
typeof( List<string> ), typeof( CustomPropertyBindingActivityTest ) );
[Description( "Description" )
[Category( "Category" )]
[Browsable( true )]
[DesignerSerializationVisibility( DesignerSerializationVisibility.Visible )]
[TypeConverter( typeof( CustomTypeConverter ) )]
public List<string> PropertyListString
{
get
{
return ((List<string>)(base.GetValue
(CustomPropertyBindingActivityTest.PropertyListStringProperty ) ) );
}
set
{
base.SetValue
(CustomPropertyBindingActivityTest.PropertyListStringProperty, value );
}
}
public static DependencyProperty PropertyIntProperty =
DependencyProperty.Register( "PropertyInt", typeof( Int32 ),
typeof( CustomPropertyBindingActivityTest ) );
[DescriptionAttribute( "PropertyInt" )]
[CategoryAttribute( "PropertyInt Category" )]
[BrowsableAttribute( true )]
[DesignerSerializationVisibilityAttribute
( DesignerSerializationVisibility.Visible )]
[TypeConverter( typeof( CustomTypeConverter ) )]
public Int32 PropertyInt
{
get
{
return ( (Int32)( base.GetValue
( CustomPropertyBindingActivityTest.PropertyIntProperty ) ) );
}
set
{
base.SetValue
( CustomPropertyBindingActivityTest.PropertyIntProperty, value );
}
}
}
}
The attribute [Designer]
above the class declaration indicates that we want to use the CustomActivityBind
designer in this class and the attribute [TypeConverter]
above each one of the properties says to the CustomActivityBind
class which typeconverter
to use for this properties.
To summarize, if you want to use your custom editor, just include these two attributes in the class and property that you want, but it is possible also to include the [TypeConverter]
attribute in one property and keep others without this tag, this way the property with the attribute will always use your custom editor and the others the original screen.
I did not remove the [Editor]
tag from the properties to illustrate how it would be if workflow activities worked as Windows Forms. In Windows Forms, we use just the [Editor]
tag to say which editor I want to use to a determined property and this editor is always used, independently of whether it has a value or not.
The classes in the project and description of each one is given below.
WFProjectCustomBindingPropertyEditor Project
ComponentChangeDispatcher
- When a value is updated, this class updates the property assigning the new value ActivityBindNamePropertyDescriptor
- Property descriptor for property 'Name
' (used by TypeConverter
).
ActivityBindPathPropertyDescriptor
- Property descriptor for property 'Value
' (used by TypeConverter
)
ActivityBindPropertyDescriptor
- Property descriptor for the whole property (name, value and line of description)
CustomActivityBindDesigner
- In charge of setting the editor for the properties that we want to use our custom property binding editor CustomTypeConverter
- Converts the ActivityBind
to correct format, to be shown on PropertyGrid
DynamicPropertyDescriptor
- Inherits from PropertyDescriptor
, used to get
and set
values of properties FilteredPropertyGrid
- The purpose of this class is to force what type of editor the property descriptor will use to get and set value to a property Helpers
- Provides auxiliary functions to parse properties, get workflow activities and others PropertyDescriptorUtils
- Triggers the GetValue
and SetValue
functions of the propertydescriptor
class from the property being edited TypeDescritptorContext
- Used as a container, groups propertydescriptor
, serviceprovider
and the instance of the object that contains the property being edited CustomPropertyBindingActivity
- Sample activity, it has 3 properties declared in it to test functionality CustomPropertyBindingEditor
- Opens the custom property editor and returns the new value (activitybind
)
WFConsumer Project
Workflow1
- Sample workflow with one activity in it (CustomPropertyBindingActivity
) and 3 properties to bind to the properties from sample activity.
Sample Solution
The sample solution attached to this article was developed in Visual Studio 2008. To test the solution, just open the project. After the Studio has opened, open the file Workflow1
, this is the SequentialWorkflow
used to test the designer class.
Considerations
I want to thank all my colleagues for their support and consideration.
I want to give a special "thank you" to my mother. She is the one who always supports me when I have something difficult to do! And that day when I developed this solution, at 12PM she said: “I know you are going to find the solution, you always do!” and at 3AM I got it done and working fine.