I went for the trifecta and died on the finish line. :-( I started out by creating WinFX Windows application, and I must say that
having a visual designer for XAML is pretty cool. The designer view and the
underlying XAML markup looks like this:

<Window x:Class="WindowsApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
Title="WindowsApplication1" Closed="Window_Closed"
>
<Grid MinHeight="50" MinWidth="50" Name="grid1" >
<Grid.Resources>
<DataTemplate x:Key="CustomerTemplate">
<DockPanel>
<TextBlock Width="300" Text="{Binding Path=CustomerID}" />
<TextBlock Width="300" Text="{Binding Path=CompanyName}" />
<TextBlock Width="300" Text="{Binding Path=NumOrders}"/>
</DockPanel>
</DataTemplate>
</Grid.Resources>
<ListBox VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Grid.Column="0" Grid.ColumnSpan="1" Grid.Row="0" Grid.RowSpan="1" Margin="0,0,0,68" Width="NaN" Height="NaN" x:Name="listBoxCustomers" />
<Button VerticalAlignment="Bottom" HorizontalAlignment="Left" Grid.Column="0" Grid.ColumnSpan="1" Grid.Row="0" Grid.RowSpan="1" Margin="11,0,0,6" Width="75" Height="23" x:Name="buttonGetCustomers" Click="buttonGetCustomers_Click">Button</Button>
</Grid>
</Window>
In the code behind, I'm able to successfully start up my workflow. The
workflow runs a DLINQ query and the problem I'm running into is getting the
results back to the client - an exception is thrown saying, "The calling
thread may not access this object because the object is owned by a different
thread."
namespace WindowsApplication1 {
public partial class Window1 : Window, IDataReceiver {
private WorkflowRuntime wr;
public Window1() {
InitializeComponent();
}
public void buttonGetCustomers_Click(object sender, EventArgs e) {
if (wr == null) {
wr = new WorkflowRuntime();
wr.StartRuntime();
}
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("DataReceiver", this);
WorkflowInstance instance = wr.CreateWorkflow(typeof(LINQLibrary1.Workflow1), parameters);
instance.Start();
}
public void Process(object data) {
this.listBoxCustomers.DataContext = data;
//exception!!! "The calling
thread may not access this object because the object is owned by a different
thread."
}
public void Window_Closed(object sender, EventArgs e) {
if (wr != null) {
if (wr.IsStarted) {
wr.StopRuntime();
}
}
}
}
}
The problem I'm running to may be very simple to get around. Originally I was
trying to return the results of the DLINQ query (the var instance) by calling
ToList(). After getting the exception, I tried creating a custom class and
collection to populate the results, but no dice. This is probably because the
workflow code gets upset about the form trying to touch data in its memory
space. Here's the Workflow code...

public sealed partial class Workflow1 : SequentialWorkflowActivity {
private IDataReceiver _dataReceiver;
public Workflow1() {
InitializeComponent();
}
public IDataReceiver DataReceiver {
get { return _dataReceiver; }
set { _dataReceiver = value; }
}
private void codeActivity1_ExecuteCode(object sender, EventArgs e) {
using(SqlConnection connection = new SqlConnection("integrated security=sspi;initial catalog=northwind;data source=.\\SQLEXPRESS")) {
DataContext db = new DataContext(connection);
connection.Open();
Table<Customer> Customers = db.GetTable<Customer>();
Table<Order> Orders = db.GetTable<Order>();
var custs =
from c in Customers
where c.CompanyName.StartsWith("A")
select new {
c.CustomerID,
c.CompanyName,
NumOrders = c.Orders.Count
};
CustomerRecordCollection results = new CustomerRecordCollection();
foreach(var cust in custs){
CustomerRecord rec = new CustomerRecord();
rec.CustomerID = cust.CustomerID;
rec.CompanyName = cust.CompanyName;
rec.NumOrders = cust.NumOrders;
results.Add(rec);
}
_dataReceiver.Process(results);
}
}
This is where I think everything is breaking down...calling the Process()
method to pass back the results. In order to do this, when I start the workflow
in the form, it is passing a reference to itself as sort of a hack to allow for
a "callback." The form implements IDataReceiver which is defined in a common
class library along with CustomerRecord and CustomerRecordCollection.
namespace Common {
public interface IDataReceiver {
void Process(object data);
}
public class CustomerRecordCollection : Collection<CustomerRecord> { }
public class CustomerRecord {
private string _customerId;
private string _companyName;
private int _numberOfOrders;
public int NumOrders {
get { return _numberOfOrders; }
set { _numberOfOrders = value; }
}
public string CompanyName {
get { return _companyName; }
set { _companyName = value; }
}
public string CustomerID {
get { return _customerId; }
set { _customerId = value; }
}
}
}
Drop me a line if you know of a workaround so I can complete the trifecta.
:-)