Fix scrollbars for a dynamic layout in a Listview/ListBox

Some colleagues at work had a problem today with a ListView that had a dynamic layout. Basically we had a ListView that contained an Expander as one of the CellTemplates, the Expander had another ListBox inside that contained several items. The problem with this was that the ScrollBar for the ListView (which contained all the Expanders) was freaking out and behaving like it was drunk. Here is the XAML for this.

   1: <ListView ItemsSource="{Binding}" >
   2:     <ListView.View>
   3:         <GridView>
   4:             <GridViewColumn Header="Test">
   5:                 <GridViewColumn.CellTemplate>
   6:                     <DataTemplate>
   7:                         <Expander Header="Test">
   8:                             <ListBox ItemsSource="{Binding}">
   9:                                 <ListBox.ItemTemplate>
  10:                                     <DataTemplate>
  11:                                         <TextBlock Text="{Binding}"/>
  12:                                     </DataTemplate>
  13:                                 </ListBox.ItemTemplate>
  14:                             </ListBox>
  15:                         </Expander>
  16:                     </DataTemplate>
  17:                 </GridViewColumn.CellTemplate>
  18:             </GridViewColumn>
  19:         </GridView>
  20:     </ListView.View>
  21: </ListView>
  22:
  23: public Window1()
  24: {
  25:     DataContext = new[]
  26:         {
  27:             new [] { 50, 30, 20, 10, 40, 50, 70, 84, 20, 30, 50 },
  28:             new [] { 50, 30, 20, 10, 40, 50, 70, 84, 20, 30, 50 },
  29:             new [] { 50, 30, 20, 10, 40, 50, 70, 84, 20, 30, 50 },
  30:         };
  31:     InitializeComponent();
  32: }

In order to fix this there is a magic trick 🙂

All you have to do set an attached  property on the ListView to instruct it’s ScrollViewer that the Content cannot Scroll. so basically the XAML now would look like this

   1: <ListView ItemsSource="{Binding}" ScrollViewer.CanContentScroll="False">
   2:     <ListView.View>
   3:         <GridView>
   4:             <GridViewColumn Header="Test">
   5:                 <GridViewColumn.CellTemplate>
   6:                     <DataTemplate>
   7:                         <Expander Header="Test">
   8:                             <ListBox ItemsSource="{Binding}">
   9:                                 <ListBox.ItemTemplate>
  10:                                     <DataTemplate>
  11:                                         <TextBlock Text="{Binding}"/>
  12:                                     </DataTemplate>
  13:                                 </ListBox.ItemTemplate>
  14:                             </ListBox>
  15:                         </Expander>
  16:                     </DataTemplate>
  17:                 </GridViewColumn.CellTemplate>
  18:             </GridViewColumn>
  19:         </GridView>
  20:     </ListView.View>
  21: </ListView>

As you can see all I changed here is adding the attached property ScrollViewer.CanContentScroll=”False” on the ListView, and viola the scrolling now works correctly 🙂

Edit: After having a chat with a fellow WPF Disciple, Andrew Smith, he pointed out that this solution will drop Virtualization support. So if you have a lot of data this is probably not the best solution for you.