Saturday 29 October 2011

Getting to grips with DataGrid (Styles Cont.)

So continuing on from the last post, here's a quick update on some more styling of your cells and grid. In this post we're going to show:
  • Changing the brush and hiding the vertical or horizontal grid lines.
  • Changing the foreground and background font brushes.
  • Playing around with text alignment (for the moment just left, center and right) in both vertical and horizontal.
So first of all, we're going to slightly expand on our test object to add some new values. Here's the one to use now:

namespace AllInTheXaml.DataGrid.BasicStyling
{
    public class TestObject
    {
        public string Test { get; set; }
        public double Value { get; set; }
        public double Bid { get; set; }
        public double Ask { get; set; }
    }
}
 
Here we're just introducing two new values which we can play around with.

Grid Lines

Lets start with the easy one. The DataGrid already exposes the brush for both the vertical and horizontal direction as VerticalGridLinesBrush and HorizontalGridLinesBrush respectively. Great thing is that you can set these to Transparent to make them invisible! It's as easy as that to hide them across the whole grid.


Here's a quick example:


<Window x:Class="AllInTheXaml.DataGrid.BasicStyling.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:Collections="clr-namespace:System.Collections;assembly=mscorlib" 
    xmlns:BasicStyling="clr-namespace:AllInTheXaml.DataGrid.BasicStyling" 
    xmlns:Controls="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit" 
    Title="Window1" Height="300" Width="300">
    <Window.Resources>
        <Collections:ArrayList x:Key="data">
            <BasicStyling:TestObject Test="positive" Value="2.3" Bid="100" Ask="102"/>
            <BasicStyling:TestObject Test="negative" Value="-1.7" Bid="99" Ask="103"/>
            <BasicStyling:TestObject Test="zero" Value="0"/>
        </Collections:ArrayList>
        <BasicStyling:DoubleSignConverter x:Key="DoubleSignConverter"/>
    </Window.Resources>    
    <Controls:DataGrid CanUserAddRows="False" 
                       AutoGenerateColumns="False"                        
                       HeadersVisibility="Column" 
                       VerticalGridLinesBrush="Transparent"
                       ItemsSource="{Binding Source={StaticResource data}}">
        <Controls:DataGrid.Columns>
            <Controls:DataGridTextColumn Header="Test" Binding="{Binding Test}"/>
            <Controls:DataGridTemplateColumn Header="Change">
                <Controls:DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*"/>
                                <ColumnDefinition Width="Auto"/>
                            </Grid.ColumnDefinitions>
                            <TextBlock Grid.Column="0" Text="{Binding Value}" HorizontalAlignment="Right"/> 
                            <Image Margin="2,0,0,0" Width="10" Height="10" x:Name="valueImage" Grid.Column="1">  
                                <Image.Style>
                                    <Style TargetType="{x:Type Image}">
                                        <Style.Triggers>
                                            <DataTrigger Binding="{Binding Value, Converter={StaticResource DoubleSignConverter}}" Value="1">
                                                <Setter Property="Source" Value="greenUpArrow_16.png"/>
                                            </DataTrigger>
                                            <DataTrigger Binding="{Binding Value, Converter={StaticResource DoubleSignConverter}}" Value="-1">
                                                <Setter Property="Source" Value="redDownArrow_16.png"/>
                                            </DataTrigger>
                                            <DataTrigger Binding="{Binding Value, Converter={StaticResource DoubleSignConverter}}" Value="0">
                                                <Setter Property="Source" Value="{x:Null}"/>
                                            </DataTrigger>
                                        </Style.Triggers>
                                    </Style>
                                </Image.Style>                                    
                            </Image>
                        </Grid>
                    </DataTemplate>
                </Controls:DataGridTemplateColumn.CellTemplate>
            </Controls:DataGridTemplateColumn>
        </Controls:DataGrid.Columns>
    </Controls:DataGrid>
</Window>
 
Which will produce a window like this with no vertical grid lines


Foreground Cell Brush

For this one we're going to keep playing around with the change column. Instead of having the negative sign, we'll show the absolute value of the change but add green or red colouring to indicate the direction.

First, you'll obviously need a new converter to get the absolute value, so lets put that together.

using System;
using System.Globalization;
using System.Windows.Data;

namespace AllInTheXaml.DataGrid.BasicStyling
{
    public class AbsConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if(value == null)
                return null;

            return Math.Abs((double) value);
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotSupportedException("Only able to support one way binding for AbsConverter");
        }
    }
}
And now all we need to do is add the right styling to our TextBlock to make sure it colours correctly. We can use the same sign converter as we did for the arrows for this.

<Window x:Class="AllInTheXaml.DataGrid.BasicStyling.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:Collections="clr-namespace:System.Collections;assembly=mscorlib" 
    xmlns:BasicStyling="clr-namespace:AllInTheXaml.DataGrid.BasicStyling" 
    xmlns:Controls="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit" 
    Title="Window1" Height="300" Width="300">
    <Window.Resources>
        <Collections:ArrayList x:Key="data">
            <BasicStyling:TestObject Test="positive" Value="2.3" Bid="100" Ask="102"/>
            <BasicStyling:TestObject Test="negative" Value="-1.7" Bid="99" Ask="103"/>
            <BasicStyling:TestObject Test="zero" Value="0"/>
        </Collections:ArrayList>
        <BasicStyling:DoubleSignConverter x:Key="DoubleSignConverter"/>
        <BasicStyling:AbsConverter x:Key="AbsConverter"/>
    </Window.Resources>    
    <Controls:DataGrid CanUserAddRows="False" 
                       AutoGenerateColumns="False"                        
                       HeadersVisibility="Column" 
                       VerticalGridLinesBrush="Transparent"
                       ItemsSource="{Binding Source={StaticResource data}}">
        <Controls:DataGrid.Columns>
            <Controls:DataGridTextColumn Header="Test" Binding="{Binding Test}"/>
            <Controls:DataGridTemplateColumn Header="Change">
                <Controls:DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*"/>
                                <ColumnDefinition Width="Auto"/>
                            </Grid.ColumnDefinitions>
                            <TextBlock Grid.Column="0" Text="{Binding Value, Converter={StaticResource AbsConverter}}" HorizontalAlignment="Right">
                                <TextBlock.Style>
                                    <Style TargetType="{x:Type TextBlock}">
                                        <Style.Triggers>
                                            <DataTrigger Binding="{Binding Value, Converter={StaticResource DoubleSignConverter}}" Value="1">
                                                <Setter Property="Foreground" Value="#FF66C872"/>
                                            </DataTrigger>
                                            <DataTrigger Binding="{Binding Value, Converter={StaticResource DoubleSignConverter}}" Value="-1">
                                                <Setter Property="Foreground" Value="#FFBD182C"/>
                                            </DataTrigger>
                                            <DataTrigger Binding="{Binding Value, Converter={StaticResource DoubleSignConverter}}" Value="0">
                                                <Setter Property="Foreground" Value="#FF000000"/>
                                            </DataTrigger>
                                        </Style.Triggers>
                                    </Style>
                                </TextBlock.Style>
                            </TextBlock>
                            <Image Margin="2,0,0,0" Width="10" Height="10" x:Name="valueImage" Grid.Column="1">  
                                <Image.Style>
                                    <Style TargetType="{x:Type Image}">
                                        <Style.Triggers>
                                            <DataTrigger Binding="{Binding Value, Converter={StaticResource DoubleSignConverter}}" Value="1">
                                                <Setter Property="Source" Value="greenUpArrow_16.png"/>
                                            </DataTrigger>
                                            <DataTrigger Binding="{Binding Value, Converter={StaticResource DoubleSignConverter}}" Value="-1">
                                                <Setter Property="Source" Value="redDownArrow_16.png"/>
                                            </DataTrigger>
                                            <DataTrigger Binding="{Binding Value, Converter={StaticResource DoubleSignConverter}}" Value="0">
                                                <Setter Property="Source" Value="{x:Null}"/>
                                            </DataTrigger>
                                        </Style.Triggers>
                                    </Style>
                                </Image.Style>                                    
                            </Image>
                        </Grid>
                    </DataTemplate>
                </Controls:DataGridTemplateColumn.CellTemplate>
            </Controls:DataGridTemplateColumn>
        </Controls:DataGrid.Columns>
    </Controls:DataGrid>
</Window> 
 
Just look how easy this is. We get to actually work with the value itself and format it exactly how we want with touching code and it's so easy to plug and play our converters and different controls to create really custom styles to the grid. This just shows one of the real great powers of WPF in action. Here's what we get


Background Cell Brush and Text Alignment Together!


There is a reason I want to show these together and it's mainly to show the limitation of some of the controls in the DataGrid. This actually is not possible to do with the DataGridTextColumn. The default control it seems that underlines this is a TextBlock (which makes sense) but if you've worked much with it you'll know that it doesn't have a HorizontalContentAlignment property. This means you can't have the control itself stretch the whole cell and align the text.

I must admit, I found this quite a bad oversight from the DataGrid developers as it seems like reasonably obvious functionality. However, given we have the DataGridTemplateColumn all is not lost! We can design it ourselves. Hoozah!

All we really need to do is add a Border around the TextBlock. We can align the TextBlock as we want with a transparent background and then use the Border (which stretches by default) to control our background brush.

Before we put that all in motion just going to introduce one last converter so that if the bid or ask values are zero they don't show up (this will also show how to hide error or null values in the grid)
using System;
using System.Globalization;
using System.Windows.Data;

namespace AllInTheXaml.DataGrid.BasicStyling
{
    public class IsZeroOrNullConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if(value == null)
                return null;

            return ((double) value) == 0;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotSupportedException("Only able to support one way binding for IsZeroOrNullConverter");
        }
    }
}
And now the XAML

<Window x:Class="AllInTheXaml.DataGrid.BasicStyling.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:Collections="clr-namespace:System.Collections;assembly=mscorlib" 
    xmlns:BasicStyling="clr-namespace:AllInTheXaml.DataGrid.BasicStyling" 
    xmlns:Controls="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit" 
    Title="Window1" Height="300" Width="300">
    <Window.Resources>
        <Collections:ArrayList x:Key="data">
            <BasicStyling:TestObject Test="positive" Value="2.3" Bid="100" Ask="102"/>
            <BasicStyling:TestObject Test="negative" Value="-1.7" Bid="99" Ask="103"/>
            <BasicStyling:TestObject Test="zero" Value="0"/>
        </Collections:ArrayList>
        <BasicStyling:DoubleSignConverter x:Key="DoubleSignConverter"/>
        <BasicStyling:AbsConverter x:Key="AbsConverter"/>
        <BasicStyling:IsZeroOrNullConverter x:Key="IsZeroOrNullConverter"/>
    </Window.Resources>    
    <Controls:DataGrid CanUserAddRows="False" 
                       AutoGenerateColumns="False"                        
                       HeadersVisibility="Column" 
                       VerticalGridLinesBrush="Transparent"
                       ItemsSource="{Binding Source={StaticResource data}}">
        <Controls:DataGrid.Columns>
            <Controls:DataGridTextColumn Header="Test" Binding="{Binding Test}"/>
            <Controls:DataGridTemplateColumn Header="Change">
                <Controls:DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*"/>
                                <ColumnDefinition Width="Auto"/>
                            </Grid.ColumnDefinitions>
                            <TextBlock Grid.Column="0" Text="{Binding Value, Converter={StaticResource AbsConverter}}" HorizontalAlignment="Right">
                                <TextBlock.Style>
                                    <Style TargetType="{x:Type TextBlock}">
                                        <Style.Triggers>
                                            <DataTrigger Binding="{Binding Value, Converter={StaticResource DoubleSignConverter}}" Value="1">
                                                <Setter Property="Foreground" Value="#FF66C872"/>
                                            </DataTrigger>
                                            <DataTrigger Binding="{Binding Value, Converter={StaticResource DoubleSignConverter}}" Value="-1">
                                                <Setter Property="Foreground" Value="#FFBD182C"/>
                                            </DataTrigger>
                                            <DataTrigger Binding="{Binding Value, Converter={StaticResource DoubleSignConverter}}" Value="0">
                                                <Setter Property="Foreground" Value="#FF000000"/>
                                            </DataTrigger>
                                        </Style.Triggers>
                                    </Style>
                                </TextBlock.Style>
                            </TextBlock>
                            <Image Margin="2,0,0,0" Width="10" Height="10" x:Name="valueImage" Grid.Column="1">  
                                <Image.Style>
                                    <Style TargetType="{x:Type Image}">
                                        <Style.Triggers>
                                            <DataTrigger Binding="{Binding Value, Converter={StaticResource DoubleSignConverter}}" Value="1">
                                                <Setter Property="Source" Value="greenUpArrow_16.png"/>
                                            </DataTrigger>
                                            <DataTrigger Binding="{Binding Value, Converter={StaticResource DoubleSignConverter}}" Value="-1">
                                                <Setter Property="Source" Value="redDownArrow_16.png"/>
                                            </DataTrigger>
                                            <DataTrigger Binding="{Binding Value, Converter={StaticResource DoubleSignConverter}}" Value="0">
                                                <Setter Property="Source" Value="{x:Null}"/>
                                            </DataTrigger>
                                        </Style.Triggers>
                                    </Style>
                                </Image.Style>                                    
                            </Image>
                        </Grid>
                    </DataTemplate>
                </Controls:DataGridTemplateColumn.CellTemplate>
            </Controls:DataGridTemplateColumn>
            <Controls:DataGridTemplateColumn Width="40" Header="Bid">
                <Controls:DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <Border Margin="-1">
                            <TextBlock Text="{Binding Bid}" HorizontalAlignment="Center">
                            <TextBlock.Style>
                                <Style TargetType="{x:Type TextBlock}">
                                    <Style.Triggers>
                                        <DataTrigger Binding="{Binding Bid, Converter={StaticResource IsZeroOrNullConverter}}" Value="true">
                                            <Setter Property="Foreground" Value="Transparent"/>
                                        </DataTrigger>
                                    </Style.Triggers>
                                </Style>
                            </TextBlock.Style>
                            </TextBlock>
                            <Border.Style>
                                <Style TargetType="{x:Type Border}">
                                    <Style.Triggers>
                                        <DataTrigger Binding="{Binding Bid, Converter={StaticResource IsZeroOrNullConverter}}" Value="false">
                                            <Setter Property="Background" Value="#FFAAC9E9"/>
                                        </DataTrigger>
                                    </Style.Triggers>
                                </Style>
                            </Border.Style>
                        </Border>
                    </DataTemplate>
                </Controls:DataGridTemplateColumn.CellTemplate>
            </Controls:DataGridTemplateColumn>
            <Controls:DataGridTemplateColumn Width="40" Header="Ask">
                <Controls:DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <Border Margin="-1">
                            <TextBlock Text="{Binding Ask}" HorizontalAlignment="Center">
                            <TextBlock.Style>
                                <Style TargetType="{x:Type TextBlock}">
                                    <Style.Triggers>
                                        <DataTrigger Binding="{Binding Ask, Converter={StaticResource IsZeroOrNullConverter}}" Value="true">
                                            <Setter Property="Foreground" Value="Transparent"/>
                                        </DataTrigger>
                                    </Style.Triggers>
                                </Style>
                            </TextBlock.Style>
                            </TextBlock>
                            <Border.Style>
                                <Style TargetType="{x:Type Border}">
                                    <Style.Triggers>
                                        <DataTrigger Binding="{Binding Ask, Converter={StaticResource IsZeroOrNullConverter}}" Value="false">
                                            <Setter Property="Background" Value="#FFFFA4A4"/>
                                        </DataTrigger>
                                    </Style.Triggers>
                                </Style>
                            </Border.Style>
                        </Border>
                    </DataTemplate>
                </Controls:DataGridTemplateColumn.CellTemplate>
            </Controls:DataGridTemplateColumn>
        </Controls:DataGrid.Columns>
    </Controls:DataGrid>
</Window> 
 
Those with the observant eye will have noticed the little introduction of margins on the border. These are due to the whitespace that the data grid cell automatically adds around your template. I don't particularly like this so using margins to override them. I'm using a constant margin around the whole border so that this wouldn't look odd if you started moving the columns around.

Here's how it looks

So that's pretty much it for me today. Look forward to some more data grids tomorrow!

Thursday 27 October 2011

Getting to grips with DataGrid (Styles)

Due to work has been a while since I've posted here, but finally got a bit more free time. I've decided to invest a bit of time looking at the possibilities of the WPF Toolkit's DataGrid and really see how far we can push it.

So first thing is first, basic stylings. I want to be able to show an arrow that indicates the direction a value is going in. A simple premise that was painful to do in WinForms.

All ready? Then lets get started! First will create a mock object that I'll look to use throughout this series so it's easy for you to recreate the data. I'll also upload the code at some point as well.

namespace AllInTheXaml.DataGrid.BasicStyling
{
    public class TestObject
    {
        public string Test { get; set; }
        public double Value { get; set; }
    }
}
 
Great, now we'll start with the most basic implementation of the data grid and see what we get.
<Window x:Class="AllInTheXaml.DataGrid.BasicStyling.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
    xmlns:Collections="clr-namespace:System.Collections;assembly=mscorlib"  
    xmlns:BasicStyling="clr-namespace:AllInTheXaml.DataGrid.BasicStyling" 
    xmlns:Controls="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit"  
    Title="Window1" Height="300" Width="300">
    <Window.Resources>
        <Collections:ArrayList x:Key="data">
            <BasicStyling:TestObject Test="positive" Value="2.3"/>
            <BasicStyling:TestObject Test="negative" Value="-1.7"/>
            <BasicStyling:TestObject Test="zero" Value="0"/>
        </Collections:ArrayList>        
    </Window.Resources>
    <Controls:DataGrid CanUserAddRows="False" HeadersVisibility="Column" 
        ItemsSource="{Binding Source={StaticResource data}}"/>        
</Window>
Well that seemed simple enough, so how does that look?

 Not too bad at all. 

Now lets come to styling. As you would imagine for the Microsoft provided grid model, it is very WPF focused. That means we can achieve a lot of what we want to do in XAML. In fact, nearly everything can be done without a single piece of code. Lets take a look at some of this XAML then.
 
<Window x:Class="AllInTheXaml.DataGrid.BasicStyling.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:Collections="clr-namespace:System.Collections;assembly=mscorlib" 
    xmlns:BasicStyling="clr-namespace:AllInTheXaml.DataGrid.BasicStyling" 
    xmlns:Controls="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit" 
    Title="Window1" Height="300" Width="300">
    <Window.Resources>
        <Collections:ArrayList x:Key="data">
            <BasicStyling:TestObject Test="positive" Value="2.3"/>
            <BasicStyling:TestObject Test="negative" Value="-1.7"/>
            <BasicStyling:TestObject Test="zero" Value="0"/>
        </Collections:ArrayList>        
    </Window.Resources>    
        <Controls:DataGrid Margin="0,10,0,0" CanUserAddRows="False"  
            AutoGenerateColumns="False" HeadersVisibility="Column"  
            ItemsSource="{Binding Source={StaticResource data}}">
            <Controls:DataGrid.Columns>
                <Controls:DataGridTextColumn Header="Test" Binding="{Binding Test}"/>
                <Controls:DataGridTemplateColumn Header="Value">
                    <Controls:DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <Grid>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="*"/>
                                    <ColumnDefinition Width="Auto"/>
                                </Grid.ColumnDefinitions>
                                <TextBlock Grid.Column="0" Text="{Binding Value}" HorizontalAlignment="Right"/>
                                <Image Margin="2,0,0,0" Width="10" Height="10" x:Name="valueImage" 
                                    Grid.Column="1" Source="greenUpArrow_16.png"/>
                        </Grid>
                        </DataTemplate>
                    </Controls:DataGridTemplateColumn.CellTemplate>
                </Controls:DataGridTemplateColumn>
                </Controls:DataGrid.Columns>
        </Controls:DataGrid>
</Window> 
 
The DataGrid provides a number of different types of template columns that you can use for rendering your cell. As you can see here, we have used a basic text one to show the header (given that we don't really want this to change from the default) but using the DataGridTemplateColumn for the value. This control gives us the ability to set exactly how the cell should look when being read. It also can be defined to show what it looks like when it is being edited as well! But that's for another time. Here we now show a text block (right aligned like all numbers should be) and an image of our arrow. So what does this look like then?


Ahh yes, well I've only used a single image. Well the reason was I wanted you to understand the templating first before we actually went to complete our goal. So I did say that almost all of it could be achieved in XAML, well here comes the little bit of code. We need to actually write a converter which will return us the sign of the value so we can predicate which arrow to use. Positive will mean an up green arrow, negative will mean a down red arrow and zero will mean set it to null. Simples! So here's the code.
using System;
using System.Globalization;
using System.Windows.Data;

namespace AllInTheXaml.DataGrid.BasicStyling
{
    public class DoubleSignConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if(value == null)
                return null;

            return Math.Sign((double) value);
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotSupportedException("Only able to support one way binding for DoubleSingConverter");
        }
    }
}

And the XAML to go with this

<Window x:Class="AllInTheXaml.DataGrid.BasicStyling.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:Collections="clr-namespace:System.Collections;assembly=mscorlib" 
    xmlns:BasicStyling="clr-namespace:AllInTheXaml.DataGrid.BasicStyling" 
    xmlns:Controls="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit" 
    Title="Window1" Height="300" Width="300">
    <Window.Resources>
        <Collections:ArrayList x:Key="data">
            <BasicStyling:TestObject Test="positive" Value="2.3"/>
            <BasicStyling:TestObject Test="negative" Value="-1.7"/>
            <BasicStyling:TestObject Test="zero" Value="0"/>
        </Collections:ArrayList>
        <BasicStyling:DoubleSignConverter x:Key="DoubleSignConverter"/>
    </Window.Resources>    
    <Controls:DataGrid Margin="0,10,0,0" CanUserAddRows="False" 
        AutoGenerateColumns="False" HeadersVisibility="Column"  
        ItemsSource="{Binding Source={StaticResource data}}">
        <Controls:DataGrid.Columns>
            <Controls:DataGridTextColumn Header="Test" Binding="{Binding Test}"/>
            <Controls:DataGridTemplateColumn Header="Value">
                <Controls:DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*"/>
                                <ColumnDefinition Width="Auto"/>
                            </Grid.ColumnDefinitions>
                            <TextBlock Grid.Column="0" Text="{Binding Value}" HorizontalAlignment="Right"/>
                            <Image Margin="2,0,0,0" Width="10" Height="10" x:Name="valueImage" Grid.Column="1">  
                                <Image.Style>
                                    <Style TargetType="{x:Type Image}">
                                        <Style.Triggers>
                                            <DataTrigger Binding="{Binding Value, Converter={StaticResource DoubleSignConverter}}" Value="1">
                                                <Setter Property="Source" Value="greenUpArrow_16.png"/>
                                            </DataTrigger>
                                            <DataTrigger Binding="{Binding Value, Converter={StaticResource DoubleSignConverter}}" Value="-1">
                                                <Setter Property="Source" Value="redDownArrow_16.png"/>
                                            </DataTrigger>
                                            <DataTrigger Binding="{Binding Value, Converter={StaticResource DoubleSignConverter}}" Value="0">
                                                <Setter Property="Source" Value="{x:Null}"/>
                                            </DataTrigger>
                                        </Style.Triggers>
                                    </Style>
                                </Image.Style>                                    
                            </Image>
                        </Grid>
                    </DataTemplate>
                </Controls:DataGridTemplateColumn.CellTemplate>
            </Controls:DataGridTemplateColumn>
            </Controls:DataGrid.Columns>
    </Controls:DataGrid>
</Window> 
 

And we get a lovely column looking like this
   
So I hope that this shows just how much can be achieved with the DataGrid from a styling perspective very easily from just a few basic pieces of XAML.