Welcome to Pete Brown's 10rem.net

First time here? If you are a developer or are interested in Microsoft tools and technology, please consider subscribing to the latest posts.

You may also be interested in my blog archives, the articles section, or some of my lab projects such as the C64 emulator written in Silverlight.

(hide this)

Silverlight and WPF Tip: Fitting items in a ListBox

Pete Brown - 18 April 2010

One layout issue many folks run into in WPF and Silverlight is the problem of ListBox content not automatically sizing to the width of the ListBox.

Using my WPF Twitter Application video as an example, here's a shot with it with the items not fitting correctly. Compare to the second screen shot, and you can see the text has scrolled off the right of the ListBox.

image

Here's a shot with the ListBox fixed, and the items all fit in place. Notice that the text has wrapped and there is no horizontal scrollbar.

image

Luckily, the solution is pretty simple: You just need to do four things, but because it is a combination of these things, the solution often eludes folks:

  1. Use a grid as the root element of your ItemTemplate/DataTemplate
  2. Tell the ListBox to disable horizontal scrolling.
  3. Wrap or clip any text
  4. Use * sizing, not Auto sizing for variable width columns

Here's the updated Xaml. Note the highlighted lines.

<ListBox Margin="12,83,12,12"
            Name="listBox1"
            ScrollViewer.HorizontalScrollBarVisibility="Disabled"
            ItemsSource="{Binding Path=Tweets}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <!-- In the video, this is in a UserControl. That's still ok to do. -->
            <Grid Margin="10">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="*" />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>
                        
                <Image Source="{Binding Path=FromImage}"
                        Height="70"
                        VerticalAlignment="Top"
                        Grid.RowSpan="2" />
                <TextBlock Text="{Binding Path=From}"
                            Grid.Column="1" />
                <TextBlock Text="{Binding Path=Text}"
                            TextWrapping="Wrap"
                            Grid.Column="1"
                            Grid.Row="1" />
            </Grid>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Here's an explanation of why:

1. Use a Grid

The Grid is the most powerful and flexble of the standard panel controls. It also has provisions to ensure that it stays at a width which does not exceed what it has been allowed. If you use another panel, they may stretch their content to the right and otherwise not behave well in a listbox.

2. Disable Horizontal Scrolling

When horizontal scrolling is enabled, the listbox allows the content to be any width. When disabled, the listbox constrains the items width to be the visible width on-screen.

3. Wrap or Trim, or Clip Text

When #1 and #2 are in place, but you do not wrap text, the text will simply be truncated. If you trim it (using TextTrimming) or clip it (using the default clip or your own), the text will take up only the amount of space you want it to. This isn't required to get layout to work the way you want, but it is required to get the result to look the way you want.

4. Use Star Sizing instead of Auto Sizing for Width

If you set the width of your grid columns to be "Auto" for the bits that are pushing out the horizontal size of the template, Silverlight and WPF will gladly give your grid all the room it needs. "Auto" means, "give me what I want". If you set it to "*" (Star), the grid will give your element the room that is available. If you set the variable-width column to a fixed number, you will always get that size, even if the ListBox doesn't really have it. Always set the columns containing variable-width content to have a Width of * or a multiple of * like 2* if your layout is more complex. The other items can have Auto or fixed (a number) width.

 

That's all there is to it. This will solve 90% of the ListBox sizing issues out there. If it's not working for you, start dismantling your DataTemplate element-by-element and see if you've got something in there that's forcing the list box item to be a wider width.

       
posted by Pete Brown on Sunday, April 18, 2010
filed under:        

19 comments for “Silverlight and WPF Tip: Fitting items in a ListBox”

  1. Bigsbysays:
    Number 4 is most useful. It brings back memories when there was no Blend (just wonderful XAMLPad), XAML was wrote by hand and, as no one was here to explain how, when and who, one had to figure this things out on trial/error basis. I still thing that, reagarding the Visual, building your own implementation of a Panel derived class is a most useful and almost mandatory exercise to really understand how WPF (and cousin E) work.
  2. Petesays:
    @Bigsby

    Oh yes. I'm pretty sure that start, with no Blend, and later interim/CTP builds also with no blend is why I just got used to typing Xaml all the time. The Cider team did an awesome job with the Silverlight and WPF editors in VS2010, but I'm just an angle bracket jockey. Sounds like you're the same :)

    Pete
  3. Renaudsays:
    Hu, then we are three but also many other developers you just pleased with your solution. I am a huge fan of the control listbox and I have been playing with it for a while in every sauce. I am just so glad I won't have to deal with the sizeChanged event of the parent control anymore.

    Thanx Pete
  4. Dmitry Lyalinsays:
    I am working right now on a very large WPF project and i can tell you that sizing and scrollbars are a problem in many situations :(. I plan to write-up some of the issue for the team when I get more free time. Keep up the great posts friend!
  5. parsevalsays:
    Nice post Pete!
    Anyway I have a similar problem with an accordition inside a scroll viewer...
    I would need to fit It vertically but when I try to expand an item the height is not updated....
    so I'm forced to set a fixed height for the accordion...

    .(
  6. Petesays:
    @parseval

    An accordian *inside* a scrollviewer? That sounds pretty confusing from a UX standpoint.

    As to the specific problem, it's probably due to it using a stackpanel as the container. It's been a bit since I played with the accordian. You doing this in WPF or Silverlight? What version?

    Pete
  7. shaggygisays:
    I tried the example and does not seem to work for my problem. I have a ListBox and I bind a list of strings to the ItemsSource. I basically have the same XAML setup like your example with a Grid then a TextBlock. The problem is... when I try to select one of the items, I can not select the item until the mouse is over the text in the item. If I click to the right of the text, nothing will happen.

    Is your example setup to work for my problem? Is there another way to get around this? TIA:)
  8. Bishoy Harbysays:
    you may need to add to those four just one more step that is to set the lisbox property HorizontalContentAlignment="Stretch" for that when i tried it (putting a grid in the listbox) , it didn't fill up the space especially that i was not using text objects (I was putting comboxboxes inside my Listbox :D)
  9. Tedsays:
    Pete,

    I am doing something similar in one of my projects; however, when I implemented the code above the ultimate height of the listboxitem is not increasing to account for the textblock wordwrapping. So what I end up with is just a single line of readable text and a pixel or so of the tall letters on the 2nd row (t,l, etc). The main difference between the two is that I do not have a 70px image. So my question is whether I am missing something in my markup or do I just need a element whose height is greater than that of the wrapped text?

    Thanks,
    Ted

    The XAML

    <Grid>
    <Grid.ColumnDefinitions>
    <ColumnDefinition Width="Auto"/>
    <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
    <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <Ellipse Width="16" Height="16" Fill="{Binding Path=Criticality, Converter={StaticResource CriticalityBrushConverter}}" VerticalAlignment="Top" HorizontalAlignment="Center" Margin="2,0,0,0" Grid.Column="0"/>

    <TextBlock MinWidth="50px" Margin="5,0,0,0" HorizontalAlignment="Left" Grid.Column="1" ToolTip="{Binding Path=Reason}">
    <TextBlock Text="{Binding Path=Blurb, FallbackValue=Blurb}" TextWrapping="Wrap" /> <Hyperlink><TextBlock Text="Goto"/></Hyperlink>
    </TextBlock>
    </Grid>
  10. Oliversays:
    Hi,

    I have tried the above and it the items still are shrinking to their minimum size.
    I have tried decompiling the template but even the most basic template shrinks.

    <ListBox
    HorizontalContentAlignment="Stretch"
    ScrollViewer.HorizontalScrollBarVisibility="Disabled">
    <ListBox.ItemTemplate>
    <DataTemplate>
    <Grid Background="Red">
    <Grid.ColumnDefinitions>
    <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>
    <TextBlock Text="blabalaabalabaalan" TextWrapping="Wrap"/>
    </Grid>
    </DataTemplate>
    </ListBox.ItemTemplate>
    </ListBox>
  11. Shanesays:
    I struggled hard in silverlight to get this to work with no success. Finally, overriding the listbox container style set me free:
    <ListBox.ItemContainerStyle>
    <Style TargetType="ListBoxItem">
    <Setter Property="HorizontalContentAlignment" Value="Stretch"></Setter>
    </Style>
    </ListBox.ItemContainerStyle>

    I found the solution here

    http://stackoverflow.com/questions/746190/silverlight-3-listbox-datatemplate-horizontalalignment

    Hope this helps someone
  12. Water Damage Restorationsays:
    Binding issue at runtime...
    <ListBox Grid.Row="3" Grid.ColumnSpan="2" Name="Listbox_Jobs" Background="#FFF2F2D3">
    <ListBox.ItemTemplate>
    <DataTemplate>
    <Border Margin="3" BorderThickness="1" BorderBrush="SteelBlue" CornerRadius="3">
    <Grid Margin="2">
    <StackPanel Orientation="Horizontal">
    <TextBlock Text="{Binding Job_UUID}"></TextBlock>
    <TextBlock Text="{Binding Job_Date}"></TextBlock>
    <TextBlock Text="{Binding WaterDamage}"></TextBlock>
    </StackPanel>

    </Grid>
    </Border>
    </DataTemplate>
    </ListBox.ItemTemplate>
    </ListBox>

Comment on this Post

Remember me