Search code examples
windows-phone-8mvvmlonglistselectorui-virtualization

Items being reordered in LongListSelector


In my WP8 app, I'm using LongListSelector to display data items. Grouping works, jump list works.

I have the usual master/detail scenario here - click on item in the list, new page shows up with more information.

The problem is with navigating back to the page with LongListSelector. The list is basically messed up - items are randomly reordered, even between groups. Clicking on an item works properly - ShowItemInfo receives view model for the item the user clicked on.

Previously I was using ListBox, which too suffered from this issue. But I could disable virtualization by using default StackPanel as items panel. I don't know how to disable virtualization on LongListSelector (I don't want to, but the bug is horrible).

View model is simple. I'm using Caliburn.Micro, its conventions and BindableCollection for list of groups. I populate the list just once with AddRange method.

When the page is navigated back to, I'm not doing anything - and I even can't since I use MVVM and Caliburn.Micro. The items are loaded into list in OnInitialize callback, which is invoked only once during lifetime of view model.

The XAML for view is this:

<Grid x:Name="LayoutRoot" Background="Transparent">
    <Grid.Resources>
        <phone:JumpListItemBackgroundConverter x:Key="BackgroundConverter"/>
        <phone:JumpListItemForegroundConverter x:Key="ForegroundConverter"/>

        <Style x:Key="ListJumpListStyle" TargetType="phone:LongListSelector">
            <Setter Property="ItemTemplate">
                <Setter.Value>
                    <DataTemplate>
                        <Border Background="{Binding Converter={StaticResource BackgroundConverter}}"
                                HorizontalAlignment="Stretch">
                            <TextBlock Text="{Binding GroupTitle}" 
                                       Foreground="{Binding Converter={StaticResource ForegroundConverter}}" />
                        </Border>
                    </DataTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Grid.Resources>

    <phone:LongListSelector x:Name="Items" LayoutMode="List" IsGroupingEnabled="True"
                            JumpListStyle="{StaticResource ListJumpListStyle}">

        <phone:LongListSelector.ItemTemplate>
            <DataTemplate>
                <Grid cal:Bind.Model="{Binding}"
                      cal:Message.Attach="[Event Tap] = [ShowItemInfo($dataContext)]"
                      Background="Transparent">
                    <TextBlock x:Name="ItemText" Style="{StaticResource PhoneTextTitle2Style}" />
                </Grid>
            </DataTemplate>
        </phone:LongListSelector.ItemTemplate>

        <phone:LongListSelector.GroupHeaderTemplate>
            <DataTemplate>
                <Border>
                    <TextBlock Text="{Binding GroupTitle}" />
                </Border>
            </DataTemplate>
        </phone:LongListSelector.GroupHeaderTemplate>

    </phone:LongListSelector>
</Grid>

Solution

  • I've narrowed down the issue to the data template, more specifically the attached property Bind.Model, which allows Caliburn.Micro to bind view model to view:

    <DataTemplate>
        <Grid cal:Bind.Model="{Binding}"
              cal:Message.Attach="[Event Tap] = [ShowItemInfo($dataContext)]"
              Background="Transparent">
            <TextBlock x:Name="ItemText" Style="{StaticResource PhoneTextTitle2Style}" />
        </Grid>
    </DataTemplate>
    

    When this view is changed to use explicit bindings instead conventions (and Bind.Model attached property is removed), LongListSelector works like a charm.

    I believe there has to be a bug within Caliburn.Micro, which causes this issue. Initially, I thought the bug was in the 1.5.2 version, which I'm using (unfortunately I have to), and that it was fixed in new 2.x versions. However, that is not true, the bug is still manifesting in latest stable version 2.0.2.

    I was using this technique in WPF without any problems. I've also tried running the app in emulators for WP8.0 and WP8.1, but the behavior is the same.

    If anybody finds out, what causes this bug (or new version of Caliburn.Micro fixes it), feel free to edit answer or post a comment. I would be happy to use conventions in data templates again.

    My mistake: for data templates, Bind.ModelWithoutContext should be used. Even documentation explicitly says so:

    Bind.ModelWithoutContext - View-First - Set’s the Action.Target to the specified instance. Applies conventions to the view. (Use inside of DataTemplate.)

    With Bind.ModelWithoutContext LongListSelector works properly as it should (with conventions in item template).