Archive for January, 2009

The WPF DataGrid and Me

or, how to change the background of a cell in a WPF Toolkit DataGrid data-bound to a DataTable.

This seemingly simple task is either very complex or I am missing something very fundamental here. My requirement was to change the background colour of a cell to light red if that cell contained the value 1.0. My first impulse was to root around the object model looking for a Cells collection. After all there’s a Columns collection so I expected at least a Rows and, with some luck, a Cells. No such luck. After losing a few hours I admitted defeat and asked the question in the WPF MSDN newsgroups. I got a single answer but thankfully it sent me in the right direction.

Like in many other things in WPF I was in the “wrong” mindset to approach the problem, what I should look for was some way of binding something to the cell style through some converter. The answer suggested using some XAML based on:

<wpf:DataGrid.CellStyle>
    <Style TargetType="{x:Type wpf:DataGridCell}">
        <Setter Property="Background"
                Value="{Binding SomeProperty,
                Converter={StaticResource cellBackgroundConverter}}" />
    </Style>
</wpf:DataGrid.CellStyle>

This is spot on for controlling the style of cells that are bound to objects. The converter would be coded along these lines:

public object Convert(
    object value,
    Type targetType,
    object parameter,
    CultureInfo culture)
{
    var obj = (MyCustomObject)value;
    return obj.IsHighlighted 
        ? new SolidBrush(Colors.LightSalmon)
        : SystemColors.AppWorkspaceColor;
}

Now the problem with DataGrids data-bound to DataTables is that the value passed to the converter is a DataRow, not exactly very useful since I also need the column and it is nowhere to be found. So ending the suspense, the solution is to use MultiDataBinding. This will allow us to pass the row and the DataGridCell to the MultiConverter and from both we can derive the correct cell (there’s a download with all the code at the end of this post).

In your XAML file, in the resources (for some reason it will not work in the style of the DataGrid) add:

<myApp:CellHighlighterConverter x:Key="cellHighlighterConverter" />

<Style x:Key="CellHighlighterStyle">
  <Setter Property="dg:DataGrid.Background">
    <Setter.Value>
      <MultiBinding
        Converter="{StaticResource cellHighlighterConverter}" >
        <MultiBinding.Bindings>
          <Binding RelativeSource="{RelativeSource Self}"/>
          <Binding Path="Row" Mode="OneWay"/>
        </MultiBinding.Bindings>
     </MultiBinding>
   </Setter.Value>
  </Setter>
</Style>

And in the DataGrid declaration add the parameter:

CellStyle="{StaticResource cellHighlighterConverter }"

And finally create a new converter with the following code:

public class CellHighlighterConverter : IMultiValueConverter
{
  public object Convert(
    object[] values,
    Type targetType,
    object parameter,
    CultureInfo culture)
  {
    if (values[1] is DataRow)
    {
        //Change the background of any
        //cell with 1.0 to light red.
        var cell = (DataGridCell)values[0];
        var row = (DataRow)values[1];
        var columnName = cell.Column.SortMemberPath;

        if (row[columnName].IsNumeric()
           row[columnName].ToDouble() == 1.0)
        return new SolidColorBrush(Colors.LightSalmon);
    }
    return SystemColors.AppWorkspaceColor;
  }

  public object[] ConvertBack(
      object value,
      Type[] targetTypes,
      object parameter,
      CultureInfo culture)
  {
      throw new System.NotImplementedException();
  }
}

Final Notes

The Path=”Row,Mode=OneWay” binding must be set OneWay or you get a nasty exception outside your code (“A TwoWay or OneWayToSource binding cannot work on the read-only, etc, etc”). Took me a while to find out which binding this was referring to because the exception was only thrown when I changed tabs (my DataGrid sits on a TabControl).

Another oddity is that I am using the SortMemberPath to fish for the column name. This sounds so wrong… but I did not find any other way.

And finally, a disclaimer, I am not sure if this is the right way or even the best way. I surely would like to hear from anyone with a leaner solution since I am going to have a lot of large DataGrids with a lot of colours throughout my application.

Yet More Notes

A few hours after posting this I found a series of posts about this same problem. Since they follow a very different route I am leaving here a link for reference:

WPF Codeplex: change style of a single cell in a datagrid

 And the Code

You can download a simple WPF application demonstrating the solution from here

User Interface and multithreading in .NET

The not so secret recipe for a responsive User Interface is to farm out as much work as possible to a different thread. The easiest approach in the .NET world is to create a BackgroundWorker and a couple of delegates. It is a very simple model and the pseudo code is:

public partial class Form1
{
    BackgroundWorker _worker = new BackgroundWorker();

    private void LoadFile(string filter)
    {
        _worker.DoWork += (sender, e) =>
            { // read a large file from disk };    

        _worker.RunWorkerCompleted += (sender, e) =>
            {
                txtFileContents.Text = e.Result.ToString();
                lblStatus.Text = "Loaded";
            };
        lblStatus.Text = "Loading...";
        _worker.RunWorkerAsync(theFilePath);
    }
}

It is hard to make it any simpler. The only caveats are that you have to know about delegates and you have to grok the fact that your code does not run in order. In fact the first line to run is “txtStatus.Text = “Loading…”;” followed by the “_worker.RunWorkerAsync()” which then kicks off the DoWork block followed by the RunWorkerCompleted block.

The simplicity of this approach covers most scenarios but there are two disadvantages: you have to have one BackgroundWorker per concurrent piece of code and if you have several arguments you need to pass to the DoWork delegate you need to pack them manually. This can add up to a considerable amount of repeated code in complex UIs. To remedy the problem I came up with two classes that wrap the BackgroundWorker and remove the disadvantages. They also use a fluent interface or internal DSL to hopefully make the process more explicit. The bad news is that you still have to know about delegates. The translation of the example would be:

public partial class Form1
{
    private void LoadFile(string filter)
    {
        lblStatus.Text = "Loading...";

        WorkerPool.Next
          .AddArguments(theFilePath)
          .DoWork( e => { // read a large file }; )
          .WhenFinished( e =>
            {
                txtFileContents.Text = e.Result.ToString();
                lblStatus.Text = "Loaded";
            });
    }
}

The first advantage of this approach is that you don’t need to declare any BackgroundWorker. It still exists but it is in the WorkerPool class. The second advantage is the AddArguments method which accepts any number of arguments packs them into a list and passes it on to the DoWork delegate. Then there is the order of execution which matches the order of coding and, finally, there are several overloads for DoWork and WhenFinished simplifying the signatures of the delegates. The argument classes (the e parameters above) are of the exact instances that the BackgroundWorker generates so all the original information is there.

The disadvantage of this method is that you have to clean up after yourself. The BackgroundWorker is a component that when sited on a form or control is disposed by them. Since I am creating instances inside the WorkerPool class they have to be disposed of at some point. From my tests so far I haven’t found any problems and I will report back any I come across as I intend to use this library throughout the whole application I am building. Having said this, the WorkerPool recycles BackgroundWorkers and you should not have to clear it (WorkerPool.Clear()) at any time unless you are creating and cancelling large numbers of threads.

I will continue the description of WorkerPool in future posts covering thread cancelling, progress report and error handling. Meanwhile have fun with it and report back any bugs or improvements.

The code, in C# can be downloaded from here. This code requires .NET 3.5 but it can possibly run with .NET 2.0 if you replace the two or three Linq code lines.

Is WPF really RAD?

Our flagship application is a hybrid of VB 6.0, ATL and straight C++ and we are well into the development of a new version that will have many UI enhancements. Since it is a major realease and we have some time available we decided to use WPF. In a way it will help us future proof the UI by synchronizing with Microsoft’s own direction. This is all good but after spending nearly all day trying to make the WPF’s ListView match the behaviour of a WinForms or VB 6.0 ListView with CheckBoxes I am starting to have doubts about the whole thing. I can’t bring myself to ditch WPF completely as some of the features are pure goodness but there are two areas that are letting it down big time. First is the absence of all the controls available in WinForms and second is the performance and usability of the WPF designer.

Build your own?

Looking through the newsgroups it seems that the standard response to my first point is that it is very easy to put together complex controls with WPF. So if you need, like I do, a ListView with checkboxes then just build it. This does not fly because replicating all the behaviours that are expected is not trivial. I found a very good post that does just what I need here. This blog entry is very well put together and the code works. I managed to add a fully data-bound ListBox with CheckBox support in a few minutes. But then my ListBox needs to support the checking and un-checking of multiple items simultaneously. No luck. I will have to do it myself. Probably not very difficult even with the data binding code but contrast this with a simple drag and drop of a control onto the WinForms design surface and some bog standard .NET code. The latter is a much better development experience and a lot less code for me to debug and test. The solution is to host the WinForms but I find myself reverting to this solution too frequently with all the problems that this brings.

Slow going

The second problem is the WPF designer and to some extent Visual Studio. I have two main development PCs, one is an x86 running XP and the other an x64 running Vista x64. In both of them Visual Studio has a very decent performance except in it comes to the WPF designer. There are frequent hold ups and even a crash every now and then. It is usable but barely. Ten years after VB 6.0’s “death” Visual Studio designers aren’t still as responsive and stable as they were then. I don’t know what to make of this. The solution has been to drop the designer and use XAML which is very inefficient since I have to compile the application every time I need to see the resulting UI.

I don’t want this post to be gratuitous bashing but I am struggling and so are many other developers out there. I hope this helps the Visual Studio/WPF teams understand the real problems we are facing and maybe get some relief in the form of a Service Pack.