C# – properly resize wpf ListView’s last column

cexpression-blendlistviewwpfxaml

How to properly handle listview's last column sizing in such a way that:

  1. listview does not display the ugly and unused 'extra' column (see picture 1, or here for example)
  2. the (actual) last column is stretched to fill the remaining space
  3. the horizontal scrollbar is not shown
  4. resizing in any direction leaves no artefacts (see below)

I've been struggling with this for quite some time and it doesn't work out, nor did numerous searches on the web provide any insight. Here's what I have so far:

  1. I first tried adjusting the width of the last column to exactly fill the entire view, but then the horizontal scrollbar pops up. So now I set all column's ColumnHeaderContainerStyle to a transparent style that doesn't show anything, and override the HeaderContainerStyle on each Column with the actual style; for the last column, I made the margin negative so that it fills the extra empty space that originates from the extra column. See picture 2

  2. calculate last column width as difference between actualWidth and sum of widths of other columns taking into account the margin/padding

  3. same as for 2; width cannot be too small or else the horizontal scrollbar is shown

  4. this is introduced by 2: if the last column's width is set to a constant value, I can grow and shrink without problems. When using the code to stretch the column, the view properly stretches when growing, but when shrinking a part of the listview's rows disappear. The column header however stays more or less properly sized, but is still smaller then normal, see picture 3. This makes me think there's something special about shrinking: the calculation I do is the same, yet visually it's not. However I could not find where/how this happens in the framework.

Pictures:

Picture 1 http://deathwillendthiswar.dommel.be/default.jpg
Picture 2 http://deathwillendthiswar.dommel.be/start.jpg
Picture 3 http://deathwillendthiswar.dommel.be/afterdrag.jpg

Code for stretching, applied to last column's with in ListView resize handler

int StretchLastColWidth( ListView lv, int minWidth )
{
  double width = lv.ActualWidth;
  if( width <= 0 )
    return minWidth;

  GridView grdView = lv.View as GridView;
  for( int i = 0 ; i < grdView.Columns.Count - 1 ; ++i )
    width -= ( grdView.Columns[ i ].ActualWidth + 6 ); //padding?

  Decorator border = VisualTreeHelper.GetChild( lv, 0 ) as Decorator;
  ScrollViewer scroll = border.Child as ScrollViewer;
  if( scroll.ComputedVerticalScrollBarVisibility == System.Windows.Visibility.Visible )
    width -= 14; //my scrollbar is smaller than the system default..

  width -= 12  //comes from padding??

  if( width > minWidth )
    return Convert.ToInt32( width );
  else
    return minWidth;
}

Listview's xaml:

<ListView ItemContainerStyle="{StaticResource ListViewItemStyle}">
  <ListView.View>
    <GridView ColumnHeaderContainerStyle="{DynamicResource ListViewEmptyHeaderStyle}">
      <GridViewColumn Width="80" Header="Hdr0" HeaderContainerStyle="{DynamicResource ListViewHeaderStyle}"/>
      <GridViewColumn Header="Hdr1" HeaderContainerStyle="{DynamicResource ListViewLastHeaderStyle}"/>
    </GridView>
  </ListView.View>
</ListView>

If really needed I could post the entire template; note that although I've shown the pictures using a rather heavily my modified style, the results are exactly the same when using a standard ListView without modification. Should anyone want to try, use this style to see the border around the row the items:

<Style x:Key="ListViewItemStyle" TargetType ="{x:Type ListViewItem}">
  <Setter Property="BorderBrush" Value="White"/>
  <Setter Property="BorderThickness" Value="1"/>
  <Setter Property="Margin" Value="3,1"/> <!--if not used, border sticks to scrollbar etc-->
  <Setter Property="SnapsToDevicePixels" Value="true"/>
</Style>

Best Answer

Check out this CodeProject article:

ListView Layout Manager By Jani Giannoudis

Related Topic