What are the best practices with Databinding and ObservableCollections in WPF? - wpf

I am using LinqToSql as my datasource, let’s say I query a list of programmers from a table and then loop through that data populating an ObservableCollection. (First question, is this part wrong? It just seems weird to query data into a list and then loop through it to populate another list)
Next, when I databind my ObservableCollection of programmers to a ListBox. Then I bind the selectedItem of the Listbox to a TextBox. I understand that when I select an item in the ListBox the textbox will be updated and when I make a change in the textbox, the ListBox will get updated and as a result the ObservableCollection that the listbox is bound to will also be updated.
What I don’t completely understand is what the best practice is to get the data from the OC back into my original datasource. Do I just loop through the observable collection commiting my changes by updating each record? That doesn’t seem terribly efficient.
Thanks so much for your help!
-Josh

This is purely a view-model issue. You have complete control over all the objects and properties on the data side of the fence. Sometimes you have a simple arrangement where the user can only edit a single record, databinding does all the work, and you just write it back to the database when the user clicks save.
If you have a lot more data being displayed and any one piece of it can be modified by the user, then it is probably wise for you to keep a dirty flag in the object itself that records whether any of the properties have been changed. That way, you can efficiently save back just the modified entries.
Since your objects probably support INotifyPropertyChanged and your collections are observable, you can even automatically detect and manage the dirty flag. Or it might be simpler to just set dirty to true in all of your setters.
In any case, this information can even be useful to the user. For example, a user-interface can show unsaved records in bold or with some other convention.

The ObservableCollection is not the only one collection which you could use in wpf. But it is standard collection allows wpf to automatically update ui item containers like ListBox on collection changes like addition or removal of an item. You can't do it with List.
When the user modifies a textbox in ListBoxItem the ObservableCollection is not updated cause you don't add or remove or reorder items in the collection. You change the property in one of the items.
The ListBox contains a list of ListBoxItem containers, one for each item in collection specified as an ItemsSource.
The DataContext of each ListBoxItem is set to the corresponding item stored in ObservableCollection. So, if you change the text in TextBox in code, the binding engine will change the property of that item specified for TextBox.Text in binding. Nothing to change or update for the ObservableCollection object.
In the setter of such item's property the PropertChanged event of INotifyPropertyChanged interface are usually raised. That is also the usual the place to set up a dirty flag. Then you could also commit changes immediately from there, keep some list of dirty objects or search for them on commit like:
items.Where(item => item.IsDirty)
Also there are good tools like Snoop and WPFInspector, which greatly help you to understand the wpf app visual tree and datacontexts for each element

Related

WPF MVVM DataGrid ItemsSource binding to different collection

I have a Window with a dynamic menu and a DataGrid that shows different records based on the which menu item has been clicked.
Each menu item returns an ObservableCollection of a custom class.
All the classes are diffent and not necessarily related to each other.
At the moment I created one ObservableCollection(Of Object) in my ViewModel, filling it with new results everytime a menu item is clicked.
The problem is that Object does not implement INotifyPropertyChanged, forcing me to manually assign the ItemsSource.
I'm sure there is a way to accomplish what I'm after, but I cannot think of it.
You have two options:
clear the ObservableCollection, then add the new items to it. This can be slow though as there are multiple notifications (events) fired when you do it, if it's any more than a few tens of items then you'll start to notice some UI slow downs while you do it
ensure that your viewmodel implements INotifyPropertyChanged and the property that contains the ObservableCollection fires a property change notification. Then simply assign a new ObservableCollection when you have a new list to show.
Because your collections contain different types of objects, I trust you've looked into Data Templating (another example) to structure your UI - that way you can have a custom layout that is dependent on the type of the list item.

PropertyGrid-like multiselection in other elements(listView)

i want to immitate the behavior of multiselection of items (eg. in a list view) and their appearence in a PropertyGrid. So, obly the same properties are shown and values set in the grid are set on all multiselected properties.
I have created a listview with some datatemplates and have in another listview some data(with databinding). Now i want wo have a multiselection on the listview with the data and have the properties shown in the another listview like in a propertygrid.(explanation: The ProprtyGrid is still not available in WPF and if has not the flexibility of showing data the way i need it)
So, how do i need to prepare my data to be shown in the list propertygrid-multiselection-style? Is that even possible???
Greets,
Jürgen
I'm not sure if this question is still active, or what not. But I have found a solution that works for me - and I need somewhere to share it.
The first issue you are going to have solve is selecting the items, ie. multi-select. See my tip here that explains how to do that.
Next, I guess its basically binding to listview's selectedItem property and how you want to update your propertyGrid based on changes to the selectedItem prop.
Hope this helps.

How can I set a datatrigger to select\unselect listbox items?

This issue has been plaguing me for a while, and I just can't get it right. I have multiple listboxes each bound to different lists. In the end, I just want to be able to keep it so only one item is selected at a time across all the lists. Any ideas?
This has got to be very simple, but I have had no luck. I tried the idea of a gloabl\static DependencyObject to match against each ListBoxItem's Tag property, but have trouble getting the datatrigger to fire for each listbox when the value is updated.
For example, if my form has different ListBoxes, each with their own backing list collections and their own DataTemplates, I want to be able to select an item from one list, and have it be the only selected item. Which means I want the others to unselect. I tried binding the ListBoxItem IsSelected Property to a MultiBinding that compares the unique ListBoxItem value to a global value, but had no luck there.
As far as I understand, IsSynchronizedWithCurrentItem works for lists using the same backing collection.
ok, here's a try:
get the collectionviewsource.View of each of your lists (if you don't use these already, get the default views). In the View you can subscribe to CurrentChanged and in there for each other lists' CollectionViewSource.View.MoveCurrentToPosition(-1);

DataGrid Sorting retention on ItemsSource Changed

I am using an MVVM approach.
I have a ViewModel and View called AllSomethingViewModel and AllSomethingView. The View Model contains a list of SomethingViewModels and a SelectedViewModel. The View contains a usercontol bound to the AllSomethingViewModel's SelectedVM property and a listbox control that lets me select a VM. Basically when I pick a new VM the usercontrol's DataContext changes and so the view associated with SomethingViewModel updates with new information.
SomethingViewModel contains a list of objects called ObservableCollection(DataPoints) data.
I have a DataGrid bound to data and columns defined that are bound to data's members. This works fine. I can change views and this datagrid updates and everything is nice.
The issue I am running into is that I would like whatever sorting is applyed to the datagrid to persist when the datacontext changes.
On the View associated with SomethingViewModel, I can subscribe to DataContextChanged event but I'm not sure what To do from there to get the sorting to apply.
For Example. I have 2 SomethingViewModels. So in my list there are 2 options. When i pick the first one I get my datagrid with my data. In the datagrid I decide to sort by DateCreated Ascending order. Then I go to my second VM, the datacontext changes so the data in the grid is updated but it is no longer sorted!
If your sorting is done by the DataGrid then it is stored in the ICollectionView that the DataGrid uses to display its data.
ICollectionView view = CollectionViewSource.GetDefaultView(myDataGrid.ItemsSource);
// Sorting is found in view.SortDescriptions
There's an example of setting your sorting in code here. Hope that's enough to get you going in the right direction

Append Items to Databound ItemsControl in WPF

I've got a Combo Box that is databound to an ObservableCollection of items. I would like to have a default selected item that is (None) that would set the value of the property I've bound to "SelectedValue" to null.
I think there ought to be a way to achieve this with some combination of Style/DataTemplate/TemplateSelector. I'm trying to design this with MVVM in mind, so I'd like something that doesn't use codebehind and is as reusable as possible. I'd also like the benefits of the ObservableCollection (updating the collection causing the control to rebind) to remain intact.
Bonus part B:
I would like to also be able to append an extra visual element to the bottom of an ItemsControl as well. I was thinking it would be easy to change the DataTemplate if I knew how to trigger it on the last item of a collection. Willing to entertain other options here.
The simplest way I've found to do this is to insert a "special" value into the underlying collection, and display the "(None)" text when it's selected. Obviously then you need to run your binding through a converter to take this value into account and return null when it's selected. (See this question of mine which was a result of me trying to add an actual null value to a ComboBox's underlying collection.)
Having said that, it might actually be possible to do what you want with the CompositeCollection class. You could make a separate collection (with only one item - your Null item) and bind your ComboBox to both it and your original collection through the CompositeCollection.

Resources