IanG on Tap

Ian Griffiths in Weblog Form (RSS 2.0)

Blog Navigation

April (2018)

(1 item)

August (2014)

(1 item)

July (2014)

(5 items)

April (2014)

(1 item)

March (2014)

(1 item)

January (2014)

(2 items)

November (2013)

(2 items)

July (2013)

(4 items)

April (2013)

(1 item)

February (2013)

(6 items)

September (2011)

(2 items)

November (2010)

(4 items)

September (2010)

(1 item)

August (2010)

(4 items)

July (2010)

(2 items)

September (2009)

(1 item)

June (2009)

(1 item)

April (2009)

(1 item)

November (2008)

(1 item)

October (2008)

(1 item)

September (2008)

(1 item)

July (2008)

(1 item)

June (2008)

(1 item)

May (2008)

(2 items)

April (2008)

(2 items)

March (2008)

(5 items)

January (2008)

(3 items)

December (2007)

(1 item)

November (2007)

(1 item)

October (2007)

(1 item)

September (2007)

(3 items)

August (2007)

(1 item)

July (2007)

(1 item)

June (2007)

(2 items)

May (2007)

(8 items)

April (2007)

(2 items)

March (2007)

(7 items)

February (2007)

(2 items)

January (2007)

(2 items)

November (2006)

(1 item)

October (2006)

(2 items)

September (2006)

(1 item)

June (2006)

(2 items)

May (2006)

(4 items)

April (2006)

(1 item)

March (2006)

(5 items)

January (2006)

(1 item)

December (2005)

(3 items)

November (2005)

(2 items)

October (2005)

(2 items)

September (2005)

(8 items)

August (2005)

(7 items)

June (2005)

(3 items)

May (2005)

(7 items)

April (2005)

(6 items)

March (2005)

(1 item)

February (2005)

(2 items)

January (2005)

(5 items)

December (2004)

(5 items)

November (2004)

(7 items)

October (2004)

(3 items)

September (2004)

(7 items)

August (2004)

(16 items)

July (2004)

(10 items)

June (2004)

(27 items)

May (2004)

(15 items)

April (2004)

(15 items)

March (2004)

(13 items)

February (2004)

(16 items)

January (2004)

(15 items)

Blog Home

RSS 2.0

Writing

Programming C# 5.0

Programming WPF

Other Sites

Interact Software

Filling ListView Columns in WPF

Wednesday 30 May, 2007, 06:00 PM

WPF’s ListView control left-aligns the content of columns by default. This can lead to slightly surprising results when you use a TextBox in one of the columns:

ListView with inconsistent TextBox sizes

What’s happening here is that the TextBox is being sized to content. (That’s what happens if you left-align an element in WPF.) It gets worse if your text boxes are initially empty—they start out very thin. And as you type into them, they expand. It’s hard to imagine why anyone might want that behaviour, so you’ll probably want to change it.

A simple but somewhat unsatisfactory fix is to set the Width of the TextBox. This lets you provide a more sensible initial width. It also gets rid of the size-to-content behaviour, which good because you usually don’t want that on a TextBox. The problem with this is that a fixed-width TextBox will not resize when the user resizes the column by dragging the header. Wouldn’t it be better if the TextBox simply took its width from the column header? That way, you can set a sensible initial width, and then allow the user to make the text boxes wider by resizing the column.

To do this, we need to make sure the list column contents have a horizontal alignment of Stretch, which means ‘make this element the same width as its container’. Throughout most of WPF, Stretch is the default, so it’s a little surprising that the ListView doesn’t follow suit. In fact it uses Left, which is a little irritating—the problem described here simply wouldn’t occur if ListView hadn’t used the normal default. Fortunately, we can change it. (So far I’ve worked out how to do this twice, and then promptly forgotten again on both occasions. I’m putting it in a blog so that next time I’ll remember, or at least I’ll know where to look.)

The alignment of the column content is controlled by the ListViewItem container. There will be one of these for each row in the ListView, which will generate them automatically if you don’t supply your own. In fact if you’re using data binding, you don’t have the option to supply your own. And since putting a TextBox column into a ListView probably only makes sense in data binding scenarios, chances are you’ll be using generated ListViewItem containers.

So it’s not as simple as setting properties on the ListViewItem containers—those containers don’t appear anywhere in your XAML because they are generated implicitly by WPF.

As with any control that derives from ItemsControl, ListView lets you set properties on the generated item containers setting the ItemContainerStyle property. We can use this to modify the HorizontalContentAlignment of the ListViewItem containers:

<Grid>
  <Grid.Resources>
    <x:Array Type="{x:Type s:String}" x:Key="items">
      <s:String>Foo</s:String>
      <s:String>Bar</s:String>
      <s:String>Spong</s:String>
    </x:Array>
  </Grid.Resources>

  <ListView ItemsSource="{StaticResource items}">

    <ListView.ItemContainerStyle>
      <Style TargetType="ListViewItem">
        <Setter Property="HorizontalContentAlignment"
                Value="Stretch" />
      </Style>
    </ListView.ItemContainerStyle>
    
    <ListView.View>
      <GridView>
        <GridViewColumn Header="Data" Width="80">
          <GridViewColumn.CellTemplate>
            <DataTemplate>
              <TextBox Text="{Binding .}"  />
            </DataTemplate>
          </GridViewColumn.CellTemplate>
        </GridViewColumn>
        <GridViewColumn Header="Length"
             DisplayMemberBinding="{Binding Length}" />
      </GridView>
    </ListView.View>
  </ListView>

</Grid>

As you can see, each TextBox now fills the column:

ListView with TextBoxes filling the whole column

These text boxes will now resize when the user resizes the column header.

(Note: this particular example isn’t terribly useful, because it binds directly to an array of strings. Strings are immutable, so editing the string causes WPF to generate a new string. Because arrays don’t generate change notifications, the Length column fails to update. In practice, you’d bind to properties of some object designed to be edited, so you wouldn’t see this problem. I’ve over-simplified the data binding here so as not to obscure the main point.)

The ListViewItem actually copies its horizontal alignment down to each individual column. This means there’s no way to control the alignment for individual columns. In fact that’s not really a problem, because if you set it to Stretch for everything, it’s easy enough to set a different alignment on the child content specified in the CellTemplate. Indeed, this is what makes the default choice of Left alignment so odd here: given a column alignment of Stretch, you can use Left, Right or Center in each column template as you see fit. Moreover, a TextBlock does the right thing without even trying: a TextBlock set to Stretch will align the text according to its TextAlignment property, so even though the TextBlock may nominally fill the entire column width, it’ll still look like it’s left aligned by default because the alignment of the text inside the TextBlock can be managed independently of the alignment of the TextBlock itself. So there really does seem to be no good reason for this default left-aligned setting imposed by the ListView—with that in play, you can’t do anything other than left-align the contents of the column. But once you’ve fixed that with the style shown above, everything works as you’d hope.

Note that while fixing the content alignment to Stretch gives you full control over each column’s alignment, you’ll still be left with some white space on either side of each column. The ListView adds a horizontal margin of 6 device independent pixels on either side of each column. This turns out to be much harder to remove. I’ll show how to do that in a future blog.

Copyright © 2002-2024, Interact Software Ltd. Content by Ian Griffiths. Please direct all Web site inquiries to webmaster@interact-sw.co.uk