The June 2010 release of the Windows Azure Tools + SDK will include:
- Full support for Visual Studio 2010 RTM
- .NET 4 support to provide developers with the flexibility to build services targeting either the .NET 3.5 or .NET 4 framework
- Cloud storage explorer to make it easier for developers to build compelling services by displaying a read-only view of Windows Azure tables and blob containers through the Visual Studio Server Explorer
- Integrated deployment, which will enable developers to deploy services directly from Visual Studio by selecting 'Publish' from the Solution Explorer
- Service monitoring to help developers track and manage the state of their services through the 'compute' node in Server Explorer
- IntelliTrace support for services running in the cloud to simplify the process of debugging services in the cloud through the Visual Studio 2010 Ultimate IntelliTrace feature. For more information see this post.
To download the new Tools + SDK, please click here.
Almost every activity has arguments. The workflow designer presents only the root activity's arguments (these are the "Workflow Arguments")
The activity's arguments has to be binded to ExpressionTextbox placed on the activity canvas (i.e. The Activity designer).
When your activity is static and is completely defined in xaml there is no problem to define the binding in the xaml definition of the activity designer (there are tons of examples).The question is what happens when arguments and ExpressionTextBoxes are built in code?
How can we bind dynamically created arguments with dynamically created ExpressionTextBox?
Dynamically created arguments should be placed in an Arguments collection. I like to use a dictionary so I can refer to it later by name (the key of the dictionary).
The arguments are crated in the CacheMetadata method.
string[] MyArgumentsNames = { "arg1,arg2" };
public Dictionary<string,Argument> Arguments { get; set; }
protected override void CacheMetadata(CodeActivityMetadata metadata)
{
base.CacheMetadata(metadata);
Collection<RuntimeArgument> arguments = new Collection<RuntimeArgument>();
foreach (var argumentName in MyArgumentsNames)
{
//Create a new input argument of type string
RuntimeArgument rtArgument =
new RuntimeArgument("MyArgumentName",
typeof(string), ArgumentDirection.In);
Arguements.Add(argumentName,
Argument.Create(typeof(string), ArgumentDirection.In));
metadata.Bind(Arguments[argumentName], rtArgument);
arguments.Add(rtArgument);
}
//Set the arguments collection that has been built as part of the Activity's metadata
metadata.SetArgumentsCollection(arguments);
}
After the argument is created it is time to bind it to a dynamically created ExpressionBox.
I wrote a little method that does exactly that. I assume the activity designer is no more than a simple grid with two columns.
public void DrawExpressionBoxOnCanvas(Grid activityGrid,
string argumentName,Type argumentType,
string direction, ModelItem ownerActivity)
{
//Create a new row in the activity grid
int rowIndex = activityGrid.RowDefinitions.Count;
var newRow = new RowDefinition();
activityGrid.RowDefinitions.Insert(rowIndex, newRow);
//Create a text block on the grid with the argument name
var txtBlock = new TextBlock();
txtBlock.Uid = "txtBlock" + argumentName;
txtBlock.Text = argumentName;
txtBlock.Margin =
new Thickness() { Bottom = 5, Top = 5, Left = 5, Right = 5 };
Grid.SetColumn(txtBlock, 0);
Grid.SetRow(txtBlock, rowIndex);
activityGrid.Children.Add(txtBlock);
//Create a new ExpressionTextBox on the activity grid
var expressionTbx = new ExpressionTextBox();
expressionTbx.Uid = "ExpressionBox" + argumentName;
expressionTbx.HintText = String.Format("Enter {0}", argumentName);
expressionTbx.ExpressionType = argumentType;
expressionTbx.Margin =
new Thickness() { Bottom = 5, Top = 5, Left = 5, Right = 5 };
expressionTbx.Width = 110;
expressionTbx.OwnerActivity = ownerActivity;
// Create binding between the expression block and the activity's arguments
var bind = new Binding();
bind.Mode = BindingMode.TwoWay;
bind.Converter = new ArgumentToExpressionConverter();
bind.ConverterParameter = direction;
bind.Path = new PropertyPath(
String.Format("ModelItem.Arguments[{0}]", argumentName));
//Out arguments require L-Value expression
if (direction == "out")
expressionTbx.UseLocationExpression = true;
//Set the binding and Add the expression block to the grid
expressionTbx.SetBinding(ExpressionTextBox.ExpressionProperty, bind);
Grid.SetRow(expressionTbx, rowIndex);
Grid.SetColumn(expressionTbx, 1);
activityGrid.Children.Add(expressionTbx);
}
Hope this will help
Manu
In Workflow 4.0 it is easy to return values. All you have to do is create a set of out arguments and provide the arguments with values. The values will be available in a dictionary in the WorkflowApplicationCompletedEventArgs parameter of the Completed handler.
There is one simple step that is often being forgotten, that is to create an instance of the value to return.
If the OutArgument is of type string (or other primitive) all you have to do is set the string value to the argument. But if the argument is of a custom type you have to create an instance of this type as a variable (in the relevant scope) and set this variable to the OutArgument.
When thinking of it this is exactly what we do in code but when working in the designer we would expect the designer to automatically create the instance of the OutArgument for us. Unfortunately this is not the case.
Manu