Iris Classon
Iris Classon

Reputation: 5842

Why does the LongListSelector add extra items if the collection values are identical?

I noticed this strange behavior yesterday when putting together a demo application for WP. Usually I never pass in plain items, and never the ‘same’- but this time I did. When bound to an observablecollection of type int or string, if the value is the same, the control will add first one item, then two, then three, then four. Instead of adding one each time. Basically it does this:

Items.Count ++

If I however change the item, say string, to something unique (a GUID.ToString for example) it has the expected behavior.

This is regardless of how the DataContext is set, or if I add LayoutMode="List" IsGroupingEnabled="False" to the control.

Why does it do this? And is this expected behavior?

I spent hours searching for an answer, so please don’t just paste random links about the control :)

Code for the view:

<phone:PhoneApplicationPage
x:Class="Bug.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="True"
DataContext="{Binding RelativeSource={RelativeSource Self}}">

<StackPanel>
    <Button Click="Button_Click">Add</Button>
    <phone:LongListSelector  Width="300" Height="600" ItemsSource="{Binding Items}"/>
</StackPanel>
</phone:PhoneApplicationPage>

Code behind:

using System.Collections.ObjectModel;
using System.Windows;


namespace Bug
{
    public partial class MainPage
    {
        // Constructor
        public MainPage()
        {
            InitializeComponent();

            Items = new ObservableCollection<string>();

            //DataContext = this;
        }

        public ObservableCollection<string> Items { get; set; }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Items.Add("A");

            // This works as expected
            //Items.Add(Guid.NewGuid().ToString());
        }
    }

}

Upvotes: 5

Views: 524

Answers (4)

Jevgeni Tsaikin
Jevgeni Tsaikin

Reputation: 321

I think it is somehow related to Two different "strings" are the same object instance?, so called String Interning http://en.wikipedia.org/wiki/String_interning, which means that it doesn't matter how many "A" string you will create it will still point to the same memory.

For example: if you try to save "Guid.NewGuid().ToString())" into a private field and use that instance for "Items.Add(obj)" it will result in the same issue.

Also the count for Items is correct, so it must be a bug on LLS and the way it is rendering same-instance-objects.

Upvotes: 0

dotMorten
dotMorten

Reputation: 1966

This is a bug in LongListSelector. The reason it works with Guid is because this'll do a reference comparison and avoid the bug.

Here's a workaround by using a reference object instead:

public partial class MainPage : PhoneApplicationPage
{
    // Constructor
    public MainPage()
    {
        InitializeComponent();

        // Sample code to localize the ApplicationBar
        Items = new ObservableCollection<Object>();

    }
    public ObservableCollection<Object> Items { get; set; }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        Items.Add(new Object() { Value = "A" });

        // This works as expected
        //Items.Add(Guid.NewGuid().ToString());
    }

}
public class Object
{
    public string Value { get; set; }

    public override string ToString()
    {
        return Value;
    }
}

Upvotes: 2

robwirving
robwirving

Reputation: 1798

This is by no means an answer, but I modified @IrisClasson's original code a bit to highlight the problem more. If you alternate between adding an item "A" and an item "B" the list will show weird behavior when adding either item twice in a row, but will correct itself when you add the other item. I added a ListBox bound to the same data so that you can see how the list should look.

View:

<phone:PhoneApplicationPage
    x:Class="Bug.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait" Orientation="Portrait"
    shell:SystemTray.IsVisible="True"
    DataContext="{Binding RelativeSource={RelativeSource Self}}">

    <StackPanel>
        <StackPanel Orientation="Horizontal">
            <Button Click="Button_Click">Add A</Button>
            <Button Click="Button_Click1">Add B</Button>
            <Button Click="Button_Click2">Clear</Button>
        </StackPanel>
        <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
        <phone:LongListSelector x:Name="MyLongList"  Width="200" Height="600" 
                                 ItemsSource="{Binding Items}"/>
        <ListBox x:Name="MyListBox" Width="200" Height="600" 
                 ItemsSource="{Binding Items}" />
    </StackPanel>
    </StackPanel>
</phone:PhoneApplicationPage>

CodeBehind:

using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Windows;
using Microsoft.Phone.Controls;

namespace Bug
{
    public partial class MainPage : PhoneApplicationPage
    {
        // Constructor
        public MainPage()
        {
            InitializeComponent();

            Items = new ObservableCollection<string>();

            Items.CollectionChanged += Items_CollectionChanged;
        }

        void Items_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            Debug.WriteLine("CollectionChanged");
        }

        public ObservableCollection<string> Items { get; private set; }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Items.Add("A");

            // This works as expected
            //Items.Add(Guid.NewGuid().ToString());
        }

        private void Button_Click1(object sender, RoutedEventArgs e)
        {
            Items.Add("B");

            // This works as expected
            //Items.Add(Guid.NewGuid().ToString());
        }

        private void Button_Click2(object sender, RoutedEventArgs e)
        {
            Items.Clear();
        }
    }
}

Upvotes: 0

vjirovsky
vjirovsky

Reputation: 208

I am pretty sure it's bug of LongListSelector.

I think, when it loads diff between data, it takes all apperance of "A" in collection into list.

Let's wait for WP8.1, probably LongListSelector will be deprecated :-)

VJ

Upvotes: -1

Related Questions