Reputation: 5842
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
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
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
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
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