I have a listview which receives information from a HashSet, but when I delete one item of the HashSet, my listview is not updated.
And my listview doesn't have a method of refresh, don't know why. Here is my code:
private void deleteActivityFromAlumn(String activityName, String nif)
{
Alumn alumnDelete = Alumn.findAlumnByNIF(nif);
Activity activityDelete = Activity.getActivityByName(activityName);
Debug.WriteLine(alumnDelete.Name + activityDelete.Name);
alumnDelete.activities.Remove(activityDelete);
activityDelete.Alumns.Remove(alumnDelete);
listActivities.ItemsSource = alumnDelete.activities;
}
And the item is deleted in a right way because if I go search for the object again, it is removed from the listView, but I believe that its supposed to be updated when you refresh the ItemsSource.
To make sure that control with bound items changes when those items change you have to
Either make that collection implement INotifyCollectionChanged. The simplest way to do it is to use ObservableCollection<T> instead of HashSet<T>
Though you will lose the uniqueness property that HashSet<T> gives you.
Or to somehow force a refresh of that control when you change the collection. Though one has to take into account that it is not the best solution, as it will more often than not cause a full redrawing of the control with obvious performance and/or display issues.
Either by calling the Refresh method (WPF, WinForms)
Or by re-binding the collection
listActivities.ItemsSource = null;
listActivities.ItemsSource = alumnDelete.activities
Related
This seems to be a problem that I encounter regularly: I have a list control, in this case a DataGrid, and the items in the control come from a web service. My application regularly asks the web service for the latest list of items. Compared with what my application currently has, the resulting list may have additional items, fewer items, or different details for existing items.
How do I update my control (ie: my data grid) without:
1) De-selecting the user's currently selected items.
2) Resetting the ordering that the user may have set on various columns.
3) Doing anything else that makes it jarring for the user.
4) Incorporating too much code, turning it into an unmaintainable mess.
Am I simply going about this whole thing wrong? It seems to me this scenario should be simple to address with something as versatile as WPF.
My current thinking is to use INotifyPropertyChanged on each item, and an ObservableCollection. Then, for each item in the list, update every property when we do the refresh (adding and removing items from the collection as necessary).
You are right, use ObservableCollection
1) De-selecting the user's currently selected items.
You can use either CollectionViewSource.GetDefaultView(rootElem.DataContext).Current or create a SelectedItem property, and bind it to the UI element. I prefer SelectedItem because it is less view related. For deselecting just set it to null.
2) Resetting the ordering that the user may have set on various columns.
You can easily swap two elements in ObservableCollection, and that change immediately will appear in the view.
var tmp = myObsCollection[i];
myObsCollection[i] = myObsCollection[j];
myObsCollection[j] = tmp;
Also you can use keep order property in your items, configure sorting in CollectionView, and just rearrange orders when needed and call view.Refresh();
3) You don't need to update all properties for updated items, just find the correct item index and replace it with new one, WPF will update automatically
myObsCollection[fIndex] = updatedItem;
Use the same logic for delete/add elements
I have a WPF application with a ListBox (called listMyItems) which is successfully bound to a class of MyItems that I created. I have a List of MyItems called currentMyItems which is then assigned as ItemSource to the ListBox. It all works fine, if I add an item to the currentMyItems it pops up on the list, etc.
The problem occurs when I try to remove the selected item in the ListBox. This is the code that I use:
currentMyItems.Remove((MyItem)listMyItems.SelectedItem);
The item disappears from the ListBox but the next time I update it, it pops back up as it was never deleted. Any tips?
I think you may be confused about how data binding works. When you bind a property, you are telling WPF to go look somewhere else for the value of that property.
When you bind the ListBox.ItemsSource property to currentMyItems, you are telling WPF to go look at the currentMyItems list to find its list of items. If currentMyItems is an ObservableCollection instead of a List<T>, then the UI will automatically receive a notification to update the bound value when you add or remove an item from the collection.
Based on what you say in the question, it sounds like you you have two collections, one of which is bound, and the other which is used to recreate the first collection anytime a change occurs. All that is not needed.
Just create one ObservableCollection<MyItem>, bind it to the ListBox.ItemsSource property, and then add or remove items from that single collection. It should work as you would expect.
<ListBox x:Name="listMyItems" ItemsSource="{Binding MyItems}" />
and
MyItems.Add((MyItem)listMyItems.SelectedItem)
MyItems.Remove((MyItem)listMyItems.SelectedItem)
If you're interested, I also have some beginner articles on my blog for WPF users who are struggling to understand the DataContext. You may want to check out Understanding the change in mindset when switching from WinForms to WPF and What is this “DataContext” you speak of?
If you bound it correctly to an ObservableCollection and currentMyItems is that collection. Than it means that you must have reloaded currentMyItems in meantime.
Also consider binding the SelectedItem property of your ListView - your view model doesn't have to know about the view at all.
Your source collection must be modufy (inherit from IList or ICollection). If your source collection does not support this method of your interface Remove, you can't remove item from source.
So, when you want to remove item you must cast ItemsSource to IList or ICollection:
var source = listbox.ItemsSource as IList ?? listbox.ItemsSource as ICollection;
and then check:
if (source == null) return;
then:
listbox.SelectedItems.ForEach(source.Remove);
listbox.Items.Refresh();
Make the currentMyItems<MyItem> an ObservableColection<MyItem>. This way it will raise a property change whenever modified and the UI gets updated accordingly.
By using ObservableCollection you will automatically get updates on the UI.
You should use an ObservableCollection instead of List.
A good thing is to always use ObservableCollection instead of List when something to do with UI
I'm binding ItemsControl to CollectionViewSource. Here is code:
this.Trucks = new ObservableCollection<Truck>();
foreach (var truck in DataRepository.Trucks.Where(t => t.ReadyDate.Date.Equals(this.Date)))
{
this.Trucks.Add(truck);
}
this.TrucksSource = new CollectionViewSource { Source = this.Trucks };
this.TrucksSource.SortDescriptions.Add(new SortDescription("ReadyAddress.Region.RegionNumber", ListSortDirection.Ascending));
this.TrucksSource.SortDescriptions.Add(new SortDescription("TruckId", ListSortDirection.Ascending));
When I initially bind - sorting works. When I add item to ObservableCollection - it is inserted in proper spot, thats good. But when I change property which I sort by - this item is not being "shifted" in a list.
ReadyAddress.Region.RegionNumber properly raises INotifyPropertyChanged and I see it in bound fields, but order does not change. Do I expect something that shouldn't happen or there is better way to handle this?
Late answer, but with 4.5 the ListCollectionView (the default implementation for a ListBox and CollectionViewSource.View) new properties were added to make this possible.
You can use the IsListSorting and ListSortingProperties to enable automatic resorting. And no, it does not rebuild the view
list.SortDescriptions.Add(new SortDescription("MyProperty", ListSortDirection.Ascending));
list.IsLiveSorting = true;
list.LiveSortingProperties.Add("MyProperty");
This should resort when the property MyProperty changes.
Have you tried refreshing your collectionviewsource?
this.TruckSource.View.Refresh();
All answers I found mentioned View.Refresh() but that's not very good solution for big lists. What I ended up doing was to Remove() and Add() this item. Then it was properly repositioned without reloading whole list.
Word of caution! It works for what I do, but in your case removing object and re-adding may cause side effect depending how your code written. In my case it's a list with UI effect where new items show up with transition so refreshing will show transition on whole list where remove/add nicely shows how item get's repositioned.
I am experimenting with WPF and MVVM in our system. However iam having a problem with keeping things selected in lists using only MVVM ( without doing extra CollectionViews ).
What i currently have is the list
ObservableCollection<ReservationCustomerList> Customers;
And then a property storing the selected Customer
ReservationCustomerList SelectedCustomer;
In my opinion now, when the list reloads (actually from another thread async), the selection should be able to be kept, however this does not happen.
Does someone have a nice clean way of achieving this ?
The way we did it was that we did not replace the collection. We added/removed the entries and updated existing entries if required. This maintains the selection.
You can use LINQ methods like Except to identify items that are new or removed.
In case the reloaded list still contains the last selected item and you want that item to be selected, then you can raise the PropertyChange event for the property SelectedCustomer after your collection gets reloaded.
Please make your sure your viewmodel class implements INotifyPropertyChanged interface.
you can use the ICollectionView to select the entity you want.
ICollectionview view = (ICollectionView)CollectionViewSource.GetDefaultView(this.Customers);
view.MoveCurrentTo(SelectedCustomer);
in your Xaml the itemsControl must have IsSynchronizedWithCurrentItem=true
or if the ItemsControl has a SelectedItem property you can simply bind it to your SelectedCustomer Property.
When you "reload" your collection you basically replace all values in it with new values. Even those that look and feel identical are in fact new items. So how do you want to reference the same item in the list when it is gone? You could certainly use a hack where you determine the item that was selected by its properties and reselect it (i.e. do a LINQ search through the list and return the ID of the matching item, then reselect it). But that would certainly not be using best practices.
You should really only update your collection, that is remove invalid entried and add new entries. If you have a view connected to your collection all the sorting and selecting and whatnot will be done automagically behind the scenes again.
Edit:
var tmp = this.listBox1.SelectedValue;
this._customers.Clear();
this._customers.Add(item1); this._customers.Add(item2);
this._customers.Add(item3); this._customers.Add(item4);
this.listBox1.SelectedValue = tmp;
in the method that does the reset/clear works for me. I.e. that is the code I put into the event handling method called when pressing the refresh button in my sample app. That way you dont even need to keep references to the customer objects as long as you make sure that whatever your ID is is consistent. Other things I have tried, like overwriting the collections ´ClearItems()´ method and overwriting ´Equals()´ and ´GetHashCode()´ didn't work - as I expected.
Good afternoon,
I am trying to use as Linq to SQL datacontext for a ListBox in WPF.
Basically, I assign the Linq DataContext to the form's DataContext property. Then, I bind it to the list.ItemsSource.
Everything works fine, I can show the details of each of my elements in a textbox (master-details scheme).
The thing is, I would like to be able to add a new element to the list:
private void Button_Click(object sender, RoutedEventArgs e)
{
Button btn = sender as Button;
var table = lst_markets.ItemsSource as System.Data.Linq.Table<Market>;
table.InsertOnSubmit(new Market() { IdMarket = Guid.NewGuid(), Name = txt_newmarket.Text });
table.Context.SubmitChanges();
}
The value is indeed added to the database, but the ListBox is not refreshed.
What should I do to refresh the list?
Thanks,
Jeremie
Table<TEntity> does not implement INotifyCollectionChanged, so the binding manager does not get notified that the collection's contents have changed.
A few options for you:
keep an ObservableCollection that you fill from the table, and keep synchronized. As you add/remove items from it the list would stay synchronized via binding. See this article for something similar
Hack around it - set the lst_markets.ItemsSource to null and back to the table when changing the collection. This would cause a full rebind, and feels like a dirty hack, but should work.
Don't Do that! A Table<T> is not a collection - it represents a query. Bind to a collection instead. If I remember correctly, every time you iterate a Table it queries the database, which means that any time the listbox feels it needs to enumerate, or the binding manager, or your ui code does the same you are hitting the database.
This forum post has an ObservableEntitySetWrapper that may give you some ideas.
Also see this SO question: How to refresh a WPF DataGrid?