views:

434

answers:

3

I've seen this question asked numerous times and have gone over my code over and over. But when I assign to my SelectedItem-bound property with an object, it does not update the display's selected item. It appears that the ListBox thinks the object I assign is not a member of its items.

public class MainViewModel : ViewModelBase
{
  //...
  public SortedObservableCollection<TubeViewModel> Items { get; private set; }
  public TubeViewModel SelectedTube { //get, set, propertychanged, etc. }
}


 <ListBox x:Name="TubeList"
       Margin="10"
       ItemsSource="{Binding Items}"
       ItemTemplate="{StaticResource TubeTemplate}"
       SelectedItem="{Binding SelectedTube, Mode=TwoWay}" 
       SelectionMode="Single"
       VirtualizingStackPanel.IsVirtualizing="False">
 </ListBox>

Here's the impl from one of the places where I try to set SelectedTube - definitely occurs on the main thread.

     var match =
        from t in Items
        where t.Model.DataFileName == filename
        select t;
     if (match.Any())
        SelectedTube = match.First();

I was noticing that the SelectedTube was never highlighted unless I manually clicked on it, but kind of ignored it. But then I wanted to ScrollIntoViewCentered the selected item, so I added a DependencyProperty in my view to watch for SelectedItem changes. The handler initially looked like so:

  private void OnSelectedItemChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
  {
     if (TubeList.SelectedItem == null)
        return;

     TubeList.ScrollIntoViewCentered(TubeList.SelectedItem);
  }

But didn't work if I set the SelectedItem through my binding. After deciding I wanted to only scroll it if it was not on screen, I changed the code to this:

  private void OnSelectedItemChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
  {
     if (TubeList.SelectedItem == null)
        return;

     var container = TubeList.ItemContainerGenerator.ContainerFromItem(TubeList.SelectedItem) as FrameworkElement;

     if(!container.IsVisible)
        TubeList.ScrollIntoViewCentered(TubeList.SelectedItem);
  }

If SelectedItem is externally set, container is null. Every time. I even added code to check for null and then iterate over the ItemsSource looking for a filename match, then update SelectedItem to make sure SelectedItem was definitely an object in the listbox. No luck.

It's not a visibility thing, if the item is on screen, it still doesn't highlight. Do I need to implement some sort of equality function? I've wasted more time than one might think possible on this.

thanks.

+1  A: 

Nothing jumps out at me as obviously wrong, but you've glossed over a lot. I can only suggest you simplify things until you either find the problem, or have a complete non-working example that you can post.

HTH,
Kent

Kent Boogaart
A: 

Have you tried checking the hashcode of each object in the itemsource against the hashcode of the object you are trying to set? The Linq query will return something different.

As a test trying setting the SelectedTube to Items[0].

Kevin Mills
+1  A: 

Maybe this is also related to the fact that ItemContainerGenerators are generated in a separate thread... (at least in the TreeView)

Try calling UpdateLayout() before you ask the ItemContainerGenerators property.

Yacoder
I don't get why this is necessary, but I'm happy it is. Thanks a ton.
Tom