Cascading IPropertyChanged - c#

I have a data structure organised as such:
A List<Graphic> containing a List<Symbol> which contains a List<Alias> amongst other things.
I want to be able to run a function within the Graphic class whenever anything changes within an alias/symbol/graphic. The best way that I can see to do this would be to implement IPropertyChanged on each of the three classes. However, is it possible to cascade these whilst getting a reference to the Graphic as to what exactly changed?
Note: The changes will generally be to the properties within an Alias but it is just as plausible that a Symbol could be removed/added or renamed.

You can leverage class ObservableCollection<T> that implements INotifyCollectionChanged and INotifyPropertyChanged
Basically, you need to create a derived class and override some methods
public class Data
{
public ObservableCollection<String> InnerCollection { get; set; }
}
public class collection : ObservableCollection<Data>
{
protected override void InsertItem(int index, Data item)
{
item.InnerCollection.CollectionChanged += InnerCollection_CollectionChanged;
base.InsertItem(index, item);
}
private void InnerCollection_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
//Actually it does not make any sense. You may need to construct something special. But firing an event it would be enough
OnCollectionChanged(e);
}
protected override void RemoveItem(int index)
{
var date = base.Items[index];
date.InnerCollection.CollectionChanged -= InnerCollection_CollectionChanged;
base.RemoveItem(index);
}
}
Using something like this, you can nest your events as deep as you want.

Related

Some circular, repeated event handling here? But what and how?

How can I avoid this warning message in VS2017 (or: what may be happening):
'ObsCol<T>.ObsCol()' contains a call chain that results in a call to a virtual method
defined by the class.
Review the following call stack for unintended consequences:
ObsCol<T>..ctor()
ObservableCollection<T>.add_CollectionChanged(NotifyCollectionChangedEventHandler): Void
this is the code:
[Serializable]
public class ObsCol<T> : ObservableCollection<T>
{
public ObsCol()
{
this.CollectionChanged += new NotifyCollectionChangedEventHandler(ObsCol_CollectionChanged);
}
private void ObsCol_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
MainViewModel.IsDirty = e.NewItems != null || e.OldItems != null;
}
}
The purpose of this all is to know in the ViewModel whether or not the calculation output corresponds to the input data.
Upon entering the constructor, CollectionChanged is empty, so my handler is the only thing in the list.
Changing CollectionChanged does not change the collection, does it?
Things seem to work alright, though...
The Problem: some subclass could override the event, leading to unexpected construction behavior
public class Boom<T> : ObsCol<T>
{
public override event NotifyCollectionChangedEventHandler CollectionChanged
{
add { throw new NotImplementedException(); }
remove { }
}
}
Solutions:
Use OnCollectionChanged override
public class Solution1<T> : ObservableCollection<T>
{
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
DoSomething();
base.OnCollectionChanged(e);
}
...
}
Seal the class
public sealed class Solution2<T> : ObservableCollection<T>
{
public Solution2()
{
this.CollectionChanged += new NotifyCollectionChangedEventHandler(ObsCol_CollectionChanged);
}
...
}
Seal the event
public class Solution3<T> : ObservableCollection<T>
{
public sealed override event NotifyCollectionChangedEventHandler CollectionChanged
{
add { base.CollectionChanged += value; }
remove { base.CollectionChanged -= value; }
}
...
}
What you do depends on your use case... the first solution is most flexible, since you can still create subclasses with all kinds of override.
With the SerializableAttribute in your question, it might be worth to seal the whole class since you probably don't want to serialize sub-types anyway (Solution 2).
If you are ever in a situation, where the OnEvent/RaiseEvent method is not available for whatever reason, Solution 3 allows to seal only the minimal subset of functionality that is used in the constructor.

c# How to call and handle a custom generic event arg?

hoping someone could lend a hand here. I have a custom even argument that contains a collection. I can build the event arg but I cannot implement it. I want to put the event handler in the base class and override it in my derived classes.
The even arg:
public class ImportEventArgs<T> : EventArgs
{
public IEnumerable<T> Data { get; }
public ImportEventArgs(IEnumerable<T> data)
{
Data = data;
}
}
The part in my base class that is not resolving:
public virtual void EventHandler<EventArgs> ImportComplete;
The override:
override void EventHandler<WellPathImportEventArgs<WellPath>> ImportComplete;
I figured this is not the correct way of doing this, could anyone point me in the right direction?
You cannot change a method's signature when you override it.
However, since all event args derive from the same type, you don't have to. Just keep the existing method definition and cast the argument, like this:
public override void ImportComplete(object sender, EventArgs e)
{
var myEventArgs = e as ImportEventArgs<WellPath>;
if (myEventArgs != null)
{
foreach (var item in myEventArgs.Data)
{
//Your code here
}
}
}

C# generic type argument from child class, possible?

I guess the title doesn't really say a lot, so here's the explanation.
I have a parent class, which I want to hold an event Action, where T is the type of whatever child class inherits it.
e.g. something like this:
abstract class ActiveRecord
{
protected event Action<T> NewItem;
}
class UserRecord : ActiveRecord
{
public UserRecord()
{
NewItem += OnNewItem; // NewItem would in this case be Action<UserRecord>
}
private void OnNewItem(UserRecord obj)
{
// Flush cache of all users, since there's a new user in the DB.
}
}
So the question is, is the above possible, and what would I use for T in the Parent class ?
Note that I don't want a generic argument to my parent class, since that'd really look stupid, and thus hinder the readability.
class UserRecord : ActiveRecord<UserRecord>
I'm working on an inheritance based ORM, in case you wonder what it's for. The NewItem event is raised whenever the ORM inserts data into the database.
Edit: Trying to clarify a bit better, with correct naming of the classes.
The ActiveRecord class resides in a separate assembly, and the UserRecord resides in a web project I'm working on. (Note: I'm the developer of both parts, however it's two separate projects)
I want to be able to raise events upon succesfully inserting a row, deleting a row, and so forth. The events should pass the record in question as argument to the event handler, hence the need for Action.
However as I'm declaring these events in the base class (ActiveRecord), I need some way to pass the CHILD type to the event handlers, in order to get a strongly typed object.
I could of course go Action and then do a typecast in my implementation, but that really wouldn't be very nice :-(
Hope this clears up the question a bit, otherwise please ask :-)
Edit 2: Just wanted to let you know reflection, dynamic methods or anything similar is an option, if that'd help. However I doubt it for this particular scenario :-(
I wouldn't really recommend it, but you could use a C# version of the curiously recurring template pattern:
class Parent<T> where T : Parent<T>
{
protected event Action<T> NewItem;
}
class Child : Parent<Child>
{
public Child()
{
NewItem += OnNewItem;
}
private void OnNewItem(Child obj)
{
// Do something
}
}
In practice, this will be pretty hard to use sensibly; there must be a better solution to the overall design, in all honesty.
Try this:
abstract class Parent<T>
{
public event Action<T> NewItem;
}
class Child : Parent<Child>
{
public Child()
{
NewItem += OnNewItem;
}
private void OnNewItem(Child obj)
{
// Do something
}
}
If you can't make Parent to be abstract, you can add one abstract transition class between Parent and Child.
class Parent<T>
{
public event Action<T> NewItem;
}
abstract class Transition<T> : Parent<T> { }
class Child : Transition<Child>
{
public Child()
{
NewItem += OnNewItem;
}
private void OnNewItem(Child obj)
{
// Do something
}
}
Actually I think I just found a much better approach:
abstract class ActiveRecord
{
protected virtual void OnNewItem() {}
}
class UserRecord : ActiveRecord
{
protected override void OnNewItem()
{
// Still got the UserRecord object, now it's just "this"
}
}
This seems much more clean to me, so I'll be using that instead.

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);

Binding a BindingList to a collection

I have a library that returns a collection like this:
public IEnumerable Alerts { .. }
and I want to turn this collection into a BindingList for use in the GUI. What is the best way to keep a BindingList synchronised with an IEnumerable collection?
edit: For this problem, assume I have no control of the library and the actual implementation uses a List.
But I do not want to touch this code.
This library also has a nice interface with AddAlert, RemoveAlert etc. What is the best way to keep the GUI synchronised with all these changes?
Assuming the class you are wrapping exposes things like Insert, you should just be able to derive from BindingList<T>, overriding a few key methods - something like:
class MyList<T> : BindingList<T>
{
private readonly Foo<T> wrapped;
public MyList(Foo<T> wrapped)
: base(new List<T>(wrapped.Items))
{
this.wrapped = wrapped;
}
protected override void InsertItem(int index, T item)
{
wrapped.Insert(index, item);
base.InsertItem(index, item);
}
protected override void RemoveItem(int index)
{
wrapped.Remove(this[index]);
base.RemoveItem(index);
}
protected override void ClearItems()
{
wrapped.Clear();
base.ClearItems();
}
// possibly also SetItem
}
This should result in the lists staying in sync as you manipulate them.

Categories

Resources