How can I get a ListView GridViewColumn to fill the remaining space in my grid?
I was trying to achieve the same thing but then decided I would like my ListView columns to consume a percentage of the ListView instead, the result of this is all columns consuming a portion of space and all space being consumed in the ListView. You could set this up to have whatever percentage you like on the last column to directly achieve your 'fill remaining space on last column' goal.
I find this method fairly robust and reliable (even on resize!) so thought I might share.
I have four columns in my ListView for this example. All you need is to register the SizeChanged
event in your ListView with the below event handler:
private void ProductsListView_SizeChanged(object sender, SizeChangedEventArgs e)
{
ListView listView = sender as ListView;
GridView gView = listView.View as GridView;
var workingWidth = listView.ActualWidth - SystemParameters.VerticalScrollBarWidth; // take into account vertical scrollbar
var col1 = 0.50;
var col2 = 0.20;
var col3 = 0.15;
var col4 = 0.15;
gView.Columns[0].Width = workingWidth*col1;
gView.Columns[1].Width = workingWidth*col2;
gView.Columns[2].Width = workingWidth*col3;
gView.Columns[3].Width = workingWidth*col4;
}
The issue is the column width of a GridViewColumn is double, rather than a GridLength object, and there is no conversion in place to handle the *. Not sure if this is an oversight by the WPF team or not. You would think it should be supported.
Aside from the converter, the only other way I've seen it done is here: http://www.ontheblog.net/CMS/Default.aspx?tabid=36&EntryID=37.
Both are additional work that should not be required. I have found other "weird" things with the ListView and GridView combo so I quit using them. If I need a datagrid I use the 3rd party one we license, if I need a complex ListBox style menu, I just use a templated ListBox.
Came across this when looking into a similar problem, my issue was I wanted all columns to be 'Auto' expect the first, which would just fill in the extra space, so I expanded on GONeale's solution.
private void ListView_SizeChanged(object sender, SizeChangedEventArgs e)
{
ListView _ListView = sender as ListView;
GridView _GridView = _ListView.View as GridView;
var _ActualWidth = _ListView.ActualWidth - SystemParameters.VerticalScrollBarWidth;
for (Int32 i = 1; i < _GridView.Columns.Count; i++)
{
_ActualWidth = _ActualWidth - _GridView.Columns[i].ActualWidth;
}
_GridView.Columns[0].Width = _ActualWidth;
}
Then the XAML is simply:
...
<ListView.View>
<GridView>
<GridViewColumn Header="Title" />
<GridViewColumn Header="Artist" Width="Auto" />
<GridViewColumn Header="Album" Width="Auto" />
<GridViewColumn Header="Genre" Width="Auto" />
</GridView>
</ListView.View>
...
This code could also be used on more generically as number of columns isn't hard-coded and with a little tweaking you could probably make the 'fill column' definable through some sort of logic.
Hope it helps someone :)
The solution from David Hanson-Greville's OnTheBlog mentioned in another answer isn't available anymore, even though the blog still exists. I was able to find it on the Wayback Machine and with a few moderations, here it is:
The trick is that you set Stretch=true on your ListView and it will stretch the columns that do not have a width equally.
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
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;
}
///
/// 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);
}
///
/// 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 >= 10)
{
column.Width = newWidth - 10;
}
}
//Store the columns in the TAG property for later use.
listView.Tag = columns;
}
}
}
}
Then you just add the namespace to the XAML file:
xmlns:Extensions="clr-namespace:Demo.Extension.Properties"
and use it on your list view:
<ListView ItemsSource="{Binding Path=Items}" DisplayMemberPath="Name"
ScrollViewer.VerticalScrollBarVisibility="Auto"
Grid.Column="0" Margin="8" Extensions:ListViewColumns.Stretch="true">