I am using an observable collection as an item source for my WPF listbox. In code this means:
private readonly ObservableCollection<AssetBrowserFilePreviewElement> mLastElements; // = new ...
private void OnInitialized(object sender, EventArgs args)
{
mBrowser.SelectedFilesListView.ItemsSource = mLastElements;
}
// in another method:
mLastElements.Clear();
foreach(var elem in elements)
mLastElements.Add(elem);
(OnInitialized gets called and elements is not empty)
As written in the title my problem is that adding and removing items from the collection has absolutely no effect on the listbox, it always remains empty. If I bind the IEnumerable<AssetBrowserFilePreviewElement> elements as ItemsSource instead of this Clear and Add-loop i get the expected results. In this case it would work this way but in other methods i want to selectively remove or add items and therefore I need the observable collection to work.
What else do I have to do to propagate modifications to the collection to the listbox?
EDIT:
As suggested in the response I've created a property and a binding in XAML but still no response whatsoever.
XAML:
<ListBox Padding="10,10,10,10" x:Name="SelectedFilesListView" Grid.Row="1" ItemsSource="{Binding Path=CurrentFiles}">
C#:
private readonly ObservableCollection<AssetBrowserFilePreviewElement> mCurFiles = new ObservableCollection<AssetBrowserFilePreviewElement>();
public ObservableCollection<AssetBrowserFilePreviewElement> CurrentFiles { get { return mCurFiles; } }
CurrentFiles.Clear();
foreach(var elem in elements)
CurrentFiles.Add(elem);
CurrentFiles getter gets called when the control loads. The method that modifies the collection gets called as well, elements.Count == 18, no visual change.
EDIT 2:
I have added a test element to CurrentFiles in the constructor of AssetBrowserViewModel (where the Property is defined and which is set as DataContext). This item shows up, but modifying the elements of the collection keeps showing that test element.
In your code you simply set ItemSource once. There is no mechanism which tracks changes.
Instead you need a Binding (preferably in XAML).
This will listen to the PropertyChanged events of the ObservableColletion and update the ListBox accordingly.
Very well, after all it was just a stupid mistake caused by Resharper. It decided to import ObservableCollection from SharpDX.Collections instead of System.Collections.ObjectModel. I realized it when checking the subscription on the CollectionChanged event and there was no CollectionChanged event.
Also a binding is not required. Setting ItemsSource in code behind to a private field and modifying that collection works like a charm (as does a binding).
Related
I've been struggling with this problem for several days now.
I have a combobox that gets its ItemsSource twice.
The first time i try to see if i have an offline backup of the data that i am trying to show.
While the offline data is showing, the software needs to download the updated data from the database.
Everything works fine if i don't have any stored data in the offline backup.
The problem arises when i have a list of items backed up, i put it in the combobox itemsSource, and then change that list after a couple seconds.
The list actually shows correctly, but the selectedItem (which is something that i get from the viewmodel) gets reset and i can't set it again from code-behind.
The SelectedItem property in the viewModel is actually properly set, but it doesn't get to the UI.
The list is an ObservableCollection of the same type as the selectedItem.
My combobox
<ComboBox ItemsSource="{Binding SomeList, Mode=OneWay}"
SelectedItem="{Binding ListItem, Mode=TwoWay}"
DisplayMemberPath="ItemProperty"
Margin="10,0,0,0" Width="300" VerticalAlignment="Center"/>
The way i set the selectedItem in both the offline and online methods:
SomeList= await _mainRepository.BackEndOfflineFirst_GetList();
if (SomeList.Count > 0)
{
ListItem = SomeList.SingleOrDefault(list=> list.Property.Equals(SomeVariable.Property));
}
Any help is appreciated.
I should mention that adding IsSynchronizedWithCurrentItem="True" to the combobox always makes the selectedItem the first item of the list.
The check for list.property equals another variable.property ALWAYS returns one single element that is guaranteed to be in the list, and is never null.
Forgot to mention (kinda important, my bad), but i'm using Prism and i implement BindableBase, so the properties are defined as follows:
private ObservableCollection<Type> _someList ;
public ObservableCollection<Type> SomeList
{
get { return _someList ; }
set { SetProperty(ref _someList , value); }
}
It is impossible to infer the cause of the problem from your explanations.
Everything is correct in the code examples - the problem is somewhere else.
Just in case, I ask you to clarify: do you raise PropertyChanged in all properties?
One tip (including as a probable cause), use the ReadOnly property for the observable collection:
public ObservableCollection<...> SomeList {get;}
= new ObservableCollection<...>();
SomeList.Clear();
foreach(var item in await _mainRepository.BackEndOfflineFirst_GetList())
SomeList.Add(item);
Found out why it wasn't working.
BindableBase propertyChanged only gets raised when the value actually changes.
I was getting basically the same object (different objects, same value), so it wasn't actually raising the propertyChanged.
To fix this, i put a
ListItem = null;
SomeList.Clear();
inside a finally clause, before actually giving the value i receive from the DB to the ListItem.
It also does not seem to work if i change properties too quickly (hence the finally clause), so i couldn't just put a
SomeList.Clear();
SomeList = await...
back to back.
Another problem i found was that SomeList.Clear() actually throws a NotSupportedException that wasn't actually showing up, hence skipping some of the code.
This was caused by SomeList being an ObservableCollection. Changed it to a generic List and i can Clear it now.
If you need to clear the ObservableCollection, a simple Collection = null; works too.
Thank you to everyone that helped me.
I've a ListView, each item of the list have associated an event with: MouseLeftButtonDown, this event called a method that will display some details of the item clicked.
Now what I'm trying to achieve is trigger this event by selecting the item behind code. Suppose that in listview I've this item:
Foo
Foo2
I've Foo2 stored in an object variable. What I did:
object currentItem = "Foo2";
MyList.SelectedItem = currentItem;
the problem's that isn't possible fire the MouseLeftButtonDown by selecting an item behind code.
What I need is fire MouseLeftButtonDown on the ListViewItem contained inside the variable currentItem, that of course, is available also in the list MyList.
Is possible do this? Thanks.
Maybe you should use listView_SelectionChanged event to manuplate your data.
Does the selected item contain all the information you want to display? If so, you could simply bind the elements displaying additional information directly to properties of the ListBox.SelectedItem. If you don't want the elements to display until there is an item selected, bind the visibility of the panel containing the elements to the ListBox's SelectedItem property with a NullToVisibilityConverter (mentioned several times on StackOverflow).
This way you have a completely declarative view on your UI, drastically reducing the complexity of the code behind.
I have a ListBox that is bound to a ListCollectionView, at one point I select an item in the listbox, then in the future I am recreating the ListCollectionView, but the ListBox is internally keeping a reference to an item from the old list. This is a problem because that item has a reference back to its parent container, and it is effectively causing a memory leak.
Here is a screenshot from .NET memory profiler showing how the ListBox's SelectedItem and SelectedValue are keeping a reference to the DataPoint object.
The new DataPoint object in the new ListCollectionView is equal to the existing selected one(because it has its own identifier field and I override object.Equals) but is not the same reference, how can I force the ListBox to change its SelectedItems so that it doesn't cause a memory leak? Is there a better way than having to forcefully unselect and reselect the items from the code behind?
Currently the WPF looks like:
<!-- Listbox of items -->
<ListBox
x:Name="ListBoxOfStuff"
ItemsSource="{Binding ListView}"
ItemTemplate="{Binding ItemTemplate}"
And in the code behind I have
public ListCollectionView ListView
{
get { return _listview; }
private set
{
if (_listview == value)
return;
_totalItemsInCollection = value.Count;
_listview = value;
_listview.Filter = this.ApplyFilter;
RaisePropertyChanged("ListView");
RaisePropertyChanged("FilteredInCount");
}
}
Did the comments (above) resolve your question? Will's suggestion sounds like what you need. That's what I always do - have a separate property on your view-model that is bound to SelectedItem, or if you're using it as a multiple-selection listbox: SelectedItems (which is a collection). When a new collection is assigned to your bound property, the previous should be released. If it is not (and I have had that happen, in certain cases) making it re-bind by invalidating the binding, always cleared that up for me. You could also check to ensure that something else is not keeping a reference to that item. For example, I saw an implementation that raised an event, that contained a reference to a list-item's SelectedItem. It was not a weak-reference, and it lingered, causing a memory-leak. The cure, was to provide an id within that event that served to identify which item the user had just selected, but not an actual reference. You're doing the right thing, in using your profiler to check the memory situation (kudos).
I have a ComboBox that has an ItemSource bound to a an ObservableCollection and its SelectedItem property is bound to a property of T. Both properties live on my view model. Everything works great except for one issue that is causing me to tear my hair out. When I set a property on the object bound to the SelectedItem, the ComboBox binding stops working. VS 2012 output window does not report any binding errors and no exceptions are thrown.
XAML
<ComboBox ItemSource="{Binding Path=Connections, Mode=OneWay}"
SelectedItem="{Binding Path=SelectedConnection, Mode=TwoWay}"
DisplayMemberPath="Name"/>
<TextBlock Text="{Binding Path=ConnectionName, Mode=TwoWay}" />
<TextBlock Text="{Binding Path=ConnectionGroup, Mode=TwoWay}" />
View Model
private void SaveChanges()
{
this.SelectedConnection.Name = this.ConnectionName;
this.SelectedConnection.Group = this.ConnectionGroup;
// -- Save the changes
}
Now all of that works just fine. But as soon as SaveChanges() completes the ComboBox starts acting funny. It looks like everything is fine, but if you attempt to change the selection nothing happens; the same item is selected and the setter for "SelectedConnection" does not fire. I tried systematically commenting out code in SaveChanges() and if I leave only a single line that is setting any property on SelectedConnection I see this failure happen.
I also added an event handler in the code behind of the control to handle the SelectionChanged event on the ComboBox. After the break happens, any time I try to change the value via the UI or in Code the event handler fires twice in immediate succession. The first has the event args' AddedItems set with a single value which is the value I just selected and would expect. However the event fires again immediately with only a single item in the RemovedItems collection of the event args. That collection also contains the item I just selected.
I have tried setting the SelectedConnection property to null and then after the method finishes, setting back to the object in question. No luck.
I also saw an old bug post about moving the ItemSource property after the SelectedItem property on my XAML mark up but that had no effect either.
I'm trying to avoid creating a shadow list and trying to keep everything in sync as that was going to be my next try but that is just getting ugly to do something so simple. Any ideas would be greatly appreciated.
If it matters, this is Visual Studio 2012 building to the 4.5 framework.
EDIT
I've now also tried just completely removing the modified object from the ObservableCollection after setting its properties. It still shows as if it was the selected value (its name is immediately visible in the ComboBox) but it no longer shows up in the drop down list. It also will still not allow itself to be "unselected" even though it doesn't exist in ItemSource any more.
** UPDATE **
I changed the method in question to completely null out the ObservableCollection that is bound to the ItemSource and then restore it once the process is over with like so:
private void SaveChanges()
{
var selected = this.SelectedConnection;
var name = this.ConnectionName; // -- bound to fields on the view
var group = this.ConnectionGroup;
this.Connections = null;
this.RaisePropertyChanged("Connections"); // -- SelectionChanged fires as expected
selected.Name = name;
selected.Group = group;
this._repository.SaveChanges();
this.Connections = new ObservableCollection<Connection>(this._repository.AllConnections);
this.RaisePropertyChanged("Connections"); // -- SelectionChanged event doesn't fire
this.SelectedConnection = selected; // -- SelectionChanged event doesn't fire
}
After that is done the ComboBox once again allows me to change the value as you would expect. However the selected item does not actually appear as selected (even though it is correctly set in the View Model). So yeah, it's a really ugly workaround and it creates its own issue as well.
Have just had this same issue, and I think I tracked down what was doing on.
It only appeared after I made some recent changes (suggested by the code analysis) to add equals/hashcode/operator overloads for a class that I had as IComparable, and was used in an ObservableCollection for a combobox. I made the hashcode based on a descriptive field of the object. However that field could be user-modified, and after the user modified the value, the combobox broke, being unable to unselect that item anymore.
Given that there are no unchanging fields in this object, there's no way to create a fixed hash code based on the object's properties. However if I remove the hashcode override entirely (or set it to something fixed, like type.GetHashCode), everything works again.
So, check to see if your hashcode is based on whatever value you were changing.
I gave up on doing it the way I had originally envisioned. Instead of directly modifying the object in the ItemSource collection, I made the ItemSource collection to be a list of Key/Value pairs and extracted the object I needed from the source list based on the key of the selected pair. I was able to edit that object without issue (as expected). It works now, I just added a layer of code in there that I was hoping to avoid. The UI also allowed me to update the Value side of the pair to reflect changes to the underlying model's "Name" field without giving me the same issues it was previously. I'll just count it as a small win and move on.
I have an ObservableCollection<MyClass> named myCollection that should be binded to two CollectionViewSources (AllItems and SelectedItems).
AllItems's source property is binded to the myCollection. SelectedItems's source property should be bind to myCollection items which IsSelected == true.
public class MyClass : INotifyPropertyChanged
{
//fields and interface implementations
public string Name {//proper code};
public bool? IsSelected {//proper code};
}
// some where else in the MainWindow
AllItems.Source = myCollection;
SelectedItems.Source = myCollection.Where(input=>input.IsSelected==true);
Problem: Every thing is OK when the Window is loaded. But when the IsSelected value for some items in the myCollection is changed obviously it has no effect on the SelectedItems. So to overcome this problem I update the source property of the SelectedItems every time an IsSelected property is changed.
Question: How can I do these kind of binding so that there is no need to manually update the source property of the SelectedItems?
Thnaks.
First of all, you should unconditionally remove your manual filtering setup and replace it with something more appropriate. The choice of what to replace with depends mainly on what .NET version you are targeting.
If targeting .NET 4.5 then a simple solution is and instead enable live filtering on the collection view.
For earlier versions of .NET you will have to do some manual work no matter what, but it is better to just call Refresh on the collection view that re-bind your control. To do this, you should defer the filtering to the collection view itself by setting the Filter event handler from XAML.
After the changes in MyClass you should raise PropertyChanged Event.
If you already do this than you should look at your SelectedItems.Source in Debug mode. Maybe its the correct value already there but its not shown to you.
I mean if SelectedItems.Source belongs to some visible Elements - GUI - you should refresh it on the screen. Cause the other way the value is there but will not be shown till your Element on the Screen is repainted.