Why doesn't ItemsSource bind to my custom CollectionChanged event - c#

This is the weirdest thing I've ever faced. As in Windows 8 MS removed filtering and sorting from CollectionViewSource, I've had to build my own, called CollectionView<T>. CollectionView has a View property of type IObservableCollection<T>, a custom interface I made just to keep things abstracted. Its definition is pretty simple
public interface IObservableCollection<T> : IReadOnlyList<T>, INotifyCollectionChanged
{
}
Then, I have my internal class which implements this interface:
internal class FilteredSortedCollection<T> : IObservableCollection<T>
{
public event NotifyCollectionChangedEventHandler CollectionChanged;
public void RaiseCollectionChanged(NotifyCollectionChangedEventArgs args)
{
var copy = CollectionChanged;
if (copy != null)
copy(this, args);
}
public Func<IEnumerator<T>> RequestEnumerator { get; set; }
public Func<int> RequestCount { get; set; }
public Func<int, T> RequestItem { get; set; }
public IEnumerator<T> GetEnumerator()
{
return RequestEnumerator();
}
public int Count { get { return RequestCount(); } }
public T this[int index] { get { return RequestItem(index); } }
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Things worked until here. CollectionView filters and orders properly, and the View works as expected. Except when I bind it to a ListView.ItemsSource property it just behaves as if it didn't implemented INotifyCollectionChanged. Nobody listens to the CollectionChanged event (checked with the debugger) and the UI doesn't update with new elements added. But if I add some items and then set the ItemsSource property, the UI updates. Just as if it were a normal, non-observable list.
Does anybody know what can be happening here? I've tried deleting the IObservableCollection interface, so FilteredSortedCollection just implemented IReadOnlyList<T> and INotifyCollectionChanged directly, but it didn't worked.

Your collection needs to implement IList. I just ran into the same issue where I had implemented IList which worked great in my Windows Phone app, but when I attempted to use the view model for Windows 8 application it wasn't honoring the changed events.
I added the implementation of IList to my class and now everything works as expected

Related

Model List<T> and ViewModel ObservableCollection<T> Duplicate Data?

What is the best practice for having a List<T> in the business layer that needs to be displayed on a UI? I currently use databinding with an ObservableCollection<T> in the viewmodel that duplicates the data of the List<T>. The obvious problem of this is when the List<T> is modified in the business layer the ObservableCollection<T> needs to be recreated so the changes of the List<T> are reflected in the UI. This can't be the best way.
I also will not accept using an ObservableCollection<T> in the business layer as an answer.
Thanks!
If you insist on having List<T> and separate events notifying about list modification, then duplication is the only sensible way.
If you have ListChanged event with no details on what was actually changed, you can avoid duplication and just wrap the list in a proxy collection implementing INotifyCollectionChanged interface which will fire appropriate CollectionChanged events in NotifyCollectionChangedAction.Reset mode.
If you have granular ItemChanged, ItemAdded etc. events, then you're effectively duplicating ObservableCollection<T> functionality. In this case, you can wrap your application in a proxy collection implementing INotifyCollectionChanged interface, but which understands your architecture and translates events into appropriate NotifyCollectionChangedAction.
Having ObservableCollection<T> in business layer isn't a bad idea at all. It is a specialized collection which provides common interface to notify about item changes, not some class designed specifically for WinForms or WPF or whatever.
You can implement the INotifyCollectionChanged interface, but if you want to use it in a way that you can hold on your implementation the collection in case implementing a class of your own also holding an implementation of IEnumerable will do a lot of the work for you a for instance is what follows, this is the base class i use for holding all the collections that will be updated, on this implementation there is also an ordering consideration in the variable _ordering:
public abstract class BaseINotifyCollectionChanged<T, K> : INotifyCollectionChanged, IEnumerable<T>
{
Func<T, K> _ordering;
bool _ascending;
public BaseINotifyCollectionChanged()
{
}
public BaseINotifyCollectionChanged(Func<T, K> ordering, bool ascending = true)
{
_ordering = ordering;
_ascending = ascending;
OnCollectionChanged();
}
protected abstract IList<T> GetCollection();
public event NotifyCollectionChangedEventHandler CollectionChanged;
protected void OnCollectionChanged()
{
if (CollectionChanged != null)
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
public void RaiseCollectionChanged()
{
OnCollectionChanged();
}
public IEnumerator<T> GetEnumerator()
{
return _ordering == null ? GetCollection().GetEnumerator() : _ascending ? GetCollection().OrderBy<T, K>(_ordering).GetEnumerator() :
GetCollection().OrderByDescending<T, K>(_ordering).GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _ordering == null ? GetCollection().GetEnumerator() : _ascending ? GetCollection().OrderBy<T, K>(_ordering).GetEnumerator() :
GetCollection().OrderByDescending<T, K>(_ordering).GetEnumerator();
}
}
}
When you have this implementation you can use as you wish and through out all the collections you need on your app, working on some dry for yourself here is one example of the use you can give to this abstract class:
public class Categories : BaseINotifyCollectionChanged<Category, string>
{
long _onCategoryRoot;
public void SetOnCategoryRoot(long categoryId)
{
_onCategoryRoot = categoryId;
RaiseCollectionChanged();
}
protected override IList<Category> GetCollection()
{
Category category = new Category();
return _onRoot ? category.GetRootCategories() : category.GetSubCategoriesOnRoot(_onCategoryRoot);
}
}
When you set a _onCategoryRoot in the class the collection you will be displaying will be updated via the RaiseCollectionChanged() method, so you need to add in your viewmodel a property with the class Categories and set the binding in the XAML.

ObservableCollection along with a repository

I am having trouble with grasping the concept of a ObservableCollection inside MVVM. For start I would like to point out that I am doing this in a Windows 8/Metro App, not WPF or Silverlight.
According to microsoft documentation, this collection has the following usefulness:
"Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed." From what I understand this helps you a lot when binding is involved. On the net I found a lot of simple examples, by creating a ObservableCollection on runtime and then working on it, but I didn't find out what is the proper way of using this collection with a repository.
Let' say I have the following repository interface that is an implementation for a ORM database backend, or a raw ADO.NET implementation
public interface IRepository<T>
{
ObservableCollection<T> GetAll();
void Create();
void Update();
void Delete();
T GetByKey(object key);
}
and a simple ViewModel that use the repository as a model
public class ViewModel
{
private ObservableCollection<Dummy> _obsListDummy;
private RelayCommand _addCommand,_deleteCommand,_updateCommand;
private IRepository<Dummy> _repositoryDummy;
public class ViewModel()
{
_repositoryDummy=Factory.GetRepository<Dummy>();
}
public ObservableCollection<Dummy> ObsListDummy
{
get
{
return _repositoryDummy.GetAll();
}
}
public RelayCommand AddCommand
{
get
{
if (_addCommand == null)
{
_addCommand = new RelayCommand(p => DoAdd();
//DoAdd method shows a popup for input dummy and then closes;
);
}
return _myCommand;
}
}
........
}
My view would be a simple XAML with a grid, also Dummy object has INotifyPropertyChanged implemented.
Right now with this implementation after adding or updating or deleting, the ObservableCollection isn't refreshing, I know I could have put IEnumerable instead, but I dont'see an elegant solution of how would make repository to sync with the ObservableCollection that is in the model, other than subscrbing to CollectionChanged and there you treat all the states, but to it seems that I would repeat myself along with the logic that I do in the repository. And to make matters even worse, let's say I would like to get some push notification from my repository, towards the ObservableCollection.
I hope I was understand about my problem.
Thanks in advance.
You should implement INotifyPropertyChanged on your ViewModel and your ObsListDummy property should inform the ViewModel about changes applied to the collection. So it should look like this:
public class ViewModel: INotifyPropertyChanged
{
// Declare the event
public event PropertyChangedEventHandler PropertyChanged;
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
private ObservableCollection<Dummy> _dummyCollection;
public ObservableCollection<Dummy> DummyCollection
{
get { return _dummyCollection; }
set
{
// Set the value and then inform the ViewModel about change with OnPropertyChanged
_dummyCollection = value;
OnPropertyChanged("DummyCollection");
}
}
}
This whole INotifyPropertyChanged interface and implementation includes some dirty work like declaring event and creating a helper method to raise the event so I would suggest you to use some libraries for that like MVVM Light.
You should use a member of type ObservableCollection to store your Dummy ViewModels. In your Initialize method you read the dummies from the repository, create Dummy ViewModels and put those in the ObservableCollection. Now your view will get updated, when you use Binding to ObsListDummy (and add / remove from that collection, also note that Binding only works with public properties).
Right now, you just have a new ObservableCollection on each read, no events involved, so your View will never know about a change.
Further your ViewModel shall implement INotifyPropertyChanged.

Do a check when List.Add method is called

I have a composite property called Items of type List to an order class. On the GUI the user fills out some fields like Name, Description, Price, Quantity, etc... and then clicks the Add Item button which of course adds the item to the order's list of items. What I'd like to do is create a method that checks the item's IsComplete property which does a check to ensure the required properties are set so that way someone can't just call order.Items.Add(item) if it isn't complete. If it's not I'd like an exception to be thrown if the item's IsComplete property returns false... What would be an easy way to go about this?
This can be achieved by sub-classing List<T> into a derived class, and then overriding the Add method, like so.
public class MyItemCollection : List<MyItem>
{
public override void Add(MyItem item)
{
if (item.IsComplete)
{
base.Add(item);
}
else
{
throw new InvalidOperationException("Unable to add an incomplete item");
}
}
}
Your order class would then have the property MyItemCollection rather than List<T>, like so:
public class Order
{
public MyItemCollection Items { get; set; }
}
You can also use ObservableCollection<T>: http://msdn.microsoft.com/en-us/library/ms668604.aspx
It implements INotifyCollectionChanged: http://msdn.microsoft.com/en-us/library/System.Collections.Specialized.INotifyCollectionChanged.aspx
Since the method Add(T) is not virtual you can't override, it.
ObservableCollection allow to throw an event when an element was added but not to undo this add.
You can implement the interface IList<T> with a List<T> storred internaly and add the desired verification in the method Add(T item) before calling the _list.Add(item) like in the exemple below :
public class MyItemCollection : IList<MyItem>
{
private List<MyItem> _list;
public MyItemCollection()
{
_list = new List<MyItem>();
}
public void Add(MyItem item)
{
if (item.IsComplete)
{
_list.Add(item);
}
else
{
throw new InvalidOperationException("Unable to add an incomplete item");
}
}
//Then you have to implement all the IList interface members...
}
The only problem with this solution is that it require to write a lot of boilerplate code.
If only one class is responsible of the manipulation of your List, you can also decide to implement a method AddToMyItemCollection(MyItem item) in the responsible class. It is even a good practive as it's respect the GRASP pattern protected variation (Instance.getC() is preferable to Instance.getA().getB().getC())

do something before collection changes in observablecollection in wpf

I am not sure what i am trying to achieve is actually achievable or not.
I have an observablecollection with me and its collectionchanged event is already been handled. What i want to do is I want to make some changes in the existing list of objects in the observablecollection just before the collectionchanged event of the observablecollection gets fired. In other words i want to do something to the existing list of objects in the observablecollection before anyone adds or removes any object from the observablecollection. Something like handling the collectionchanging event but unfortunately there is not such event in observablecollection. I hope i have been clear enough.
Since you need to take action before the user changes the collection, I believe your CollectionChangedEvent is happening too late (the collection has already changed).
Instead, consider creating your own collection class which derives from ObservableCollection and then override the Add(), Insert(), and Remove() methods to do your additional processing before calling the base class implementation. You should be able to find examples of that on the web.
Here is some sample code to get you started. It derives from Collection:
public class MyCollection<T> : Collection<T>, INotifyCollectionChanged, INotifyPropertyChanged
{
public MyCollection(Collection<T> list)
: base(list)
{
}
public MyCollection()
: base()
{
}
#region INotifyCollectionChanged Members
public event NotifyCollectionChangedEventHandler CollectionChanged;
protected void NotifyChanged(NotifyCollectionChangedEventArgs args)
{
NotifyCollectionChangedEventHandler handler = CollectionChanged;
if (handler != null)
{
handler(this, args);
}
}
#endregion
public new void Add(T item)
{
// Do some additional processing here!
base.Add(item);
this.NotifyChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, base.Count-1));
this.OnPropertyChanged("Count");
}
}
You have been clear enough and the simple answer is: There is no such event and it is not possible.
The only solution I can think of is to derive from ObservableCollection<T> and implement that functionality yourself, i.e. in your implementation of Add you would first raise the CollectionChanging event and then call the Add method of the base class. You would do the same for all other relevant methods.
Having said all that, I am not really sure, this is the correct way to do it. Can you provide a reason why you would need this functionality?
Actually, the collection changed event in ObservableCollection is fired when (among other things) :
You add an item to the ObservableCollection.
You remove an item from the ObservableCollection.
You clear the ObservableCollection.
When I say "you", that means that if CollectionChanged Event occurs that means that "YOU" (understand : something in you application) has added, removed or cleared the list.
That being said, I guess you just have to find where those actions take place and put your code here...
You could create your own implementation of INotifyCollectionChanged that wraps the collection, listens to the event, changes the collection as appropriate and then sends the event along.
But when you change the collection, another event is raised, so you would have to make sure you're handling those events properly, probably by swallowing them
public class WantDoSomethingBeforeChangeGuy
{
internal WantDoSomethingBeforeChangeGuy()
{
Members = new ImplMembers(this);
}
public ImplMembers Members { get; }
private class ImplMembers : ObservableCollection<Artist>
{
private readonly WantDoSomethingBeforeChangeGuy _owner;
public ImplMembers(WantDoSomethingBeforeChangeGuy owner)
{
_owner = owner;
}
protected override void ClearItems()
{
foreach (var item in this)
{
item.DoSomething(_owner);
}
base.ClearItems(); }
}
}

Watch an object graph for changes

Is there a way to watch an object graph for changes on any object, and do something based on that change?
Lets say I have the following:
public class Main:INotifyPropertyChanged
{
public ObservableCollection<Foo> FooItems { get; }
public ObservableCollection<Bar> BarItems { get; }
}
public class Foo:INotifyPropertyChanged
public class Bar:INotifyPropertyChanged
{
public ObservableCollection<Other> OtherItems { get; }
}
public class Other:INotifyPropertyChanged
What would be the best way to implement some sort of change notification system across all objects? For example an autosave, where any change would trigger the system to serialize the Main class.
Should I have glue code in the Main class watching the BarItems for changes, hooking up to their PropertyChanged? This seems a bit messy, and error prone to me. Is there a better way?
Rather than objects raising their own property changed events, perhaps they could raise a shared event instead. For example:
public class SharedChangeNotifier
{
public static event EventHandler<DataChangedEventArgs> SharedChangeEvent;
protected void RaiseChangeEvent()
{
if (SharedChangeNotifier.SharedChangeEvent != null)
{
SharedChangeNotifier.SharedChangeEvent(
this, new DataChangedEventArgs());
}
}
}
public class Foo : SharedChangeNotifier
{
public int MyProperty
{
get { ... }
set
{
...
RaiseChangeEvent();
}
}
}
You could then attach an event handler to the static SharedChangeNotifier's SharedChangeEvent to be notified whenever any object deriving from SharedChangeNotifier is changed, like this:
SharedChangeNotifier.SharedChangeEvent += (sender, args) => {
DoWhatever();
};
I just read an interesting blog post on that issue at http://www.lennybacon.com/ReBlinderFleckChangeTracking.aspx
The post is in German, but as it's mostly code, it should be OK.
Hope this helps!
The way I have done it in the past was to create a separate ChangeTracker class with a method to Register objects into it. Inside that method, use reflection to explore the registered object, and hook into events on each of its properties that implements INotifyPropertyChanged.
You can then add methods to the ChangeTracker to interrogate the state, e.g. IsDirty(), or even implement INotifyPropertyChanged on the ChangeTracker.
(Be sure to implement and use IDisposable on the ChangeTracker, and drop all the event handlers at that time).
You could have the same handler for all items that implement INotifyPropertyChanged events:
foreach (INotifyPropertyChanged obj in FooItems)
obj.PropertyChanged+= this.modified;
// likewise for bar items, and when items are added
private void modified(object sender, EventArgs args)
{
this.Save();
}
edit> To do the same when an item is added:
private void addToList<T>(ref List<T> l, T item) where T : INotifyPropertyChanged
{
item.PropertyChanged += this.modified;
l.Add(item);
}
call it using:
Foo item = new Foo();
List<Foo> fooItems = new List<Foo>();
addToList<Foo>(ref fooItems, item);

Categories

Resources