Bind ObservableCollection as source for different CollectionViewSource - c#

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.

Related

Using ViewModels in ObservableCollections in Prism

As far as I know, the default way to use a ObservableCollection that is bound to a listview is with model classes as elements (ObservableCollection<MyModel>). So when a listview element is selected, we use NavigateAsync and pass the model, which then can be used by the ViewModel to "fill itself".
The problem with this approach is, that it's not possible to use ViewModel properties for binding in the listview.
For example:
I have a View, ViewModel and Model "PickList", which contains a collection of "PickLine" objects - each having a View, ViewModel and Model themselves. The PickLine object contains a property "PickedQuantity" and a property "OpenQuantity". Now in my PickList view, I don't want to bind these two to separate items (e.g. two labels), but I want to have one label to display both I a format like for example "PickedQuantity / OpenQuantity". I know this example can be solved by using multi binding or something like this. But that's not the meaning of it all.
My PickLine ViewModel already has a property "QuantityString", that I want to bind to the label of a listview element via DataTemplate. But how can I do this. Is it even possible?
Make a property that combines the two other properties and bind to that. E.g.:
public string FullQuantity {get {return $"{PickedQuantity} / {OpenQuantity}";}}
Then in the setter for PickedQuantity and OpenQuantity, you will want to call whatever PropertyChanged method you have set up to notify the bindings of a property change and pass in the FullQuantity property name so elements that are bound to FullQuantity get updated when either PickedQuantity or OpenQuantity are changed.
This way, you are only binding one label's text to one property and that label would get updated when either of the two quantity properties are changed.
Note: I am unfamiliar with Prism, but this approach should work regardless of the Mvvm framework in use.
Your PickListViewModel should expose a collection property whose items are of type PickLineViewModel (not PickLine).
Whether you need an ObservableCollection<PickLineViewModel> depends on where changes can happen - in service / model that initially created the PickLines or in the GUI or both. In any way, you have to make sure the changes are propagated from one side (the collection of view models) to the other (the collection of models). Google wrapping observable collection as a starter (hint: avoid two-way sync if possible). These blog posts are old but still relevant and make a good reading. A trivial wrapping is described in this answer.

Applying a Storyboard when a item changed using ListCollectionView

I'm refactoring a WPF application that before was using a ObservableCollection<T> as datasource with a ListCollectionView for performance purpose.
The main idea is that I've a ItemHolder which has a ObservableCollection<T> that contains all the data and each view of my application shows only a part of it (based on an IdParent).In this case ListCollectionView works fine since I set a filter on the ItemHolder.Items and I get the data.
I wish to execute a storyboard when a record is updated inside this collection (adding an item works since I see it on the GridView).
Is there a way I can get a storyboard applied when a item is changed? I've seen no SourceCollectionChanged (or something similar) as event of the ListCollectionView.
I have never worked with ListCollectionView before so I cannot tell you anything new. But I can suggest to create a custom control that inherits from ListCollectionView and then implement an CollectionChanged event to handle when an item is added to the custom control and trigger the animation from withing the user control.

Binding SelectedItem vs SelectedIndex - When should I choose one over the other?

Let's say that you have an observable collection of object type Foo, and you have a custom ListView that the user will select from.
Your bound data object:
// property with getter / setter / INotifyPropertyChanged
ObservableCollection<Foo> MyCollection;
In XAML:
<ListView ItemsSource={Binding MyCollection} />
Is it more appropriate to bind to the SelectedIndex in XAML and create the following in your data object:
int SelectedIndex { get; set; } // also raising property changed notifications
Foo SelectedObject
{
get { return MyCollection[SelectedIndex]; }
}
Or to create this and bind to the SelectedItem in XAML:
Foo SelectedObject { get; set; } // also raising property changed notifications
And why?
Both cases are acceptable, however which you choose usually depends on the design of your data models, and which method would require the least amount of code to get working.
A few rules I use to determine which one to select
If SelectedObject cannot be null (such as an enum), and you need to default to be no item selected, then use SelectedIndex.
If SelectedObject may not be considered .Equals() to any item in your Items list, use SelectedIndex. This is because SelectedItem compares objects with the Items collection using .Equals(), so a reference comparism will return false which will result in your object not becomming selected.
This usually happens when your selected item comes from a different location than where your list of items is. For example, one database calls to load the Items for the list, and a separate database call obtaining an object that includes a SelectedObject property.
If you need to reference only one of SelectedObject or SelectedIndex in other parts of your code, use that one.
If your data models already have a SelectedIndex or SelectedObject property, then use that.
If all other things are equal, I use a SelectedObject property to bind the SelectedItem property.
This is because to me it makes more sense to refer to something like SelectedUser instead of SelectedUserIndex in the code behind, and I prefer to avoid looking up the item in the collection anytime I want to do something with the selected item.
Depends on your needs. If you need to set the selected item, you need the second version. If you need the selected item's index, you need the first version. If you need neither, it's up to your personal preference.
Both approaches are fine. Use the one you find easier to use. I don't find myself relying on indices often, but your cases may be different.
If you look at MVVM libraries, Caliburn.Micro, for example, expects you to use SelectedObject approach. It explicitly supports this convention.
And note on collection properties. In general, it's better idea to have read-only auto-properties and set them in constructor (property change notification won't be necessary). It is not advised to change instances of collections. While WPF supports it, it's much easier to subsribe to collection events in your own code if the instance doesn't change.
I personal think bind to the SelectedObject is much cleaner unless you need access to the SelectedIndex
The only time I use SelectedIndex is when I want to set a default value of 0 to select the first item.

ComboBox Binding Breaks on Changes to object in SelectedItem

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.

WPF Databinding Magic

I have ObservableCollection<Foo> that is bound to an ItemsControl (basically displaying a list).
Foo mostly looks like this (there are other members but it doesn't implement any interfaces or events):
class Foo
{
public string Name { get; set; }
//...
}
When the user clicks on an item I open a dialog where the user can edit Foo's properties (bound to a small viewmodel with a Foo property for the selected item), the Xaml looks like this:
<TextBox Text="{Binding Foo.Name,Mode=TwoWay}"
Grid.Column="1" Grid.Row="0" Margin="2" />
The really strange thing is, when the user edits the name, the value in the list changes! (not while typing but after the focus leaves the field)
How does it do that? I haven't implemented the INotifyPropertyChanged interface on the Foo object!
So far I checked that it doesn't just refresh the whole list - only the selected item. But I don't know where I could set a breakpoint to check who's calling.
Update: thanks to casperOne for the link to the solution! I'll add a summary here in case it goes 404:
[..] actually you are encountering a another hidden aspect of WPF, that's it WPF's data binding engine will data bind to PropertyDescriptor instance which wraps the source property if the source object is a plain CLR object and doesn't implement INotifyPropertyChanged interface. And the data binding engine will try to subscribe to the property changed event through PropertyDescriptor.AddValueChanged() method. And when the target data bound element change the property values, data binding engine will call PropertyDescriptor.SetValue() method to transfer the changed value back to the source property, and it will simultaneously raise ValueChanged event to notify other subscribers (in this instance, the other subscribers will be the TextBlocks within the ListBox.
And if you are implementing INotifyPropertyChanged, you are fully responsible to implement the change notification in every setter of the properties which needs to be data bound to the UI. Otherwise, the change will be not synchronized as you'd expect.
This is a total guess, but I'm thinking that because you have two-way binding enabled, WPF is now aware of when it makes changes, and will update other items it knows is bound to the same instance.
So because you have changed the value of the Name property through the textbox, and WPF knows when you have changed that value, it does its best to update whatever else it knows is bound to it.
It uses reflection to set the value of that property. INotifyPropertyChanged is only needed if the TextBox needs to be informed of a change in the Name property of the Foo class.
Because they're databound to the same object. If you change the binding to
{Binding Foo.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}
then they'll be in synch when the user types in the textbox.

Categories