Martijn's blog - E-Commerce, EAI, BizTalk and .NET

2006/10/29

Supporting WPF XML Browser Applications (XBAP) in a CompositeUI Application Block application

Currently, we’re building a real application in WPF for a local government. The application uses the MVC pattern quite heavily, so I did a test to see whether we could use the Composite UI Application Block (CAB, read more about it’s architecture and use, download it from MSDN, play with the labs to understand it) to enable easier composability in order to distribute our use cases more easily throughout the development team, decouple logic from UI even more, etc.

It turns out the CAB currently doesn’t support WPF. After some searching, I discovered Kent Boogaart from Australia (thanks Dion!) is busy writing a support layer for WPF in CAB. So far, so good. Testing the download he provided, however, revealed the implementation doesn’t support so-called XBAP applications.

XBAP application are browser hosted WPF applications and are different in some aspects to ‘traditional’ WPF applications. For instance, the Application object which is the root for all WPF applications, doesn’t allow you to call the Run method if the WPF application is running within a browser. This is because of the asynchronous nature of the webbrowser.

After a few simple modifications to Kent’s code and a little more trickery, I had the BankTeller application working in XBAP. Well, sort of… The thing with CAB is that it assumes synchronous applications. An example of this (taken straight from Microsoft.Practices.CompositeUI.CabApplication):
public void Run()
{
 RegisterUnhandledExceptionHandler();
 Builder builder = CreateBuilder();
 AddBuilderStrategies(builder);
 CreateRootWorkItem(builder);

 ...

 rootWorkItem.FinishInitialization();
 rootWorkItem.Run();
 Start();

 // Whoops for non-blocking Start()s
 rootWorkItem.Dispose();
 if (visualizer != null)
  visualizer.Dispose();

}

As I explained earlier, XBAP applications take offense to calling the Run method on their Application, they’re asynchronous. In order to make XBAP applications work, a few modifications have to be made to CAB itself.
  1. Don’t call Run on the Application class.
  2. Ensure the correct UI element get’s displayed.
  3. Remove the dispose calling from the main Run() method of Microsoft.Practices.CompositeUI.CabApplication.
  4. Implement a correct IDisposable pattern for cleaning up resources once the Application is actually done with them.
  5. Ensure we call Dispose to clean up used resources.
For those just interested in the endresult, a modified WPF port capable of supporting CAB based applications in XBAP, you can download the example.

If you’re interested in more detail about the changes I made and how I did this, please read the whitepaper I wrote, which can be downloaded here.

A special thanks to Kent Boogaart for creating the WPF port for CAB, without which I wouldn’t have been able to put CAB to use in my WPF scenario!

2006/10/17

WPF - Using CompositeCollection to combine multiple sources for databinding

As I said before, I'll blog about things I learn and find interesting... ;-) Today, I realized there's a very easy way to combine different sources for a binding. It's done using what's called a CompositeCollection. Here's how it works:
  1. Create a datasource (XmlDataProvider, ObjectDataProvider, ...)
  2. Create a CollectionViewSource to look at the data in a specific way
  3. Bind a control's Source/ItemsSource/... property to the CollectionViewSource
  4. Add a view 'static' items
  5. Bind to another view at the same or different data
An example:
<XmlDataProvider x:Key="MyData" XPath="/Info">
  <x:XData>
    <Info xmlns="">
      <Item ID="12345" Name="Book 1" Price="$10.00" />
      <Item ID="24678" Name="Book 3" Price="$9.00" />
    </Info>
  </x:XData>
</XmlDataProvider>

<CollectionViewSource x:Key='a' Source="{Binding Source={StaticResource MyData}, XPath=Item/@Price}" />
<CollectionViewSource x:Key='b' Source="{Binding Source={StaticResource MyData}, XPath=Item/@Name}" />

We now have the two CollectionViewSource instances up and running. For example purposes, they're looking at the same datasource, but this could be anything else. Now, let's create a simple ListBox control which binds to both these CollectionViewSource instances and adds constant items..
<ListBox>
  <ListBox.ItemsSource>
    <Binding>
      <Binding.Source>
        <CompositeCollection>
          <ListBoxItem>My Constant Item</ListBoxItem>
          <CollectionContainer Collection="{Binding Source={StaticResource a}}" />
          <CollectionContainer Collection="{Binding Source={StaticResource b}}" />
        </CompositeCollection>
      </Binding.Source>
    </Binding>
  </ListBox.ItemsSource>
</ListBox>

It's as easy as that. The output would be:

My Constant Item
$10.00
$9.00
Book 1
Book 3

Just like the MergedDictionaries within the ResourceDictionary, you can easily combine different sources of data and bind to those sources, even TwoWay.

2006/10/15

WPF - Writing an IMultiValueConverter

Currently, I'm working on a project involving WPF. I will post my experiences from time to time. This week, I wrote a simple IMultiValueConverter implementation which can be used to format a set of bindings in a String.Format fashion. This is how it works:

Define the class and assign a key to refer to it:
<Window.Resources>
  <c:StringFormatterConverter
      x:Key="StringFormatterConverter" />
</Window.Resources>

Bind some element (GridViewColumn in this case) to a datasource, in this case something which has properties called LASTNAME and FIRSTNAME on it:
<GridViewColumn.DisplayMemberBinding>
 <MultiBinding>
   <Binding Path="LASTNAME" />
   <Binding Path="FIRSTNAME" />
 </MultiBinding>
</GridViewColumn.DisplayMemberBinding>

Now set a converter on the MultiBinding:
<MultiBinding Converter="{StaticResource StringFormatterConverter}" ConverterParameter="{}{0}, {1}">

Both IValueConverter and IMultiValueConverter implement two methods:

  • Convert
  • ConvertBack
One for binding one direction and the other for the opposite direction (from->to).

The ConverterParameter in the Converter is used to pass the format we'll be using to display the bound elements.
Let's look at a code sample (sanity checks are removed for readability purposes):
public class StringFormatterConverter : IMultiValueConverter
{
 public object Convert(
  object[] values,
  Type targetType,
  object parameter,
  System.Globalization.CultureInfo culture)
 {
  return string.Format(
   culture,
   formatArgument,
   values);

 }

 public object[] ConvertBack(
  object value,
  Type[] targetTypes,
  object parameter,
  System.Globalization.CultureInfo culture)
 {
  throw new NotImplementedException(
   "Unable to convert a formatted argument string back.");
 }
}

It's that simple, WPF will pass the values to the converter and expects a converted value back. We format the values using the other supplied arguments, parameter ('{0}, {1}') and culture.

You can use any string.Format argument you'd like; just remember to escape the first '{' sign, so the Xaml interpreter knows you don't mean to use a Markup Extension.

Escaping the first brace is done by placing an empty mark extension in front of
the character ('{}{').

The namespace prefix 'c' refers to the assembly and namespace implementing the converter (e.g. <xmlns:c="clr-namespace:Martijn.Wpf.Converters" />