The Switch activity I created is based on the switch statement in C# or the Select Case statements in VB.  You have an expression and one or more case statements, one of which can be the default.  To model within workflow the case becomes a property that must be on the children of the Switch activity, which you can do this a couple of ways.  One would be to have a Case activity with a Cases property that would be a required child.  Another would be to have an attached Cases property that would be added to all children of the Switch.  I chose to use an attached property because I think it gives a better user experience and it makes for a better sample since I don’t think there are that many out there for advance attached property scenarios.

 

When you use have an attached property it will not be displayed in the property browser until you create an IExtenderProvider and implement the CanExtend method like the following:

 

bool IExtenderProvider.CanExtend(object extendee)

{

    return (extendee is Activity) && (((Activity)extendee).Parent is Switch);

} 

 

Along with the CanExtend method you also need to implement methods used for getting and setting the value of the property.  The naming convention that must be followed is the “Get” / “Set” followed by the name of the property.  For the switch activity I have the following:

 

[TypeConverter(typeof(ActivityBindTypeConverter))]

[CategoryAttribute("Switch"), ]

      [DescriptionAttribute("If any of the values in Cases equals the Switch's Expression then this activity will be run.")]

public object GetCases(Activity theActivity)

{

    object value = null;

 

    if (theActivity.Parent is Switch)

    {

        if (theActivity.IsBindingSet(Switch.CasesProperty))

            value = theActivity.GetBinding(Switch.CasesProperty) as object;

        else

            value = theActivity.GetValue(Switch.CasesProperty) as object;

    }

 

    return value;

}

 

public void SetCases(Activity theActivity, object value)

{

    if (theActivity.Parent is Switch)

    {

        if (value is ActivityBind)

            theActivity.SetBinding(Switch.CasesProperty, value as ActivityBind);

        else

            theActivity.SetValue(Switch.CasesProperty, value);

    }

}

 

Notice that any attribute that you would normally place on the property you will be adding above the GetPropertyName method.  Once you have the IExtenderProvider you link it up with the activity in the designer’s Initialize method:

 

protected override void Initialize(Activity activity)

{

    base.Initialize(activity);

 

    IExtenderListService extenderListService = (IExtenderListService)GetService(typeof(IExtenderListService));

    if (extenderListService != null)

    {

        bool foundExtender = false;

        foreach (IExtenderProvider extenderProvider in extenderListService.GetExtenderProviders())

        {

            if (extenderProvider.GetType() == typeof(CasesExtenderProvider))

                foundExtender = true;

        }

 

        if (!foundExtender)

        {

            IExtenderProviderService extenderProviderService = (IExtenderProviderService)GetService(typeof(IExtenderProviderService));

            if (extenderProviderService != null)

                extenderProviderService.AddExtenderProvider(new CasesExtenderProvider());

        }

    }

}

 

With attached properties you are also able to include a validator:

 

public static DependencyProperty CasesProperty = DependencyProperty.RegisterAttached("Cases", typeof(object), typeof(Switch), new PropertyMetadata(null), typeof(CasesValidator));

 

The validator in this case is validating that the Cases property is only set on a direct child of a Switch:

 

public class CasesValidator : Validator

{

    public override ValidationErrorCollection  Validate(ValidationManager manager, object obj)

    {

        ValidationErrorCollection errors = base.Validate(manager, obj);

 

        Activity activity = manager.Context[typeof(Activity)] as Activity;

        if (activity != null)

        {

            Switch switchAct = activity.Parent as Switch;

            if (switchAct == null)

                errors.Add(new ValidationError("The Cases property can only be set on a direct child of a Switch activity.", 123, false, "Cases"));

        }

 

        return errors;

    }

}

 

Since you can bind the Cases and Expression properties the value might not be set until runtime so validation of the values should be done in the Switch activity’s Initialize method.  If either are set to invalid values an exception is thrown to the user.  The current implementation only allows the properties to be set to a primitive, string or enum value.