In previous posts we saw how to bind our designer properties to our activity and use VB.NET syntax for more complex values. In this post let’s see how to show variables and work with namespaces.

Importing Namespaces

If you want to use certain types in your Xaml, you need to import necessary namespaces, as an example our List<T> type is in Sysem.Collections.Generic namespace which needs to be imported for a valid WF Xaml. Let’s see how this is done. When creating the WorkflowDesigner control, you initially need to create an empty workflow, like an empty Sequence or Flowchart and feed it to the Load method of the WorkflowDesigner:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private void CreateDesigner()
{
designer = new WorkflowDesigner();
designer.Load(CreateDefaultSequence());
designer.Flush();
}

public Sequence CreateDefaultSequence()
{
var seq = new Sequence {DisplayName = "Empty Sequence"};
var settings = new VisualBasicSettings();
var references = new VisualBasicImportReference {Assembly = "System", Import = "System.Collections.Generic"};

settings.ImportReferences.Add(x));
VisualBasic.SetSettings(seq, settings);

return seq;
}

Although user can import necessary namespaces using the designer, it’d be ideal to automatically import mostly used namespaces upon designer startup.

Load method of designer accepts “Object” which is ambiguous as the only type of objects I managed to feed it was ActivityBuilder, Sequence and Flowchart.

Displaying Variables

Sometimes you need to allow user to select an existing variable name and bind it to a property of your Activity. How do you do that? Remember the ModelItem property on your designer? The ModelItem property is hierarchical so traversing this you can gain access to the root element of your workflow (here we use a SequenceActivity) where defined variables can be found. ModelItem also has information about the type of the property so you can just select properties of certain type. The following extension method finds all available variables, regardless of their types:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public static IEnumerable<ModelItem> FindAllVariables(this ModelItem selectedItem)
{
var variables = new List<ModelItem>();
while (selectedItem.Parent != null)
{
Type parentType = selectedItem.Parent.ItemType;
if (typeof(System.Activities.Activity).IsAssignableFrom(parentType))
{
var mp = selectedItem.Parent.Properties["Variables"];
if (mp != null && mp.Collection != null && mp.PropertyType == typeof(Collection<Variable>))
{
mp.Collection.ToList().ForEach(variables.Add);
}
}

var dels = selectedItem.Properties.Where(p => typeof(ActivityDelegate).IsAssignableFrom(p.PropertyType));
foreach (var actdel in dels)
{
if (actdel.Value != null)
{
foreach (var innerProp in actdel.Value.Properties)
{
if (typeof(DelegateArgument).IsAssignableFrom(innerProp.PropertyType) && null != innerProp.Value)
{
variables.Add(innerProp.Value);
}
}
}
}
selectedItem = selectedItem.Parent;
}

return variables;
}

Now to make this easier, let’s think what is it that we want to select? We need the name of the variable, along with related ModelItem information, so let’s create a Model class to contain this information:

1
2
3
4
5
6
7
8
9
10
11
12
public class VariableItem : Observable
{
public VariableItem(string name, ModelItem variable)
{
Name = name;
Variable = variable;
}

public string Name { get; set; }

public ModelItem Variable { get; set; }
}

It is easier now to bind a ComboBox to a list of VariableItem instances. All we need to do is to get all the variable of certain types (or all the variables, regardless of their types) as a list of VariableItems and bind it in the designer. This extension method will do the trick for you and has both generic and non-generic flavors:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static IEnumerable<VariableItem> FindVariableOfType(this ModelItem selectedItem, Type typeToFind)
{
var variables = FindAllVariables(selectedItem);
var found = new Dictionary<string, ModelItem>();

variables.ForEach(x =>
{
var name = x.Properties.GetValue<string>("Name");
var type = x.Properties.GetValue<Type>("Type");

if (type == typeToFind)
{
found.Add(name, x);
}
});

return found.Select(x => new VariableItem(x.Key, x.Value));
}

public static IEnumerable<VariableItem> FindVariableOfType<T>(this ModelItem selectedItem)
{
return FindVariableOfType(selectedItem, typeof (T));
}

Notice ModelItem has information about the variable, like name of the variable and the underlying type of the variable. In next post we’ll see how to use complex objects in our activities and how to fill and serialize them to / from xaml from our custom designers.