problem binding ListBox on ObservableCollection<T> - wpf

I have a strange "problem". Could someone explain me why :
If I have in an ObservableCollection, twice (or more time) an item with the same value, then the selections of those values in the ListBox won't work properly ?
In fact, what the ListBox is doing when I click on an item(Even in single item selection) : It selects the first item from the ObservableCollection collection with a matching value. so in the case if multiple items with same value are in the collection, then only the first one will be selected !

Because objects you entered to collection have same references. you should create new instances in each case or override Equal function and write your logic for identifying items. WPF ListBox calls Object.Equal function to identify if the items are same.
Hope this helps

You need to create a new object to hold each object.
I.e.
MyCollection.Add(new MyContainer() { Data = myObject } );
This way the listbox will select the objects properly as it has unique containers.
This would be implicit if you were using ViewModels

Related

ItemsCollection's ContainerFromItem fails for an object that I'm pretty sure is in the collection

I am trying to add search functionality to a ListBox that will scroll down to the item being searched for.
I have a ListBox that is bound to an ObservableCollection that I have filled with a bunch of RecipeNameDTO objects. I can easily find an object in the Items collection with a simple search.
string searchItem = tbSearchString.Text;
var recipenameitem = lbRecipeNames.Items.Cast<DTO.RecipeNameDTO>().Where(u => u.RecipeName.ToLower().Contains(searchItem.ToLower())).FirstOrDefault();
I can reproducibly find items with this method.
However, if I then try to find the object's container using ContainerFromItem, the method returns a null unless the object is visible in the ListBox when I execute the method:
ListBoxItem lbi = (ListBoxItem)lbRecipeNames.ItemContainerGenerator.ContainerFromItem(recipenameitem);
I am certain (I think) that the actual object exists in the ItemsCollection before I execute ContainerFromItem because I use a non null result from the search I documented in the beginning of this post. Also, I can scroll down the ListBox and find the object I'm searching for.
It must be something with the way the ListBox caches the objects in the ItemsCollection that is preventing ContainerFromItem from returning the container. Is there a fix to my code (or understanding of the problem)?
Michael
I took Andy's suggestion and made my ListBox IsSynchronizedWithCurrent = True and then used the following code to set the current item and scroll it into view:
string searchItem = tbSearchString.Text;
CollectionViewSource cvs = (CollectionViewSource)this.FindResource("cvsRecipeName");
ObservableCollection<DTO.RecipeNameDTO> itemsCollection = (ObservableCollection<DTO.RecipeNameDTO>)cvs.Source;
List<DTO.RecipeNameDTO> recipenameitems = itemsCollection.Where(u => u.RecipeName.ToLower().Contains(searchItem.ToLower())).ToList();
if (recipenameitems.Count > 0) { cvs.View.MoveCurrentTo(recipenameitems[0]);}
lbRecipeNames.ScrollIntoView(lbRecipeNames.SelectedItem);
I'm sure I could modify this to make it more flexible, but here's the first fix.

Update the List view observable collection using MVVM

I have a usercontrol, with one list box and one list view control in it. For Listview i have bound the observablecollection of type TrafficManager class as shown below:
private static ObservableCollection<TrafficManager> _trafficCollection;
public ObservableCollection<TrafficManager> TrafficCollection
{
get { return _trafficCollection; }
set
{
_trafficCollection = value;
OnPropertyChanged("TrafficCollection");
}
}
I have bound this to itemsource of list view.
Now my requirement is on selection of the listbox item, i need to filter some items of the listview. For that i used a linq to get the desired rows from list view and added that to the list view collection. Before adding i did a listview Collection TrafficCollection.Clear() and then added to that collection.But now the issue is on selection of another item in list box i need the original listview contents again to carry out the filtering using the linq again. Here once the TrafficCollection.Clear() executes the original observable collection data vanishes. How do i maintain a backup of original observable collection data "TrafficCollection" of listview. Remember i have only one view. Is there anyway to do this? Please let me know.
you can use CollectionViewSource filtering, refer here
SO Link: Trigger Filter on CollectionViewSource
this will not clear original collection.

Not add or remove, but edit a combobox item in Silverlight?

So I have this combobox, which ItemsSource is set to a List of objects.
What I want to do after that is change the Display value of one specific Item (the first on the list), because this label does not suit the context in some situations but does in others.
So far I found that you could add an item using the Add method, insert one using the Insert method, and remove one using RemoveAt.
But how do you update one ? I know I could use RemoveAt and Insert afterwards, but it would be a bit labor intensive, plus I would have to recreate the object with all its values...
Any ideas?
Either replace an item in your "List of objects":
myList[0] = new MyObject("Another display text").
For this (and Add, Remove etc) to work, your list would have to be an ObservableCollection<T> or any other that implements INotifyCollectionChanged.
Or modify the item itself:
myList[0].DisplayText = "Another display text";
For this to work, your MyObject class would have to implement INotifyPropertyChanged, you'd have to raise the PropertyChanged event when the DisplayText property is set, and you'd have to set the DisplayMemberPath or the ItemTemplate of your ComboBox correctly.
Take a look at the MVVM pattern for more information.

ListView selection change doesn't remove an old item

I have a strange behavior with my WPF ListView Control.
ListViews ItemSource is Observable collection.the ItemSource is updated periodically.
When I'm selecting one of the item and then selecting other item and no item updated, everything is OK.
But when I'm selecting an item witch is updated while I'm standing on, then selecting other item, now I have two items selected instead of one.
When I'm looking with the debugger, I see the event args of SelectionChanged event. I see that added item is OK but no removed item.
Anyone knows what's the problem?
Thanks!
Edit:
My observable collection:
protected class CustomObservableCollection : ObservableCollection<T>
{
public void Refresh()
{
ListCollectionView lcv = (ListCollectionView)(CollectionViewSource.GetDefaultView(this));
lcv.Refresh();
}
}
The update method witch called when there is a change in some item:
public void RefreshItem(T domainObject)
{
foreach (T item in obsCollection) {
if (!DomainObjectComparer.Equals(domainObject, item)) continue;
DomainObjectCopier.CopyProperties(domainObject, item);
obsCollection.Refresh();
break;
}
}
It looks like your copier class makes two items in your collection equal (I think, inferring this from the limited amount of code above).
If two items or more are the same (equal) in a ListView, then selecting one will select all of them as an equality comparer is used in the selection logic.
Well, Apparently the problem was with the overridden GetHashCode() method of the ListView item object.
the hash code included all the fields in it's calculation. I remoed all the fields (properties) and now the overridden GetHashCode() is only calculating the hash based on item's ID. it solved the problem.
I also have Equals() method overridden.
If someone knows why it is related I will like to know.

How to keep a data-bound list reverse-sorted

I have a listbox bound to a collection. I would like the ListBox to always reverse the order of the items. This handler--hooked up to the control's load event--works for the initial load, but not thereafter. Ive tried using the SourceUpdated event but that doesnt seem to work.
How do I maintain a constant active sort?
MyList.Items.SortDescriptions.Add(New SortDescription("Content", ListSortDirection.Descending))
How is the collection stored that supplies the items for the ListBox? It should be a collection that supports INotifyCollectionChanged. The framework provides ObservableCollection<T> which you can use.
In the constructor of your ViewModel (or wherever the collection lives), you then get the DefaultView for adding the SortDescription. The CollectionView is like a layer on top of your collection, which you can use to sort, group, filter, etc. the items without actually affecting the underlying data source. The framework creates a default one for you. To get a reference to it, you can use code similar to the following:
var collectionView = CollectionViewSource.GetDefaultView(Widgets);
if(collectionView == null)
return;
collectionView.SortDescriptions.Add(new SortDescription("Content", ListSortDirection.Descending));
With that in place, you should be able to add items to the ObservableCollection<T> and the sort order will be maintained.
If your source collection is a List<T> or some other collection that doesn't implement INotifyCollectionChanged, there is no way WPF can detect when an item is added. You need to use a collection that implements INotifyCollectionChanged, like ObservableCollection<T>.
Also, the items in your collection need to implement INotifyCollectionChanged so that changes to the items are taken into account

Resources