Two ListViews with different filters on the same data set - wpf

I have a ViewModel with some ObservableCollection _questions, which is loaded from DB when VM instance created. Also this list is used to collect data to save back to DB.
This Vm is used for a View1 and it displays the list in ListView with filtering by a property using CollectionViewSource.GetDefaultView(_questions).Filter = ...
Now I need to create View2 which will display same list but without filtering.
I can't bind it to the same ObservableCollection _questions because it has filter defined on CollectionViewSource, but I need to use it to keep SaveToDb code same.
Is it possible to have different filtering on the same data source for two different ListViews?

I have never enjoyed using CollectionViewSource. I would instead filter using a new property in my ViewModel that filters using Linq:
public IEnumerable<MyType> FilteredItems()
{
get
{
return MyCollection.Where(x => x.MyProperty == SomeValue).ToArray;
}
}
I would then bind ItemsSource to this property and use INotifyPropertyChanged event to notify UI of changes to the collection.
Its hard to tell if this fits your scenario well as there is not much information provided on what you need to achieve.

Related

wpf databinding to downloaded data

I am having a bit of trouble understanding the correct way to do the following:
The data I am binding to exists on the internet as a json file. On a timer tick, I download it and using a JavaScriptSerializer, I deserialize it into a class.
Now, I want to bind to that data but when I deserialize, it creates a new class, so my binding breaks (meaning I have to set the ItemsSource or DataContext again).
Does anyone know a way around this?
Thanks!
What is the control that you are trying to bind your data to? If you can bind an observable collection as your data source, all you need to do is to clear your observable collection before fetching the data, and just add the fetched record to the collection post deserialization.
If you do not use ObservableCollection, you can add public properties to your ViewModel and just refresh those when you get the data back. This will ensure the refresh happens becuase your view is bound to public properties of your view model and not aware of the object returned from the call.
After binding deserialized data you should call PropertyChange event of the property you want rebind. In case the selected value is in the new (deserialized) collection it should be reselected.

WPF: Which collection class to use

After spending a whole day trying different suggestions, I'm back at square 1. I'm trying to bind my view, a XAML Window, to one of my ViewModel properties, say, SalesOrders. The ViewModel in turn talks to the Model (an EF Model on top of a database). The question I'm facing is the collection type that I should use to expose my SalesOrders property.
I have tried the following types, none of which does all of what I need.
List<T>
ObservableCollection<T>
BindingList<T>
CollectionViewSource on top of the above
Here's what I need my collection to do:
The view has Previous/Next buttons, so the collection should provide some sort of currency manager.
There's a Save button in the view, which I need to get enabled/disabled immediately based on whether the SalesOrder collection has any changes. Since SalesOrder is already an EF type, all of its fields implement INotifyPropertyChanged.
CollectionViewSource provides me with navigation methods (previous/next) but doesn't listen to PropertyChanged events, so modifying data in the view doesn't turn the Save button on. BindingList can listen to PropertyChanged events, but doesn't provide navigation methods. ObservableCollection lacks both functionalities.
TIA.
Why don't you use ObservableCollection<T> then subscribe to the CollectionChanged event to enable or disable your save button as outlined in the answer of the thread MVVM ObservableCollection Bind TwoWay.
According to MSDN about CollectionView here:
In WPF applications, all collections have an associated default
collection view. Rather than working with the collection directly, the
binding engine always accesses the collection through the associated
view. To get the default view, use the
CollectionViewSource.GetDefaultView method. An internal class based on
CollectionView is the default view for collections that implement only
IEnumerable. ListCollectionView is the default view for collections
that implement IList. BindingListCollectionView is the default view
for collections that implement IBindingListView or IBindingList.
Which means you can use BindingList for SalesOrders and bind it in the View, then to manage the navigation you can access its automatically created CollectionView from the ViewModel with:
myCollectionView = (BindingListCollectionView)CollectionViewSource.GetDefaultView(this.SalesOrders);

How can I determine if my BindingSource's List has changed?

I have a BindingSource which I use to bind to a grid. The binding source itself binds to a custom class. For instance
MyGrid.DataSource = MyBindingSource
'Bind the Binding source to data
For each classInstance as myClass in MyCollection
MyBindingSource.List.Add(classInstance)
Next
A user may add or delete items to/from this List. My aim is to save this updated List to the database. I need to determine if my binding source's list has changed (i.e. has items added to it, or has items removed from it).
I am aware that I could implement the INotifyPropertyChanged on my custom class, and exploit the OnPropertyChanged event, but my classes' property would never change in my case. The other solution that I can think of is to use the BindingSource's ListChanged event, and maintain a collection of all the added and the deleted rows there. Although this approach may work for me, I reckon its a bit flaky.
Does the binding source or a Collection (such as the IList in my case) provide any other properties that can help me determine the above?

How do I structure MVVM with Collections?

I'm having trouble understanding how to apply the MVVM pattern when Lists/Collections are involved.
Say the MainModel has a few properties and methods, as well as a list that contains other DetailModel objects. The DetailModel objects can be added, removed, or re-ordered.
The MainView will show a few controls related the the root model, and have a ListBox populated from the list. Each item will have it's own sub-view via a DetailModelView UserControl.
Finally, there is a MainViewModel. This has properties backed by the MainModel's properties and methods, bound to the Main View, with change notification keeping everything in sync. (Up to this point, I am comfortable with the pattern - more stating this in case there is something fundamental I am missing...)
When it comes to handling the list, I get confused. I have come across several examples where the MainViewModel simply exposes the list of DetailModels to the view, and the DetailModelViews are bound directly to the models. This functions, but is problematic. It does not consistently following the pattern (no DetailViewModel exists), and it drives me to include some UI-related code in my detail models. It seems clear to me that the MainViewModel should expose a list of DetailViewModels for the UI to bind, but I am stuck on how to implement such a thing!
How should manage the two lists (DetailModels and DetailViewModels)? I am really confused as where I initially populate the DetailViewModel list, and how I should handle adding, removing, or changing the order of the items to keep them synchronized!
Usually Models are nothing more than data objects. They shouldn't contain any code to do things like add/remove items from a list. This is the ViewModel's job.
In your case, I would create a MainViewModel that has the following properties:
ObservableCollection<DetailViewModel> Details
ICommand AddDetailCommand
ICommand RemoveDetailCommand
If your MainModel class is a data object, you can either expose it, or it's properties from the MainViewModel as well. Exposing it's Properties is the "MVVM purist" approach, while exposing the entire Model is sometimes more practical.
Your MainViewModel is in charge of creating the initial list of DetailViewModels, and it is in charge of Adding/Removing these items as well. For example, in the PropertyChanged event for the MainViewModel.MainModel property, it might rebuild the MainViewModel.Details collection, and the CollectionChanged event for the MainViewModel.Details property would update MainViewModel.MainModel.Details
You are right to have a separate DetailModels list and DetailViewModels list. The DetailViewModels list should be a property of type ObservableCollection<DetailViewModel>. You can populate the observable list when you set the Model (or at construction time, if you pass the model into the constructor of your ViewModel.)
private ObservableCollection<DetailViewModel> m_details;
public IEnumerable<DetailViewModel> Details
{
get { return m_details; }
}
You can the subscribe to m_details.CollectionChanged. This is where you can handle re-ordering the contents of the list in the Model.
I hope this helps.
In my experience, the only time you get away with exposing model objects to the view is if you're doing simple read-only presentation, e.g. displaying a string property in a ComboBox. If there's any kind of actual UI involving the object (especially one involving two-way data binding), a view model is needed.
Typically, a master VM's constructor will look like this:
public MasterViewModel(MasterModel m)
{
_Model = m;
_Detail = new ObservableCollection<DetailViewModel>(m.Detail);
}
where MasterModel.Detail is a collection of DetailModel objects, and _Detail is a backing field for a Detail property that's exposed to the view.
As far as adding, removing, and reordering items in this list is concerned, in the UI at least this will be done through commands on the MasterViewModel, which must manipulate both MasterModel.Detail and MasterViewModel.Detail. That's a bit of a pain, but unless you want to repopulate MasterViewModel.Detail after every change to MasterModel.Detail, it's really unavoidable.
On the other hand, if you've been wondering "why would I ever need to write unit tests for view models?", now you know.
Here is an answer that I think addresses this issue very nicely using an ObservableViewModelCollection<TViewModel, TModel>
It's nice and lazy. It takes an ObservableCollection and a ViewModelFactory in the ctor. I like it because it keeps state at the model layer where it belongs. User operations on the GUI can invoke commands at the VM which manipulate the M via public methods on the M. Any resulting changes at the M layer will be automatically handled by the class in this link.
https://stackoverflow.com/q/2177659/456490
Note my comment regarding SL vs. WPF

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