Force resize of GridView columns inside ListView

12,065

Solution 1

Finally, some results on this one. I've found a way to do the same auto-sizing that is done initially and when the gripper on a column header is double clicked.

public void AutoSizeColumns()
{
    GridView gv = listView1.View as GridView;
    if (gv != null)
    {
        foreach (var c in gv.Columns)
        {
            // Code below was found in GridViewColumnHeader.OnGripperDoubleClicked() event handler (using Reflector)
            // i.e. it is the same code that is executed when the gripper is double clicked
            if (double.IsNaN(c.Width))
            {
                c.Width = c.ActualWidth;
            }
            c.Width = double.NaN;
        }
    }
}

Solution 2

Building on top of Oskars answer, here is a blend behavior to automatically size the columns when the content changes.

 /// <summary>
 /// A <see cref="Behavior{T}"/> implementation which will automatically resize the automatic columns of a <see cref="ListView">ListViews</see> <see cref="GridView"/> to the new content.
 /// </summary>
 public class GridViewColumnResizeBehaviour : Behavior<ListView>
 {
      /// <summary>
      /// Listens for when the <see cref="ItemsControl.Items"/> collection changes.
      /// </summary>
      protected override void OnAttached()
      {
          base.OnAttached();

          var listView = AssociatedObject;
          if (listView == null)
              return;

          AddHandler(listView.Items);
      }

      private void AddHandler(INotifyCollectionChanged sourceCollection)
      {
          Contract.Requires(sourceCollection != null);

          sourceCollection.CollectionChanged += OnListViewItemsCollectionChanged;
      }

      private void RemoveHandler(INotifyCollectionChanged sourceCollection)
      {
          Contract.Requires(sourceCollection != null);

          sourceCollection.CollectionChanged -= OnListViewItemsCollectionChanged;
      }

      private void OnListViewItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
      {
          var listView = AssociatedObject;
          if (listView == null)
              return;

          var gridView = listView.View as GridView;
          if (gridView == null)
              return;

          // If the column is automatically sized, change the column width to re-apply automatic width
          foreach (var column in gridView.Columns.Where(column => Double.IsNaN(column.Width)))
          {
               Contract.Assume(column != null);
               column.Width = column.ActualWidth;
               column.Width = Double.NaN;
          }
      }

      /// <summary>
      /// Stops listening for when the <see cref="ItemsControl.Items"/> collection changes.
      /// </summary>
      protected override void OnDetaching()
      {
          var listView = AssociatedObject;
          if (listView != null)
              RemoveHandler(listView.Items);

          base.OnDetaching();
      }
 }

Solution 3

Isn't there a way to bind to the ActualWidth of the column? Something like :

<GridViewColumn x:Name="column1" Width="{Binding ElementName=column1, Path=ActualWidth}" />

I have tried this and it works only the first time, it seems. No binding error.

Solution 4

If like me you are old and prefer VB.NET then here's Oskars code:

Public Sub AutoSizeColumns()
    Dim gv As GridView = TryCast(Me.listview1.View, GridView)
    If gv IsNot Nothing Then
        For Each c As GridViewColumn In gv.Columns
            If Double.IsNaN(c.Width) Then
                c.Width = c.ActualWidth
            End If
            c.Width = Double.NaN
        Next
    End If
End Sub

This works great in WPF, finally someone has worked this out. Thanks Oskar.

Share:
12,065

Related videos on Youtube

Dave Clemmer
Author by

Dave Clemmer

I enjoy coding like an excellent beer. My particular passion and experience lies in the realm of modeling and code generation. In the late 80s and early 90s, I was involved in early modeling and code generation tools that reached the marketplace, including a tool that modeled FORTRAN programs and generated FORTRAN for parallel supercomputer architectures, and a tool that managed Shlaer-Mellor models and generated C++ code. Over the years, I have applied Shlaer-Mellor, UML, and custom modeling and various code generation techniques to greatly benefit the development of enterprise applications. My current passion and endeavor is to foster the evolution of model oriented development. In particular, I am passionate about evolving the Mo+ model oriented programming language and related model driven development tools, with as much community input as possible to achieve higher levels in the ability to create and maintain code. The open source site is at moplus.codeplex.com, and the Mo+ membership site is at modelorientedplus.com.

Updated on April 17, 2022

Comments

  • Dave Clemmer
    Dave Clemmer about 2 years

    I have a ListView WPF control with a GridView. I'd like to resize the GridView columns when the content of the columns changes.

    I have several distinct data sets but when I change from one to another, the size of each columns fits the previous data. I'd like to update dynamically. How can I do that?

    • Drew Noakes
      Drew Noakes almost 15 years
      I have the same issue. Change the data source, and the column widths don't resize. I'd be happy to set the source and call an AutoSizeColumns method or similar.
  • Thia
    Thia almost 15 years
    I have also tried : Width="{Binding Path=ActualWidth, RelativeSource={x:Static RelativeSource.Self}}"
  • ygoe
    ygoe over 11 years
    Yeah, you copy ActualWidth over to Width, which is then the intended width forever. The auto-sizing is disabled at the first time. ActualWidth then never changes again, so the intended width won't either.
  • AH.
    AH. over 9 years
    Great solution. Thanks.
  • Danielku15
    Danielku15 about 9 years
    This behavior does not take into account that it might take some time until the new ActualWidth is calculated. Therefore it might happen that ActualWidth still is too small to fit the contents.
  • Lukazoid
    Lukazoid about 9 years
    @Danielku15 Are you finding that is happening? A small sample would be great so I can work towards fixing this problem.
  • Danielku15
    Danielku15 about 9 years
    I have a ListView bound to an ObservableCollection. I have a control in my CellTemplate that is 40x40pt. Attaching the Behavior executes the OnListViewItemsCollectionChanged method, but the ActualWidth is still too small (25pt in my case). The result: it's cropped. It seems the layoutprocess did not run when the handler is executed.
  • OhBeWise
    OhBeWise over 8 years
    Similar to @Danielku15 I have a ListViewBehavior class that implements the ActualWidth, Double.NaN change in the ListView.ItemsChanged event handler. When setting the bound source property to a new collection of items, the Auto columns are calculated but at some later unknown time (so I can't resize a Fill column & columns overflow the ListView width). Remove those lines and Auto columns are calculated when expected and fit the ListView width exactly, but they aren't calculated to the new content.