Changing ListBox's ItemsPanelTemplate At Runtime

1.5k views Asked by At

I would like to have ListBox's ItemsPanelTemplate change during runtime.

I have the following XAML which allows me to change the ItemsPanelTemplate; however has the unwanted side effect of breaking the ScrollViewer.

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:ie="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"

...

<UserControl.Resources>
    <ItemsPanelTemplate x:Key="StackPanelTemplate">
        <VirtualizingStackPanel/>
    </ItemsPanelTemplate>

    <ItemsPanelTemplate x:Key="WrapPanelTemplate">
        <telerik:RadWrapPanel/>
    </ItemsPanelTemplate>
</UserControl.Resources>

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <StackPanel>
        <Button Content="StackPanel">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="Click">
                    <ie:ChangePropertyAction TargetName="TargetListBox" PropertyName="ItemsPanel" Value="{StaticResource StackPanelTemplate}"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </Button>
        <Button Content="WrapPanel">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="Click">
                    <ie:ChangePropertyAction TargetName="TargetListBox" PropertyName="ItemsPanel" Value="{StaticResource WrapPanelTemplate}"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </Button>
    </StackPanel>

    <ListBox x:Name="TargetListBox" Grid.Column="1" ItemsSource="{Binding SomeCollection}"/>
</Grid>

When you change the ItemsPanelTemplate this way. The ScrollViewer seems to stay in whatever state it was in before you changed it and using the scroll bar does not effect any change on the ListBox.

Can anyone provide any insight on this issue or perhaps provide a workaround?

Thank you.

* EDIT *

So, I've narrowed the problem down to it being related to virtualization. If you change the VirtualizingStackPanel for just a regular StackPanel the ScrollViewer does not break. This isn't really a solution for me though as this ListBox will hold many hundreds of search results.

2

There are 2 answers

2
Filip Skakun On

I think the easiest workaround would be to replace entire ListBox instead of just the panel template.

0
A.Hussainy On

well I was facing the same problem I wanted to create a ListBox that have products and make user free to change the layout from WrapPanel to List box and from ListBox to WrapPanel. so to do this you should use styles. (I recommend you to use ListView not ListBox because of scrolling issues in ListBox. anyway both will work). at first add your 2 styles inside in your app.xaml

WrapPanelTemplateLV

<Style x:Key="WrapPanelTemplateLV" TargetType="ListView">
            <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled" />
            <Setter Property="ItemsPanel">
                <Setter.Value>
                    <ItemsPanelTemplate>
                        <WrapPanel />
                    </ItemsPanelTemplate>
                </Setter.Value>
            </Setter>
        </Style>

and StackPanelTemplateLV

<Style x:Key="StackPanelLV" TargetType="ListBox">
            <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled" />
            <Setter Property="ItemsPanel">
                <Setter.Value>
                    <ItemsPanelTemplate>
                        <StackPanel />
                    </ItemsPanelTemplate>
                </Setter.Value>
            </Setter>
        </Style>

now inside your button just do this

// StackPanelLV is on my App.xaml
            MyListView.Style = (Style)Application.Current.Resources["StackPanelLV"];

and now you got the idea. make some logic to switch between the 2 styles. I recommend you to go to this question for more. Changing the styles at runtime in WPF