views:

48

answers:

1

I'm trying to create a scrolling list of fairly large textblocks. I want there to be a vertical scrollbar to show them all, and if they overflow a certain size I want them to display an ellipsis. I actually have all this working pretty good.

I have the following Silverlight XAML:

<Grid x:Name="LayoutRoot" MaxWidth="500" MinWidth="100"
    MaxHeight="500" MinHeight="100">
    <Grid.DataContext>
        <app:MainPageViewModel/>
    </Grid.DataContext>
    <ScrollViewer>
    <ItemsControl ItemsSource="{Binding TextItems}" Margin="0,20,0,20">
        <ItemsControl.ItemTemplate><DataTemplate>
            <Border MaxHeight="175" Margin="0,0,0,18" CornerRadius="5">
                <TextBlock Margin="2" TextTrimming="WordEllipsis"
                     TextWrapping="Wrap" Text="{Binding}"/>
            </Border>
         </DataTemplate></ItemsControl.ItemTemplate>
    </ItemsControl>
    </ScrollViewer>
</Grid>

My problem is that this layout does not use UI virtualization, such as with a VirtualizingStackPanel. So it is pretty slow. What is the best way to get UI virtualization into this layout? I've tried about a half dozen different ways and nothing has worked out all that well.

I managed to get this working in a ListBox because it seems to support virtualization out of the box. However, I'd prefer to use ItemsControl as I don't want these things to be selectable, and I don't want the styling that comes along with a ListBox.

This in Silverlight 4.

+2  A: 

There are a few things you need to do to make this work.

  1. Set the ItemsPanelTemplate for your ItemsControl to a VirtualizingStackPanel.
  2. Incorporate the ScrollViewer inside a ControlTemplate for your ItemsControl instead of just wrapping it around the outside.
  3. Make sure the ItemsControl has a fixed height so the layout system can work out how many items it needs to fill the viewport. (It looks like you are already doing this by putting the ItemsControl in a Grid - that will allow the layout system to determine the alloted height for the control)

Here's the simplest example I could come up with of this working:

    <ItemsControl ItemsSource="{Binding TextItems}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <VirtualizingStackPanel />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.Template>
            <ControlTemplate TargetType="ItemsControl">
                <Border>
                    <ScrollViewer>
                        <ItemsPresenter/>
                    </ScrollViewer>
                </Border>
            </ControlTemplate>
        </ItemsControl.Template>
    </ItemsControl>
Steve Willcock
@Steve Willcock. That worked like a charm Steve. So the main difference between your solution and what I was trying to do is that you used ItemsControl.Template to add in the ScrollViewer, whereas I was just trying to put a ScrollViewer on the outside of the control. Can you explain why yours works and what I was trying to do doesn't? It might help me understand the solution a bit better.
jkohlhepp
Great, glad it worked for you. Basically what happens is that if you put a ScrollViewer around the outside of the ItemsControl then the layout engine will render the whole ItemsControl - it will generate item containers for all the items in the control. The ScrollViewer is just limiting the view to a small part of the ItemsControl but the whole ItemsControl with all the items exists in memory. The second way, putting the ScrollViewer inside the template gives the ItemsControl the ability to limit the number of item containers it creates - it will only create enough to fill the space available.
Steve Willcock
Makes sense. Thanks again!
jkohlhepp