2
votes

Has appeared a strange situation with listboxitem inner sender (i just think that this sender is to blame, but not sure...)

There is ItemTemplate for ListBox:

<ListBox x:Name="list">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel x:Name="stack_panel">
                    <Image Source="{Binding ImageSource}" Tap="Image_Tap"></Image>
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
</ListBox>

Every item in ListBox has a some image with tap event:

private void Image_Tap(object sender, GestureEventArgs e)
{
   Image i = (Image)sender;
   i.Source = new BitmapImage(new Uri("Images/yellow.png", UriKind.RelativeOrAbsolute));
}

Also, there is a class for easy images changing:

public class listItems
{
    public string ImageSource { get; set; }
}

Right. Now i ready to add some elements in listbox:

for (int i = 0; i < 100;i++)
   list.Items.Add(new listItems
   {
       ImageSource = "Images/black.png"
   });

So, let's click on zeroth sun image:

image 1

Everything is ok! Let's continue, and click on a second picture:

image 2

Right, the sun is shining..but.. stop, what is that? 0_o

enter image description here

I didn't click on this item, but image of this has changed! That happens with not only th item, also it has been with some other random items (53, 81, ...)

What do you think about this? Can sender has reference to many elements?

1
I think the issue may be the setting of i.Source directly instead of updating the bound object's ImageSource property. This is overriding the binding. I believe listboxes use virtualizing stackpanels by default, so the yellow sun is being recycled. You may also need listItems class to implement INotifyPropertyChanged.Shane Charles
Instead of deleting the comment I'll admit that I was wrong. I got a chance to test my theory and that doesn't appear to be the issue.Shane Charles

1 Answers

1
votes

What do you think about this?

By default list box control reuses visual items to optimize the performance. You can switch it off if you want, however for your 100 items the performance will drop of course.

There's better solution. Instead of what you're doing, you should store the selection state somewhere in your model, not just in a visual tree.

Your item class could be:

public class listItems: INotifyPropertyChanged
{
    bool _selected = false;
    public bool isSelected
    {
        get { return _selected; }
        set 
        {
            if( value == _selected )
                return;
            _selected = value;
            var pc = this.PropertyChanged;
            if( null != pc )
                pc( this, new PropertyChangedEventArgs( "ImageSource" ) );
        }
    }

    public string ImageSource { get { return _selected ? "Images/yellow.png" : "Images/black.png"; } }

    public event PropertyChangedEventHandler PropertyChanged;
}

INotifyPropertyChanged interface allows the visual tree to be notified about the property changes.

And here's the tap handler:

void Image_Tap( object sender, GestureEventArgs e )
{
    Image img = (Image)sender;
    listItems item = (listItems)img.DataContext;
    item.isSelected = true;
}