In the first WPF ListView blog of this set, I used XML data binding and added some basic formatting to the Column Headers of the ListView. In the previous blog, a collection of objects was used as the data source. The formatting in that second example was a bit plain. So, in this blog I will look at some ways of breaking away from the standard monochrome ListView.
First, let's revisit those Column Headers. Instead of the plain block Yellow I used in the first example, we can use a Gradient Brush for the TextBlock background and place a matching color Border round the outside. The finished headers will look like this:

As before, it is best to try and centralize as much of the XAML for the GUI as possible. To do this we use DataTemplates and Styles.
First, there is a Style for the blue border which surrounds each column header TextBlock, which I have chosen to store in the Application.xaml file as an application wide Resource:
<Style x:Key="BlueBorder" TargetType="Border">
<Setter Property="BorderBrush" Value="#FF6B93CC" />
<Setter Property="BorderThickness" Value="4"/>
<Setter Property="CornerRadius" Value="2"/>
<Setter Property="Padding" Value="2"/>
< SPAN>Style>
There is a LinearGradientBrush which is used to give the TextBlock background the blue and white gradient look. Again I have put this in Application.xaml:
<LinearGradientBrush x:Key="SubtleBlue" EndPoint="0.476,-0.09" StartPoint="0.476,1.363">
<GradientStop Color="#FF7A8EEC" Offset="0.013"/>
<GradientStop Color="#FF6B93CC" Offset="1"/>
<GradientStop Color="#FFF7F6F7" Offset="0.54"/>
<GradientStop Color="#FFEDEEF4" Offset="0.46"/>
< SPAN>LinearGradientBrush>
The TextBlocks in the column headers also use a Style. This Style, as you can see, incorporates that LinearGradientBrush:
<Style x:Key="ColHeaderText" TargetType="TextBlock">
<Setter Property="FontSize" Value="14" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Background" Value="{StaticResource SubtleBlue}" />
<Setter Property="Foreground" Value="Black" />
<Setter Property="Padding" Value="2" />
< SPAN>Style>
So far, we have Styles for the Border and the TextBlock. The next step is to use these styles in a DataTemplate for the Column Headers.
<DataTemplate x:Key="IDColHeader">
<Border Style="{StaticResource BlueBorder}">
<TextBlock Text="Product ID " Style="{StaticResource ColHeaderText}"/>
< SPAN>Border>
< SPAN>DataTemplate>
<DataTemplate x:Key="NameColHeader">
<Border Style="{StaticResource BlueBorder}">
<TextBlock Text="Product Name " Style="{StaticResource ColHeaderText}" />
< SPAN>Border>
< SPAN>DataTemplate>
<DataTemplate x:Key="PackageColHeader">
<Border Style="{StaticResource BlueBorder}">
<TextBlock Text="Pack Size " Style="{StaticResource ColHeaderText}" />
< SPAN>Border>
< SPAN>DataTemplate>
These three DataTemplates are essentially the same, the only difference being the text content. It would be possible to have only one DataTemplate for all the TextBlocks and use TemplateBinding to pass in the required text from the ListView markup, but I thought I would try and keep things as simple as possible for you at this stage.
We now have everything we need in the way of resources to build the GUI of the ListView. Taking things one step at a time, here is the first pass at the ListView markup:
<ListView Name="ProductsListView"
ItemsSource="{Binding}"
Margin="5,25" >
<ListView.View>
<GridView>
<GridViewColumn
HeaderTemplate="{StaticResource IDColHeader}">
< SPAN>GridViewColumn>
<GridViewColumn
HeaderTemplate="{StaticResource NameColHeader}">
< SPAN>GridViewColumn>
<GridViewColumn
HeaderTemplate="{StaticResource PackageColHeader}">
< SPAN>GridViewColumn>
< SPAN>GridView>
< SPAN>ListView.View>
< SPAN>ListView>
The data to be displayed comes from the bound DataSource. (This is covered in the previous blog item).
Each GridViewColumn uses its own HeaderTemplate to create the gradient TextBlock with blue border and appropriate text.
If you were to run this now, you will see that the headers have the right look, but the data isn't what you want:

This is because I haven't yet set the DataTemplates for each of the cells - that is, each item in each row. More specifically, I haven't set the appropriate Binding that will link the correct field in the data source to the column.
I will do this by means of another DataTemplate. As well as fixing up the Binding, I'll play with the text settings, so you can see how easy it is to create a different look for each column, if you need to. In this case, I've decided that I'll make all the data text Blue, but will make the detail of the Product Names stand out.
Here are the three DataTemplates:
<DataTemplate x:Key="IDCellTemplate">
<TextBlock Foreground="MediumBlue"
FontFamily="Calibri"
Text="{Binding Path=ProductID}" />
< SPAN>DataTemplate>
<DataTemplate x:Key="NameCellTemplate">
<TextBlock Foreground="DarkBlue" FontWeight="Bold"
Text="{Binding Path=ProductName}" />
< SPAN>DataTemplate>
<DataTemplate x:Key="PackCellTemplate">
<TextBlock Foreground="MediumBlue"
Text="{Binding Path=PackageType}" />
< SPAN>DataTemplate>
So, the final step is simply to point to these DataTemplates from the GridViewColumn's CellTemplate property. The ProductID column, for example:
CellTemplate="{StaticResource IDCellTemplate}"
The final complete markup for the ListView is as follows:
<ListView Name="ProductsListView"
ItemsSource="{Binding}"
Margin="5,25" >
<ListView.View>
<GridView>
<GridViewColumn
HeaderTemplate="{StaticResource IDColHeader}"
CellTemplate="{StaticResource IDCellTemplate}">
< SPAN>GridViewColumn>
<GridViewColumn
HeaderTemplate="{StaticResource NameColHeader}"
CellTemplate="{StaticResource NameCellTemplate}">
< SPAN>GridViewColumn>
<GridViewColumn
HeaderTemplate="{StaticResource PackageColHeader}"
CellTemplate="{StaticResource PackCellTemplate}">
< SPAN>GridViewColumn>
< SPAN>GridView>
< SPAN>ListView.View>
< SPAN>ListView>
And the finished look is:

This example only scratches the surface of the changes you can make to the look of the ListView. Some of them will take quite a lot more work (more than it should, you may think) and I will look at those in future blogs.
You may have noticed in the examples that by default the column widths are set to a size that allows the widest item to be displayed. Of course, the user can resize the column widths at run time and a useful trick to know is this: If the column width is too small for all the data to be read, double-clicking on the divider between the two columns will cause the column to the left to resize. All its data will then be visible again. It's a little weird that you have to click on the divider, not the column header, but it works just fine.
NB. Another thing to note: If you don't want to re-run the project after each change, sometimes you will need to Rebuild the solution to get the new look to appear in the Design Pane.