Let me start off by saying that I am completely new with WPF (this is my first project and I have been working in it for less than a week). With that being said, please be easy on me!
I have three list ListBoxes that are being bound to ObservableCollections from a LINQ queries. In the beginning, everything is fine, all three are populated correctly. My client needs to drag and drop selections from one ListBox to another. I also have this working, but when I do the drag and drop, the new selection is placed at the bottom of the ListBox instead of being sorted alphabetically with the existing items.
How can I sort the ListBox at runtime through code behind after the drag and drop operation is complete.
Thanks!
It is not entirely clear how you handle drag and drop in your code. You say that your ListBoxes are all data-bound - which implies that you actually move items from one backing collection to another on drag and drop. If so, ListBox just displays the items in order they are present in the collection. You should either sort them there, or, if sorting is a view-only behavior in your case (i.e. items are actually unordered in data model, by design), you should use CollectionView to wrap your collections, set it up to do the sorting, and bind the ListBoxes to that.
Related
I have a ListView with a GridView in my C# WPF application. The ItemsSource is bound to a ListCollectionView created on the ViewModel from an ObservableCollection<MyClass>. I use the ListCollectionView for dynamic sorting, filtering and grouping, all of which can be adjusted or turned on/off from the View.
When I alter the filter or turn the grouping on/off, all of the visual list view items are recreated, which causes the UI to freeze for about a second. Since I have about 250 items displayed and there are about 10 columns (some of which have cell templates with a progressbar), this comes as no surprise.
Now, I know that the obvious answer is to enable virtualization. This however, brings some undesirable effects, such as scrolling becoming jerky or the scroll-bar changing its size as you scroll (this happens with grouping on, since the groups vary in height and so the virtualizing stack panel can not calculate the total height properly at first).
What I would like to do is to have the ListView keep a visual element for every item in the raw list (un-filtered and un-sorted, i.e. the ObservableCollection<MyClass>) and then only add to or remove from the visual tree depending on the changes in the ListCollectionView.
I hope this solution should boost my app's performance, since I rarely change the raw list, but I often alter filtering, sorting and grouping.
Do I need to create a custom control inheriting from ListView (or GridView) to do this, or is there another way?
Try to use DeferRefresh, this delay automatic refresh until the defer cycle is existed. See if that helps.
CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(SomeListView.ItemsSource);
using (view.DeferRefresh())
{
view.GroupDescriptions.Clear();
view.GroupDescriptions.Add(new PropertyGroupDescription("Country"));
view.GroupDescriptions.Add(new PropertyGroupDescription("Active"));
}
http://blogs.msdn.com/b/matt/archive/2008/08/28/collectionview-deferrefresh-my-new-best-friend.aspx
With a list view containing items I can reorder which gives a nice UI effect.
I have my item source for the list view hooked up to an observable collection. Is there anyway with the framework I could programatically simulate a reorder and give the same effect you would have if you actually reordering?
The only way I've been able to slightly achieve this is to literally clear all the items from my data source then repopulate after shuffling the items, however it doesn't look nice.
Well, you could sort the items in code-behind in a temporary helper array and then according to the order they are in in the sorted one start periodically moving items one-by-one in a small time interval (with DispatcherTimer) withing the collection - remove at it's index and insert somewhere it belongs. I think there is no built-in way to do it in a simpler manner.
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'm working with ListCollectionView objects to display lists of items. Currently, I'm building a screen that has two of these lists - one, completeList, holds all available items, the other one, sortedList, holds a subset of them with the item order being relevant. Possible actions here are
add or remove any of completeList's items to/from sortedList
move items up or down in sortedList
save the content of sortedList.
Now, my question is this: is there any possibility to insert items into sortedList at a defined position?
Right now, they will always be added at the end of the list, regardless of the currently marked item, and I didn't find a way around it - apart from the obvious dirty hack, which would have me store all items after the desired position, remove them from the list, add the new item, then re-add all stored items in the correct order.
Does ListCollectionView offer any such functionality, or is there another CollectionView class that would do the trick?
You're actually asking for something that's a logical contradiction. Suppose I have a ListCollectionViewsorted alphabetically:
American
Continental
Festival
Imperial
Tower
Worldwide
Should I be able to insert Luxor between American and Continental? Not if the view is sorted. There's only one place that item can go. And where it appears in the view is independent of where it might appear in the underlying list.
Without knowing more about your application, it's hard to know exactly what to suggest. But if a collection view is sorted, the way to make an item appear at a specific place in the view is to assign its sort key(s) a value that will, once the view is refreshed, cause it to appear in the desired location.
A fairly trivial (and generic) way of doing this is to add a DateTime property to the data item class, set it to DateTime.Now in the item's constructor, and make it the last sort key that the view uses. Then, when adding a new item, set its other sort key properties to the value of the currently selected item. If you do this, new items will always appear in the appropriate place, so long as you don't change the values of any of the sort key properties.
Having said that, from the other features you want to support, I believe that you shouldn't be using a ListCollectionView at all for what you're calling sortedList. This list isn't sorted. It's ordered, which is not at all the same thing. When the user moves an item up in the list, you actually want to change its position.
What you probably want to do is implement the list as an ObservableCollection, and wrap it in a view model class that exposes Items, SelectedItem, AddNewCommand, MoveUpCommand, MoveDownCommand, and SaveCommand properties. Then you can bind the ItemsSource and SelectedItem properties of a ListBox or ListView to Items and SelectedItem properties in the class, and bind buttons or hyperlinks or whatever in the UI to the commands. The commands will manipulate the Items property, using Remove and Insert, and since the Items is an ObservableCollection, the UI will stay in sync.
I'm trying to find out exactly what happens when the data bound with ItemsSource changes. So which method should I override to be able to make the items animate to their new positions in the sorted list?
From what I found out so far I might have to hijack the LayoutUpdated of the ScrollViewer inside the ListBox? How do I find out what happens there in order to be able to attach an animation to the ListBoxItems' position assignments?
Actually it would be even nicer to do this with some attached triggers and behaviors and not having to extend the ListBox class.
Edit: as it turned out both PagedCollectionView and CollectionViewSource destroys and recreates the item list on sorting, so rearranging the list with an animation doesn't seem to be possible without either implementing sorting manually by moving existing items around in the list or to create some sort of item buffer, both of which sounding like a possible source of weird bugs and performance bottleneck. If anyone has a working solution to these though I'd be glad to get any pointers!