Monday, February 06, 2012
 
   
 
Welcome to my site

First let me say thanks for stopping by my site. My name is David Hanson-Graville and I am a IT consultant working in the UK. Let me make it clear, I am passionate about technology and specifically .net and its various forms. I've programmed in a range of langages, but I can say, I am now at my happiest when coding with c#. I hope my blog is an enjoyable & educational read and please feel free to email me at David.Hanson@OnTheBlog.net if you have any questions. 

WPF: How to stretch columns in a ListView Minimize
Location: BlogsOnTheBlog    
Posted by: David Hanson Sun, 13 Apr 2008 21:21:11 GMT
The ListView control in WPF is a powerful option when trying to present tabular data to users. It supports many of the common behaviours found in grid controls as well as the full WFP templating architecture we have all come to love. Below is a simple of example of a ListView which has been bound to a collection of strings. The example uses a template GridVewColumn and a 3 standard GridViewColumn’s to display the data.
 

And here is the associated XAML.
 
<Window x:Class="WPFSamples.ListViewDemo"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:s="clr-namespace:System;assembly=mscorlib"
    xmlns:Extensions="clr-namespace:Demo.Extension.Properties"
    Title="ListViewDemo" Height="353" Width="714">
    <Grid>
        <Grid.Resources>
           
            <x:Array Type="{x:Type s:String}" x:Key="ourData">
                <s:String>Scott Gus:String>
                <s:String>Tim Sneaths:String>
                <s:String>Rockford Lhotkas:String>
                <s:String>Robert Scobles:String>
                <s:String>Ayende Rahiens:String>
            x:Array>
        Grid.Resources>
       
        <ListView Name="myListView"
                  Background="LightBlue"
                  ItemsSource="{StaticResource ourData}">
            <ListView.View>
                <GridView>
                   
                    <GridViewColumn Header="Names" >
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <Border>
                                    <TextBlock Text="{Binding .}"
                                               Foreground="Black"
                                               FontWeight="Bold" />
                                Border>
                            DataTemplate>
                        GridViewColumn.CellTemplate>
                    GridViewColumn>
                   
                    <GridViewColumn Header="Length"
                                    Width="50"  DisplayMemberBinding="{Binding Length}" />
                    <GridViewColumn Header="Names"  DisplayMemberBinding="{Binding}" />
                    <GridViewColumn Header="Length"
                                    Width="50" DisplayMemberBinding="{Binding Length}" />
                GridView>
            ListView.View>
        ListView>
    Grid>
Window>
 
 
As you can see, I have highlighted the column’s in our GridView that have explicit width settings.  Now looking at what we get when we run this sample you can see that the default behaviour of the GridViewColumn is to set the width to AUTO thus fitting the content being displayed.  However, there often situations where you would rather the GridViewColum’s stretch to fit the remaining available space of its container. To achieve this you would imagine you could set the width of the GridViewColumn to “*”. Unfortunately this is not supported and you will be prompted with the following message.
 
<GridViewColumn Header="Names" Width="*">
    <GridViewColumn.CellTemplate>
        <DataTemplate>
            <Border>
                <TextBlock Text="{Binding .}"
                           Foreground="Black"
                           FontWeight="Bold" />
            Border>
        DataTemplate>
    GridViewColumn.CellTemplate>
GridViewColumn>
 
Cannot convert string '*' in attribute 'Width' to object of type 'System.Double'. '*'
 
I find this inconsistent use of the * in WPF frustrating, I am sure the WPF team have a valid reason for why this is not supported but it just feels like it should be. As the * is not supported on a GridViewColumn’s width I tried many other way to get our GridViewColumn to stretch, all to no avail.
 
<ListView HorizontalAlignment="Stretch" ... Makes no difference.
<ListView HorizontalContentAlignment="Stretch" ... Makes no difference.
<GridViewColumn HorizontalAlignment="Stretch" ... Not supported.
<GridViewColumn HorizontalContentAlignment="Stretch" ... Not supported.
 
Having played around for a while and running a few google searches it became apparent that a number of people have come across this issue, particularly when trying to stretch the last column to the remaining space. As a result, I decided to solve the problem using the attached property approach. This provides a simple way if implementing the stretch column behaviour on ListView’s. As I will outline, you change the default behaviour of the GridView setting the width to Auto so that be default all columns will stretch. Below is the XAML that will allow us to do just that.
 
<ListView Name="myListView"
          Background="LightBlue"
          ItemsSource="{StaticResource ourData}"
          Extensions:ListViewColumns.Stretch="true">
 
 
For us to achieve this level of simplicity in our XAML we have to place the complexity elsewhere.....in our attached property infact. The first stage when creating a new WPF attached property is to put the appropriate infrastructure in place. You will find may examples of how to create attached properties in WPF so I won’t bore you with the details here. Below is base code for our attached property.
 
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
 
namespace Demo.Extension.Properties
{
    ///
    /// ListViewColumnStretch
    ///
    public class ListViewColumns : DependencyObject
    {
 
        ///
        /// IsStretched Dependancy property which can be attached to gridview columns.
        ///
        public static readonly DependencyProperty StretchProperty =
            DependencyProperty.RegisterAttached("Stretch",
            typeof(bool),
            typeof(ListViewColumns),
            new UIPropertyMetadata(true,null,OnCoerceStretch));
 
 
        ///
        /// Gets the stretch.
        ///
        /// The obj.
        ///
        public static bool GetStretch(DependencyObject obj)
        {
            return (bool)obj.GetValue(StretchProperty);
        }
 
        ///
        /// Sets the stretch.
        ///
        /// The obj.
        /// if set to true [value].
        public static void SetStretch(DependencyObject obj, bool value)
        {
            obj.SetValue(StretchProperty, value);
        }
 
        ///
        /// Called when [coerce stretch].
        ///
        ///If this callback seems unfamilar then please read
        /// the great blog post by Paul jackson found here.
        /// http://compilewith.net/2007/08/wpf-dependency-properties.html
        /// The source.
        /// The value.
        ///
        public static object OnCoerceStretch(DependencyObject source, object value)
        {
            ListView lv = (source as ListView);
 
            //Ensure we dont have an invalid dependancy object of type ListView.
            if (lv == null)
                throw new ArgumentException("This property may only be used on ListViews");
 
            //Setup our event handlers for this list view.
            lv.Loaded += new RoutedEventHandler(lv_Loaded);
            lv.SizeChanged += new SizeChangedEventHandler(lv_SizeChanged);
            return value;
        }
 
    }
}
 
 
Firstly, notice that dependency property inherits from DependencyObject, just about most elements in WPF derive from this type and attached properties are no exception. The remainder of the code above is fairly self explanatory, we have WPF setters and getters and we have registered the property name (DependencyProperty.RegisterAttached("Stretch") as part of this work. The only part that may not be recognised is the static method OnCoerceStretch. This method is actually just a callback that will be called prior to our property being set. It allows us to intercept the setter in order to change the the state of the value if so require.
 
public static object OnCoerceStretch(DependencyObject source, object value)
{
    ListView lv = (source as ListView);
 
    //Ensure we dont have an invalid dependancy object of type ListView.
    if (lv == null)
        throw new ArgumentException("This property may only be used on ListViews");
 
    //Setup our event handlers for this list view.
    lv.Loaded += new RoutedEventHandler(lv_Loaded);
    lv.SizeChanged += new SizeChangedEventHandler(lv_SizeChanged);
    return value;
}
 
In our coerce method above you can see we receive the dependency object the property has been attached to (in our case a listview) and then the value being set. The only this we really need to do in our method is to attach a couple of event handlers which will notify us later on about state changes in our listview.
 
    //Setup our event handlers for this list view.
    lv.Loaded += new RoutedEventHandler(lv_Loaded);
    lv.SizeChanged += new SizeChangedEventHandler(lv_SizeChanged);
 
The loaded handler is setup as we can only gain access to our listview’s internal GridView and columns once the listview has fully loaded. The SizeChanged event handler will be used to monitor when the ListView’s sizes changes, when this happens we want to be able to perform a calculation so that columns with no width having been set can stretch to fill the available space. Implementing the dependency property above allows us to use the following XAML syntax. Extensions:ListViewColumns.Stretch="true". Don’t forget to import the namespace into your XAML like this xmlns:Extensions="clr-namespace:Demo.Extension.Properties".
 
Back to our dependency property, the event handlers are once again fairly self explanatory, each event handler calls into a method called SetColumnWidths. As the SizeChanged event is called prior to the loaded event a simple check is made to ensure SetColumnWidths is not called at this point.
 
///
/// Handles the SizeChanged event of the lv control.
///
/// The source of the event.
/// The instance containing the event data.
private static void lv_SizeChanged(object sender, SizeChangedEventArgs e)
{
    ListView lv = (sender as ListView);
    if (lv.IsLoaded)
    {
        //Set our initial widths.
        SetColumnWidths(lv);
    }
}
 
///
/// Handles the Loaded event of the lv control.
///
/// The source of the event.
/// The instance containing the event data.
private static void lv_Loaded(object sender, RoutedEventArgs e)
{
    ListView lv = (sender as ListView);
    //Set our initial widths.
    SetColumnWidths(lv);
}
 
 
The final part of the puzzle is too look at our method SetColumnsWidth. This method does the majority of the work in our dependency property, it identifies columns in our GridView which do not have a width setting and then based on the available space remaining divides to those columns. The only interesting part is that the columns identified as having no width are stored in the ListView’s TAG property for reuse later.
 
///
/// Sets the column widths.
///
private static void SetColumnWidths(ListView listView)
{
    //Pull the stretch columns fromt the tag property.
    List<GridViewColumn> columns = (listView.Tag as List<GridViewColumn>);
    double specifiedWidth = 0;
    GridView gridView = listView.View as GridView;
    if (gridView != null)
    {
        if (columns == null)
        {
            //Instance if its our first run.
            columns = new List<GridViewColumn>();
            // Get all columns with no width having been set.
            foreach (GridViewColumn column in gridView.Columns)
            {
                if (!(column.Width >= 0))
                    columns.Add(column);
                else specifiedWidth += column.ActualWidth;
            }
        }
        else
        {
            // Get all columns with no width having been set.
            foreach (GridViewColumn column in gridView.Columns)
                if (!columns.Contains(column))
                    specifiedWidth += column.ActualWidth;
        }
 
        // Allocate remaining space equally.
        foreach (GridViewColumn column in columns)
        {
            double newWidth = (listView.ActualWidth - specifiedWidth) / columns.Count;
            if (newWidth >= 0) column.Width = newWidth - 10;
        }
 
        //Store the columns in the TAG property for later use.
        listView.Tag = columns;
    }
}
 
And that’s pretty much it..... were done! If we run the sample now it will look like this. You can see the two [Name] columns are stretching to fill the available space. 
 
 
 
 
 
 Sourcecode for this blog can be downloaded here.
 
 
 
 
 
 
 
Permalink |  Trackback

Comments (16)   Add Comment
Re: WPF: How to stretch columns in a ListView    By Paul on Tue, 15 Apr 2008 18:54:28 GMT
This is a very timely for me. I only change ListView to GridViewHeaderRowPresenter because I use GridViewHeaderRowPresenter object without ListView.<br>Thank you very match.

Re: WPF: How to stretch columns in a ListView    By host on Tue, 15 Apr 2008 18:45:52 GMT
Glad it helped. :-)

Re: WPF: How to stretch columns in a ListView    By Simeon on Fri, 02 May 2008 10:57:05 GMT
Thank you! It works fine!

Re: WPF: How to stretch columns in a ListView    By host on Fri, 02 May 2008 10:58:05 GMT
Welcome.

Re: WPF: How to stretch columns in a ListView    By Phil on Mon, 14 Jul 2008 20:32:11 GMT
This is a little old, but i have a problem with the code where if you turn the stretch on, it no longer allows users to resize columns. If they try, it'll just snap right back to a stretch. Is there a way around this?

Re: WPF: How to stretch columns in a ListView    By John Melville on Sun, 30 Nov 2008 22:33:04 GMT
Thanks a lot. This post was really helpful. Didn't end up using you code (I'm actually using a TreeListView, out of the samples) but your clear code made extracting the principles and recoding them trivial.<br>

Re: WPF: How to stretch columns in a ListView    By Tim Orr on Tue, 26 Jan 2010 10:12:57 GMT
Great tutorial. FYI -- the line of code below will throw an exception the result is < 0:<br> <br>if (newWidth >= 0) column.Width = newWidth - 10;<br><br>Instead, it probably makes sense to do:<br><br><br>double newWidth = (listView.ActualWidth - specifiedWidth) / columns.Count - 10;<br><br>Thanks again! Great tutorial!

Re: WPF: How to stretch columns in a ListView    By stean on Tue, 26 Jan 2010 10:12:48 GMT
The problem ist that you are setting the ListView.Tag property. If anyone overwrites it it won't work

Re: WPF: How to stretch columns in a ListView    By John on Tue, 26 Jan 2010 10:13:00 GMT
Thank YOU!!!!!!!

Re: WPF: How to stretch columns in a ListView    By Kurt on Thu, 04 Feb 2010 11:01:05 GMT
Works unless vertical scroll bar is enabled. i think you need to check for the presence of the vertical scroll bar in order to factor in the width.

Re: WPF: How to stretch columns in a ListView    By Berci on Fri, 26 Feb 2010 09:54:40 GMT
Dealing with vertical scroll bar can be implemented like this:<br>look for the code calculating column widths (method SetColumnWidths()), and change calculating newWidth as follows (3 new line and one modified):<br><br>Decorator border = VisualTreeHelper.GetChild(listView, 0) as Decorator;<br>ScrollViewer scrollViewer = border.Child as ScrollViewer;<br>double scrollBarWidth = scrollViewer.ScrollableHeight > 0 ? SystemParameters.VerticalScrollBarWidth : 0;<br>double newWidth = (listView.ActualWidth - scrollBarWidth - specifiedWidth) / columns.Count;<br><br>Hope this helps.

Re: WPF: How to stretch columns in a ListView    By David on Sat, 14 May 2011 13:11:07 GMT
Great Stuff.. Thanks for saving the day!

Re: WPF: How to stretch columns in a ListView    By cooldrl on Sat, 14 May 2011 13:11:27 GMT
It is possible, especially if there are a large number of columns, that some columns may already have a content wider than that which neWidth will calculate to. Hence I reccommend:-<br><br>(newWidth >= 0 && newWidth > column.ActualWidth) column.Width = newWidth - columns.count;

Modification to use "star" notation for listview column widths    By Benny on Sat, 14 May 2011 13:11:46 GMT
Good idea, thanks! <br><br>I found this when I was looking for a way to do _relative_ scaling on a listview columns width property. The concept you use works for this, but I made some modifications. For those who are interested:<br><br>If you use this, the columns will scale relatively (as they do in the star notation: Assuming 2 columns you can use Width=1 and Width=2 on them and the will take up relatively 1/3 and 2/3 of the listview width.<br><br>Also, I removed the code to store the columns in a tag, you don't really need that and there's a chance someone will overwrite the tag property. Here's changed code in SetColumnWidths:<br><br>private static void SetColumnWidths( ListView listView )<br> {<br> double specifiedTotalWidth = 0;<br> GridView gridView = listView.View as GridView;<br> if( gridView != null )<br> {<br> // get total width<br> foreach( GridViewColumn column in gridView.Columns )<br> {<br> specifiedTotalWidth += column.Width;<br> }<br><br> if( specifiedTotalWidth > 0 && listView.ActualWidth > 0 )<br> {<br> // Allocate remaining space equally.<br> foreach( GridViewColumn column in gridView.Columns )<br> {<br> column.Width = listView.ActualWidth / specifiedTotalWidth * column.Width;<br> }<br> }<br> }<br><br>(of course, I also changed some of the naming to match the change in behavior, but that should be obvious).

Re: WPF: How to stretch columns in a ListView    By Andrew on Sat, 14 May 2011 13:12:20 GMT
Thanks very much - a really handy bit of code. Hope your world tour is going well.

Re: WPF: How to stretch columns in a ListView    By Dylan on Sat, 14 May 2011 13:12:40 GMT
Nice one! <br>Kudos.


Your name:
Title:
Comment:
Security Code
Enter the code shown above in the box below
Add Comment   Cancel 
Tweets Minimize
Twitter / LordHanson
  1. LordHanson: #southeastern have once again proved their rolling stock can reach new lows.

    Published Wed, 01 Feb 2012 08:09:05 +0000 by
  2. LordHanson: Checked in at The Cards http://t.co/LANfHukb

    Published Sun, 29 Jan 2012 11:04:51 +0000 by
  3. LordHanson: @BillGates Run for president Bill.

    Published Fri, 27 Jan 2012 08:41:35 +0000 by
  4. LordHanson: @swhelband Again!

    Published Fri, 27 Jan 2012 08:39:44 +0000 by
  5. LordHanson: The update for the Nokia Lumia recently has done wonders for battery life! Good job #Nokia #windowsphone

    Published Fri, 27 Jan 2012 08:36:27 +0000 by
  6. LordHanson: Checked in at Victoria Station http://t.co/L0BJ5smd

    Published Thu, 26 Jan 2012 08:35:41 +0000 by
  7. LordHanson: @pdl_uk VAT No: 924 5933 08 Shoulder again! Dang!

    Published Wed, 25 Jan 2012 21:47:45 +0000 by
  8. LordHanson: @tommyjmquinn I think that would be easier. Next Thursday ok?

    Published Wed, 25 Jan 2012 21:04:46 +0000 by
  9. LordHanson: @tommyjmquinn London bridge doable?

    Published Wed, 25 Jan 2012 20:32:34 +0000 by
  10. LordHanson: @tommyjmquinn so where's the meet?

    Published Wed, 25 Jan 2012 19:04:02 +0000 by
  11. LordHanson: @tommyjmquinn your choice mate. Somewhere easy to get to from Bankside. :-D

    Published Tue, 24 Jan 2012 22:01:20 +0000 by
  12. LordHanson: @tommyjmquinn so Darius, Justin and me confirmed. Thursday good for you? Waiting to hear from Sal.

    Published Tue, 24 Jan 2012 21:47:21 +0000 by
  13. LordHanson: @mark_mann which is illegal I thought!

    Published Tue, 24 Jan 2012 21:46:17 +0000 by
  14. LordHanson: Details on Windows Phone 8 confirms NT kernel http://t.co/5Qg1MILl

    Published Tue, 24 Jan 2012 21:34:11 +0000 by
  15. LordHanson: But next target for framework is #winrt. Need to see if my dependencies like DI, RX, ReactiveUi etc will work. Hmm

    Published Mon, 23 Jan 2012 08:33:16 +0000 by
  16. LordHanson: @pdl_uk hey Phil, how's marathon training going?

    Published Mon, 23 Jan 2012 08:31:37 +0000 by
  17. LordHanson: So I now have a framework for apps which targets .net, Silverlight and windows phone. Thankyou project linker!

    Published Mon, 23 Jan 2012 08:28:08 +0000 by
  18. LordHanson: For some reason dropped twitter for a month. Can't say I missed it really. Maybe I need to broaden my follow list!

    Published Mon, 23 Jan 2012 08:24:44 +0000 by
  19. LordHanson: Soo much hype over SIRI when it came out yet I never see anyone use it and don't know anybody who does either. #apple #sooverhyped

    Published Mon, 23 Jan 2012 08:23:18 +0000 by
  20. LordHanson: #southeastern customer satisfaction survey given to me today. This should be fun! Bit wait......no extremely poor option! Just very poor.

    Published Mon, 23 Jan 2012 08:20:09 +0000 by
Print  
Archive Minimize
Print  
Contact me Minimize
Print  
Microsoft Certs Minimize







Print  
Silverlight News Minimize
Silverlight - Google News
  1. Windows Phone 8 - Silverlight Apps Are Legacy - iProgrammer

    Published Fri, 03 Feb 2012 13:03:27 GMT by
  2. Super Bowl Streaming Fail - StreamingMedia.com

    Published Mon, 06 Feb 2012 05:59:33 GMT by
  3. Delphi Developers Rejoice at Silverlight, FireMonkey and VCL Coming Together ... - San Francisco Chronicle (press release)

    Published Tue, 31 Jan 2012 17:34:58 GMT by
  4. WP7 Upgrades and WP8 - Silverlight or C++ - iProgrammer

    Published Tue, 31 Jan 2012 17:21:58 GMT by
  5. Watch The 2012 Super Bowl Online - SportsGrid

    Published Sun, 05 Feb 2012 23:15:21 GMT by
  6. US viewers can watch Super Bowl on Mac, iOS - Macworld

    Published Sun, 05 Feb 2012 20:22:31 GMT by
  7. Hydra 4 Sharpens Its Teeth, Breathes New Fire - Dr. Dobb's

    Published Sun, 05 Feb 2012 17:25:01 GMT by
  8. Will Silverlight live or die? Microsoft won't say - InfoWorld

    Published Fri, 27 Jan 2012 11:00:46 GMT by
  9. Cablevision Flips Live TV To Laptops With Microsoft Silverlight - Multichannel News

    Published Fri, 27 Jan 2012 17:24:53 GMT by
  10. Do SharePoint & Silverlight Have a Future Together? - CMSWire

    Published Mon, 16 Jan 2012 17:29:27 GMT by
Print