WPF: ListView/GridView minimum and maximum width for a column

A WPF ListView is a possible replacement for a datagrid, when it doesn't have to support advanced features like grouping or filtering of rows. If you use a ListView in your WPF application, and set the "View" property of this ListView to be a GridView.

A ListView/GridView even supports features like reordering and resizing columns. Unfortunately, it is not possible to define a minimum or maximum width for the columns. Fortunately, it is not really to do this in the code behind. Let's see how.

First we need to understand how exactly the resizing of a GridView column works. When a column can be resized, a narrow vertical line appears on the right side of the column. Passing the mouse over this line turns the cursor into a "resize" cursor.

To find out what exactly this line is made of, we can use a tool like Snoop, or Mole. These tools display the inner structure of a WPF UI, also known as the Visual Tree. For example, in Mole, we can navigate down to our ListView and display the header row's inner structure:

Header row of a ListView

The feature that is of particular interest to us is the "Thumb" control, named PART_HeaderGripper. This requires some explanations:

  • A Drag action is the action through which a control is selected (click-and-hold) and then the mouse is moved. The expected action is usually that the clicked control moves together with the mouse, but other actions may be performed too, for example resizing the columns of a grid, etc...
  • A Thumb is a small WPF control that can be dragged. It has a collection of events that are used to program what happens when a drag action is performed on the control. For example, the event Thumb.DragDeltaEvent is fired every time that the Thumb is dragged a little.
    Note: A Thumb doesn't really look like anything (well, it does look like a Rectangle!). With the great ability that WPF has to redefine the look&feel of any control, you can make a Thumb look like anything you need. This is why the Thumb control is in the namespace System.Windows.Controls.Primitives: The Thumb is usually used to create other controls, hence the "Primitives".
  • The makers of WPF controls have for convention to name inner controls of other controls with the "PART_" prefix.

350-030 and 220-602 is very easy for those students who have written 70-640. Many 640-822 and 70-236 professionals also find them tough without the latter credit.

Once we know this, we can try to intercept the events raised by the Thumb control named "PART_HeaderGripper", when the ListView's user resizes the columns. Remember, in WPF, events are "routed", we say that they "bubble" up from the control they originate from to the parent and the parent's parent, etc...

As a first try, we can add an event handler on the ListView and try to handle the "Thumb.DragDelta" event:

<ListView x:Name="MyListView" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding Path=Items, Mode=Default, Source={StaticResource DataProvider}}" Thumb.DragDelta="Thumb_DragDelta">

with:

void Thumb_DragDelta(object sender, DragDeltaEventArgs e) { // Do nothing for the moment Console.WriteLine("Thumb_DragDelta"); }

Unfortunately, if we place a breakpoint inside the Thumb_DragDelta event handler, it is never reached, not even if you resize any column of the ListView. Why not? Well, when a routed event is handled by any control, it can be marked as "handled". This is done by setting the RoutedEventArgs.Handled property to true inside the event handler.

Fortunately, there is a way to tell the framework that we are interested in an event even if it has been marked as handled. We cannot do this in XAML, however, so just remove the Thumb.DragDelta from the XAML:

<ListView x:Name="MyListView" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding Path=Items, Mode=Default, Source={StaticResource DataProvider}}">

Instead, we will add an event handler in code behind. This is one of the few things that you cannot do in XAML!

public Window1() { InitializeComponent(); MyListView.AddHandler(Thumb.DragDeltaEvent, new DragDeltaEventHandler(Thumb_DragDelta), true); }

Notice that the last parameter of the AddHandler override is set to "true": This is what indicates that we want to get notified when the DragDelta event "bubbles" to us, even if it has been marked handled before it reaches us. This time, if you run the application and resize a column, the event handler is executed and the breakpoint is reached.

The last thing we need to do is to forbid the column to be resized under a minimum size, or above a maximum size. To do this, we need to do a few steps:

  • Get the Thumb at the origin of the event. This is not the "sender" parameter, because the event handler is placed on the ListView. So even though the routed event originates on the Thumb, the "sender" is actually the ListView. However, there is another way to get the Thumb: The parameter "e" (of type DragDeltaEventArgs), like all RoutedEventArgs, contains a property named "OriginalSource". This is the Thumb we want!
  • Then, we want to get the GridViewColumnHeader containing the Thumb. Here too, we need to understand how the visual tree is built. All controls in WPF are essentially lookless, and the look&feel is created in a ControlTemplate, which is separate. We can, however, get the parent's template using the TemplatedParent property.
  • We're almost there: Once we have the GridViewColumnHeader, all we need to do is get the Column it belongs to (conveniently exposed in the Column property), and set its Width to the minimum respectively maximum value we want to use. Translated in code, this is:
void Thumb_DragDelta(object sender, DragDeltaEventArgs e) { Thumb senderAsThumb = e.OriginalSource as Thumb; GridViewColumnHeader header = senderAsThumb.TemplatedParent as GridViewColumnHeader; if (header.Column.ActualWidth < MIN_WIDTH) { header.Column.Width = MIN_WIDTH; } if (header.Column.ActualWidth > MAX_WIDTH) { header.Column.Width = MAX_WIDTH; } }

where MIN_WIDTH and MAX_WIDTH are two constants. We could as well use properties to be able to set these values from the outside.

With this code, the column will be resizable up to a certain limit, and then the Thumb will stop moving. While it requires a good understanding of the inner works of a WPF control, this is not very complicated. It requires some code-behind, however, and cannot be done in pure XAML.

http://www.galasoft.ch

Print | posted on Tuesday, May 6, 2008 7:05 PM

Feedback

# re: WPF: ListView/GridView minimum and maximum width for a column

left by Laurent at 5/6/2008 9:55 PM Gravatar
Cool idea. Let me see if I manage that later. Thanks for the idea.

# re: WPF: ListView/GridView minimum and maximum width for a column

left by Mike Brown at 5/7/2008 12:32 AM Gravatar
I beat you to the punch Laurent ;)

# re: WPF: ListView/GridView minimum and maximum width for a column

left by Laurent at 5/7/2008 9:51 AM Gravatar
Argh can't believe that. Mike, you beat me by one hour max. And this only because I was busy with something else for a moment!!!

Folks, Mike's Attached Property solution is at
http://mbrownchicago.spaces.live.com/blog/cns!2221DC39E0C749A4!707.entry

# re: WPF: ListView/GridView minimum and maximum width for a column

left by Mike Brown at 5/8/2008 2:20 AM Gravatar
Sorry...I've been itching to write something for a while. I guess this little puzzle was just what I needed to get the juices flowing...

# re: WPF: ListView/GridView minimum and maximum width for a column

left by M.S. Feroz at 9/17/2008 7:36 AM Gravatar
Thanks For your article. Excellent one. I got something more than what i expected. Really a wonderful article

# re: WPF: ListView/GridView minimum and maximum width for a column

left by chanti at 11/5/2008 1:49 PM Gravatar
really gud idea....it helped me alot...
but any empty space column is coming at right extreme column after resize it

# re: WPF: ListView/GridView minimum and maximum width for a column

left by Prajeesh Prabhakar at 5/15/2009 2:50 PM Gravatar
This is very useful. Have a nice idea. Thanks for sharing. Continue to explore these ideas.

# re: WPF: ListView/GridView minimum and maximum width for a column

left by ravi at 7/27/2009 6:07 AM Gravatar
I wnat to maintain disable the resize propert.can u help me?
Thanks

# re: WPF: ListView/GridView minimum and maximum width for a column

left by ronak at 8/1/2009 12:23 AM Gravatar
very good one

# re: WPF: ListView/GridView minimum and maximum width for a column

left by Vivek at 7/23/2010 3:40 AM Gravatar
Good article!! You have given a very good detail explanation of the problem and it's solution.

It's helpful.
Thanks,
Vivek

# re: WPF: ListView/GridView minimum and maximum width for a column

left by Hami at 9/15/2010 5:19 PM Gravatar
It would be more elegant if you used 'header.DesiredSize' to determine the best MinWidth instead of the constant.

# re: WPF: ListView/GridView minimum and maximum width for a column

left by yudiyana at 9/15/2010 5:32 PM Gravatar
Thanks for your great article and explanation. It help me much :)

# re: WPF: ListView/GridView minimum and maximum width for a column

left by Todd at 2/28/2011 9:39 PM Gravatar
There is one problem with this approach. Double-clicking the column header "thumb" will cause the column to shrink without firing the Thumb_DragDelta event.

# re: WPF: ListView/GridView minimum and maximum width for a column

left by tom at 5/9/2011 1:09 AM Gravatar
Any idea how to solve the problem with Double-clicking that Todd mentioned?

# re: WPF: ListView/GridView minimum and maximum width for a column

left by Dementev Pavel at 7/18/2011 1:20 PM Gravatar
Thanks you from Russia

# re: WPF: ListView/GridView minimum and maximum width for a column

left by Iftikhar at 1/12/2012 11:08 AM Gravatar
That works like a charm. It really saved my time.
Keep rocking.

# re: WPF: ListView/GridView minimum and maximum width for a column

left by A.L at 8/24/2012 11:17 PM Gravatar
Gracias amigo muchas gracias no sabes de la que me has sacado
Comments have been closed on this topic.