Thursday, November 28, 2013

A simple converter

This entry references WPF 4.0

In the previous post I showed how to bind a non dependent property using styles. But the value I'm binding to is an integer and the property I'm binding to is a brush so I need a converter. This is how I did it.

1. The first thing to do is write the converter. This is a class that implements IValueConverter. I put all my converters in a single file but it's up to you. You have to write a Convert and a ConvertBack method. I didn't worry about ConvertBack because I'm only binding one way. If I pass 1 it returns a red brush otherwise it returns a black brush. It looks like this.

Public Class ErrorToBrushConverter
    Implements IValueConverter

    Public Function Convert(value As Object, targetType As System.Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert

        If TypeOf value Is Integer Then
            Select Case value
                Case 1
                    Return New SolidColorBrush(System.Windows.Media.Colors.Red)
                Case Else
                    Return New SolidColorBrush(System.Windows.Media.Colors.Black)
            End Select
        Else
            Return New SolidColorBrush(System.Windows.Media.Colors.Black)
        End If
    End Function

    Public Function ConvertBack(value As Object, targetType As System.Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.ConvertBack
        Return 0
    End Function
End Class

2. It is very important to build the project before the next step. Otherwise you will try to reference a class that doesn't exist which will throw an error that will prevent you from compiling the class. Catch-22.

3. Add a reference to your namespace somewhere in the logical tree so you can reference your new converter class. The easiest way is to add this to your Window or UserControl tag in the xaml. You may even have this already for other resources.

xmlns:local="clr-namespace:<your namespace>"

4. Now create a static resource in the same window or usercontrol.

<window.resources>
    <local:ErrorToBrushConverter x:Key="ErrorToBrushConverter"/>
</window.resources>

The 'ErrorToBrushConverter' after local: is the name of the converter class we wrote. The 'ErrorToBrushConverter' after x:Key is the StaticResource name we reference in the Syle tag (see the previous post). This is how we tie the two together.

So the Style line (see previous post) that says...
<Setter Property="Foreground" Value="{Binding Path=HasErrors, Converter={StaticResource ErrorToBrushConverter}}"/>

effectively takes the integer value of the HasErrors field of the underlying dataarow for this grid row (which will be 0 or 1 because that's how I wrote the SQL), finds the converter specified in the StaticResource called ErrorToBrushConverter, calls the Convert method passing the value of HasErrors, takes the return value which will be a black or red brush, and assigns it to the Foreground property. And it does this quickly.

This might seem very complex but it's really not that much more complex that writing a stylesheet and referencing it. The good new is that, like a stylesheet, it's very reusable. You can put the static resource definition in a resource file or in the application's resources so every page of your application can use it. You can even declare the Style itself as a static resource which makes attaching it to a datagridtextcolumn very simple.

Change property in one datagrid column based on a value in another

This entry references WPF 4.0

This is the problem that drove me to create this blog. I wanted to change the foreground property of the document number in a datagrid to indicate when the document contained errors. With an Ultrawebgrid in .Net I would have written a RowIntialize event handler, checked the content of a hidden HasErrors column, and changed the color of the document number cell accordingly.

I Googled the problem and found about four different answers. One of them showed how to modify the Textblock class so that the foreground property became a dependent property and could be bound blah blah. I think the author must have been on crack. The answer is easy.

The STYLE tag is not your grandfather's stylesheet. It is dynamic. Style values can be bound and they can set any property, not just dependent properties. So you can use a STYLE to effectively bind any property.

So my <DataGridTextColumn> looks like this...

<DataGridTextColumn Header="Number" Binding="{Binding Path=DocumentNumber}">
    <DataGridTextColumn.ElementStyle>
        <Style TargetType="{x:Type TextBlock}">
            <Setter Property="Foreground" Value="{Binding Path=HasErrors, Converter={StaticResource ErrorToBrushConverter}}"/>
        </Style>
    </DataGridTextColumn.ElementStyle>
</DataGridTextColumn>

There are a couple of things to note here..

It is difficult to get to the contents of the grid now. But you don't need to because it's so easy to get to the underlying data. I don't need a hidden column called HasErrors, it's just a column in the grid's dataview.

I needed to write a converter. As SQL can't return a brush type, and that's what the foreground property is, I have to convert an integer to a brush. 

Introduction

I'm creating this blog because I'm starting to use WPF and WCF on a new project at work, not because I'm experienced in using these technologies. The problem I'm seeing is that people like me are posting simple questions and getting bad answers. Part of the problem is we don't know the correct questions to ask and part of the problem is that other people don't stop to consider what problem they are trying to solve. Also WPF has evolved over the past five years and posts that made sense five years ago are irrelevant now.

Each of my posts will explain a problem I solved and will include the version of WPF I solved it with. That way, in three years when we are on version 6.0, readers can evaluate the relevance of my solution.