Here is a solution that uses databinding and some converter magic to get the job done.
First, let's describe a simple ListView with some data and 3 columns.
<ListView>
<ListView.View>
<GridView>
<GridViewColumn Width="140" Header="Date" />
<GridViewColumn Width="140" Header="Day"
DisplayMemberBinding="{Binding DayOfWeek}" />
<GridViewColumn Width="140" Header="Year"
DisplayMemberBinding="{Binding Year}"/>
</GridView>
</ListView.View>
<sys:DateTime>1/2/3</sys:DateTime>
<sys:DateTime>4/5/6</sys:DateTime>
<sys:DateTime>7/8/9</sys:DateTime>
</ListView>
This will get us to where you are. Now, in order to have the last column grow and shrink based on the parents width, we need to do build a converter and hook it up. First, let's adjust the last column of the GridView to make the width dynamic.
<GridViewColumn Header="Year" DisplayMemberBinding="{Binding Year}">
<GridViewColumn.Width>
<MultiBinding Converter="{StaticResource lastColumnMaximizerConverter}">
<Binding Path="ActualWidth"
RelativeSource="{RelativeSource AncestorType=ListView}"/>
<Binding Path="View.Columns"
RelativeSource="{RelativeSource AncestorType=ListView}"/>
</MultiBinding>
</GridViewColumn.Width>
</GridViewColumn>
What we've done here is created a MultiBinding object, hooked up an IMultiValueConverter
, and described several parameters that we want to send into the IMultiValueConverter
implementation. The first parameter is the ActualWidth of the parent ListView. The second parameter is the View.Columns collection on the parent ListView. We now have everything we need to calculate the final width of the last column in our view.
Now we need to create an IMultiValueConverter implementation. I happen to have one right here.
public class WidthCalculationMultiConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType,
object parameter, CultureInfo culture)
{
// do some sort of calculation
double totalWindowWidth;
double otherColumnsTotalWidth = 0;
double.TryParse(values[0].ToString(), out totalWindowWidth);
var arrayOfColumns = values[1] as IList<GridViewColumn>;
for (int i = 0; i < arrayOfColumns.Count - 1; i++)
{
otherColumnsTotalWidth += arrayOfColumns[i].Width;
}
return (totalWindowWidth - otherColumnsTotalWidth) < 0 ?
0 : (totalWindowWidth - otherColumnsTotalWidth);
}
public object[] ConvertBack(object value, Type[] targetTypes,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Psst.. by the way, this is not the safest code. You'll want to spruce it up! It's just a demo and you know how demo code is! :)
Last of all, we need to instantiate our Converter instance in our XAML.
<Grid.Resources>
<Converters:WidthCalculationMultiConverter
x:Key="lastColumnMaximizerConverter"/>
</Grid.Resources>
So now we have a a converter that will figure out how wide we want to make the last column, based on the widths of the columns (not including the last one) in the ListView and a binding that will use that converter and send in the required parameters and get the "right answer" out and apply it to the last column's width.
If you've put this together right, you should now have a ListView in which the last column will always stretch to the maximum width of the parent ListView.
I hope that this gets you going but also helped you understand how we can do this without writing some code-behind and using more of the facilities provided by WPF.