Restricting the types available in TypePresenter in WF designers
The TypePresenter control is the UI that the WF designer displays for selecting a type. It is often seen on generics based activities and the InvokeMethod activity.
This drop down list provides some common types and includes some of the types already found in the current activity context. Selecting “Browse for Types …” allows for all referenced types to be searched in a dialog.
Sometimes you don’t want the TypePresenter to provide every available type. The TypePresenter has a great feature that allows you to restrict the types it displays in this list and the associated “Browse for Types …” dialog. This is done by providing a Func<Type, Boolean> reference on the TypePresenter’s Filter property.
In my scenario, I want to restrict the types available to those that derive from System.Exception. The first step to achieve this is to make a reference to the filter method in the xaml of the activity designer.
<sapv:TypePresenter HorizontalAlignment="Left"
VerticalAlignment="Center"
Margin="6"
Grid.Row="0"
Grid.Column="1"
Filter="ExceptionTypeFilter"
AllowNull="false"
BrowseTypeDirectly="false"
Label="Exception type"
Type="{Binding Path=ModelItem.ExceptionType, Mode=TwoWay, Converter={StaticResource modelItemConverter}}"
Context="{Binding Context}" />
The code behind class of the designer must contain the method defined in the Filter property (ExceptionTypeFilter in this case). This method must take a Type parameter and return a Boolean in order to satisfy the Func<Type, Boolean> signature. The filter method related to the xaml above is the following.
public Boolean ExceptionTypeFilter(Type typeToValidate)
{
if (typeToValidate == null)
{
return false;
}
if (typeof(Exception).IsAssignableFrom(typeToValidate))
{
return true;
}
return false;
}
The designer for this activity will now only display exception types in the TypePresenter.
The associated “Browse for Types …” dialog will also use this filter value.
Unfortunately the property grid will still use the default TypePresenter implementation.
I haven’t figured out a way to change this behaviour and I suspect that it is not possible.
The final piece of the puzzle is to address what happens when the developer selects an inappropriate type using the property grid. This is where activity validation using CacheMetadata comes into play.
protected override void CacheMetadata(NativeActivityMetadata metadata)
{
metadata.AddDelegate(Body);
metadata.AddImplementationChild(_internalDelay);
metadata.AddImplementationVariable(_attemptCount);
metadata.AddImplementationVariable(_delayDuration);
RuntimeArgument maxAttemptsArgument = new RuntimeArgument("MaxAttempts", typeof(Int32), ArgumentDirection.In, true);
RuntimeArgument retryIntervalArgument = new RuntimeArgument("RetryInterval", typeof(TimeSpan), ArgumentDirection.In, true);
metadata.Bind(MaxAttempts, maxAttemptsArgument);
metadata.Bind(RetryInterval, retryIntervalArgument);
Collection<RuntimeArgument> arguments = new Collection<RuntimeArgument>
{
maxAttemptsArgument,
retryIntervalArgument
};
metadata.SetArgumentsCollection(arguments);
if (Body == null)
{
ValidationError validationError = new ValidationError(Resources.Retry_NoChildActivitiesDefined, true, "Body");
metadata.AddValidationError(validationError);
}
if (typeof(Exception).IsAssignableFrom(ExceptionType) == false)
{
ValidationError validationError = new ValidationError(Resources.Retry_InvalidExceptionType, false, "ExceptionType");
metadata.AddValidationError(validationError);
}
}
The validation at the end of this method checks for an inappropriate type. It is marked as an error so that the activity is not able to be executed in this state. For example, it the property grid is used to assign the type of System.String, the designer will display the following.
The workflow runtime will throw an InvalidWorkflowException if the activity is executed in this state.
We have seen here that we can restrict the types presented by TypePresenter and back this up with some activity validation.