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);
Related
Say I have the class MyObject:
public class MyObject
{
public SomeOtherObject SomeOtherObject { get; set; }
public void MyMethod()
{
//Do something
}
}
Where MyObject.SomeOtherObject is as follows:
public class SomeOtherObject
{
public string Information { get; set; }
}
Assume that the property SomeOtherObject.Information is set by an instance of another class.
How can I automatically invoke MyObject.MyMethod() when MyObject.SomeOtherObject.Information changes?
Define an event callback. In SomeOtherObject;
public EventCallback OnInformationChangedEvent { get; set; }
I am not sure exactly what is setting the Information property. But ultimately you need a line somewhere in you SomeOtherObject class that does this;
await OnInformationChangedEvent.InvokeAsync();
You could even do this in the setter of the property itself.
In the parent MyObject you can pass the method that you want to be invoked. And refine your method as follows;
public void MyMethod(object sender, EventArgs e)
{
//Do something
}
And where you define SomeOtherObject;
SomeOtherObject.OnInformationChangedEvent += MyMethod;
There are other ways to do it (for example defining your own Observer pattern, which I think is what .NET is doing underneath anyway).
I didn't run that code so my syntax might be slightly off, but you should be 99% there with that.
I recommend using events. In the class MyObject, you can specify an event and register an event handler for this event - either use MyMethod as event handler or create another method that calls MyMethod.
Then, rewrite SomeOtherObject:
public class SomeOtherObject
{
public string Information
{
get => _Information;
set
{
_Information = value;
// add code to fire the event
}
}
private string _Information;
}
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.
How can I use C# generics to avoid having to create an extension function like so for each and every auto-generated class (Linq to SQL DBML)?
static public CharacterViewModel ToViewModel(this Character c)
{
return new CharacterViewModel(c);
}
Having a function like this provides a fairly clean way of selecting a set of items from the DB as their corresponding ViewModel, like so:
var characters = new ObservableCollection<CharacterViewModel>(from p in DB.Characters
select p.ToViewModel());
I'd like to see something like:
static public T ToViewModel<T,K>(K dbmlClass)
{
return new T(dbmlClass);
}
But I have a feeling this will involve Reflection-style object generation and I don't know how efficient that would be (or how to accomplish it).
By the way, I did previously investigate operator overloading the assignment ('=') as a possible solution, which could provide implicit casting, but I believe this would require overloading the = in the auto-generated class which I am not able to do.
Update
Thanks all, for the answers. I think I have a few avenues to check out now. To provide a bit more context, as some mentioned it wasn't clear. All of my ViewModels are derived from the following:
public class BaseDO<T>: BaseDO
{
public BaseDO(T model)
{
Model = model;
}
public T Model { get; set; }
}
abstract public class BaseDO: INotifyPropertyChanged, INotifyDeleted
{
#region Standard INotifyPropertyChanged Implementation
public void NotifyPropertyChanged(string propertyName)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged = (o, e) => { };
#endregion
public event EventHandler OnEntityDeleted = (o, e) => { };
public void NotifyEntityDeleted()
{
OnEntityDeleted(this, new EventArgs());
}
}
public interface INotifyDeleted
{
event EventHandler OnEntityDeleted;
void NotifyEntityDeleted();
}
You could use a library such as Automapper. With this you have 1 place where you setup the configuration of how objects are mapped. You can then have:
public static T ToViewModel<T,K>(K dbmlClass)
{
return AutoMapper.Mapper.Map<T>(dbmlClass);
}
// Register mappings
public static void ConfigureMappings()
{
AutoMapper.Mapper.CreateMap<Character, CharacterViewModel>();
}
It's not really clear from your question whether you want to generate the view model classes automatically or whether you've already written them and each one accepts the model as a constructor parameter. If the former then I personally add INPC to the model classes using either Castle Proxy or Frody. If the latter then I believe this is what you're after:
public static class Helper
{
static public T ToViewModel<T>(this object dbmlClass)
{
return (T)Activator.CreateInstance(typeof(T), dbmlClass);
}
}
Which you would then use like this:
var model = new Model();
var view_model = model.ToViewModel<ViewModel>();
I currently have a WPF project that makes use of MVVM. In my project, I make use of a static class which acts as a cache for collections used by the various controls in my app, something like this:
public static class AppCache
{
public static ObservableCollection<MyObject> MyObjects { get; set; }
private static async void GetMyObjectsAsync()
{
await Task.Run(() => GetMyObjects());
Mediator.Instance.NotifyColleagues("MyObjectsUpdatedMessage", true); // A helper class that implements messaging
}
private static GetMyObjects()
{
// Get objects via service call
...
MyObjects = result;
}
static AppCache()
{
MyObjects = new ObservableCollection<MyObject>();
GetMyObjectsAsync();
}
}
I then subscribe to the mediator in my various viewmodels:
public class MyViewModel : ViewModelBase
{
...
public ObservableCollection<MyObject> MyObjects
{
// My ViewModelBase lets me implement INotifyPropertyChanged like this
get { return GetValue(() => MyObjects); }
set { SetValue(() => MyObjects, value); }
}
[Message("MyObjectsUpdatedMessage")]
private void OnMyObjectSourceUpdated(bool c)
{
MyObjects = AppCache.MyObjects;
}
public MyViewModel ()
{
Mediator.Instance.RegisterHandler<bool>("MyObjectsUpdatedMessage", OnMyObjectSourceUpdated);
}
}
The problem I have with this method is that when I do things with the collections in the ViewModels (eg. add or edit a MyObject) I then have to go back and manually update the global AppCache collection and make sure it matches up with what is in the ViewModel, then make sure that I update all of the other ViewModels using this collection to use the new value since there is no binding involved: MyOtherViewModel.MyObjects = AppCache.MyObjects
The alternative is to make GetMyObjectsAsync() public and have the AppCache update itself from the database after I make changes from my ViewModel, then use the Mediator to update all the other views using the collection. I don't like this either as it means I end up making a service call I don't want to.
What I'm trying to figure out is if there is any way to use Reactive Extensions to simplify my process, such that I can have some kind of Reactive Property defined in the AppCache which my ViewModels subscribe to, and which when updated will push its updates out to all the ViewModels, something of a mix between the two options available to me (Manually update the AppShared collection but then have all its subs notified without needing the mediator).
I suppose really what I want is to have a property that is essentially bindable and shareable between ViewModels.
Is there any kind of Reactive Property I can use to achieve this sort of thing? Something like:
EDIT:
I was able to get the subscription to work as follows, but this is again similar to the option of using the Mediator as I have to call NotifyMyObjectChanged whenever I update MyObjects. Is there a way to make MyObjectsObservable 'listen' for changes to MyObjects and automatically call NotifyMyObjectChanged? If there isn't a way to do this, is there a benefit to using RX for this over the Mediator?
public static class AppCache
{
public static ObservableCollection<MyObject> MyObjects { get; set; }
public static IObservable<ObservableCollection<MyObject>> MyObjectsObservable => _mySubject; // C# 6 syntax
public static Subject<ObservableCollection<MyObject>> _mySubject { get; set; }
private static async void GetMyObjectsAsync()
{
await Task.Run(() => GetMyObjects());
NotifyMyObjectChanged() // this basically just replaces the mediator
}
private static GetMyObjects()
{
...
MyObjects = result;
}
private static void NotifyMyObjectChanged()
{
_mySubject.OnNext(MyObjects);
}
static AppCache()
{
_mySubject = new Subject<ObservableCollection<MyObject>>();
GetMyObjectsAsync();
}
}
public class MyViewModel : ViewModelBase
{
ObservableCollection<MyObject> MyObjects
{
get { return GetValue(() => MyObjects); }
set { SetValue(() => MyObjects, value); }
}
IDisposable _subscription { get; }
MyViewModel()
{
_subscription = AppCache.MyObjectsObservable.Subscribe (HandleMyObjectChanged);
}
private void HandleMyObjectChanged(ObservableCollection<MyObject> myObjects)
{
MyObjects = myObjects;
}
public void Dispose()
{
_subscription.Dispose();
}
}
So what you want to do is something like this:
public static class AppCache
{
static AppCache()
{
_mySubject = new Subject<MyObject>();
}
private static void NotifyMyObjectChanged(MyObject object)
{
_mySubject.OnNext(object);
}
public static IObservable<MyObject> MyObjectsObservable
{
get { return _mySubject; }
}
}
public class MyViewModel : ViewModelBase
{
MyViewModel()
{
_subscription = AppCache.MyObjectsObservable.
.Where(x => x == value)
.Subscribe (HandleMyObjectChanged);
}
private void HandleMyObjectChanged(MyObject object)
{
... do work here ....
}
public void Dispose()
{
_subscription.Dispose();
}
}
In this case what you're basically doing here is sending a notification to your view model that something on your MyObject has changed. You can take the object variable from the handler and use that to copy the changed properties into your view model. Alternatively you could send a "message" class that has a list of properties and their new values, that might be a little lighter than sending the entire object.
The other thing to keep in mind is that the Observable might send the "event" on a thread other than the UI thread. There's a Subscribe override that lets you specific a TaskScheduler to use, so you can specify the UI scheduler so you don't have to do the marshalling yourself.
As I said in my comment, there are tons of ways to do this, so you'll have to play around to find something that fits your needs the best, but hopefully this gives you some direction.
UPDATE
So here's a bit of an update to your AppCache code (from your edit), but your question is basically that you want to get rid of the mediator and replace it with RX. In the end, you still end up with a mediator of some sort, you just have to decide whether RX gives you something more than your own implementation of a mediate would. I would (and do) use RX, but it's really a preference thing, so you're going to have to do some research on your own.
This shows you the use of the Observable.FromEventPattern function, which is pretty useful and gives you a nice shortcut:
public static class AppCache
{
ObservableCollection<MyObject> MyObjects { get; set; }
public static IObservable<ObservableCollection<MyObject>> MyObjectsObservable { get; private set; }
private static async void GetMyObjectsAsync()
{
await Task.Run(() => GetMyObjects());
NotifyMyObjectChanged() // this basically just replaces the mediator
}
private static GetMyObjects()
{
...
MyObjects = result;
MyObjectsObservable = Observable.FromEventPattern(h=>MyObjects.CollectionChanged += h, h=>MyObjects.CollectionChanged -= h);
}
static AppCache()
{
GetMyObjectsAsync();
}
}
I wanted to show you the usuage but this code isn't perfect, if a subscriber calls Subscribe before the MyObjectsObservable is created, obviously it would blow up.
Here's another link that shows you a lot of the stuff that RX can do, that to me makes it a great tool: http://www.introtorx.com/uat/content/v1.0.10621.0/04_CreatingObservableSequences.html#
Lets say I have the below code. What is the difference between assigning the actions directly and subscribing to an event?
//Action directly assigned
public class ClassA
{
public Action<string> OnAdd;
private void SomethingHappened()
{
OnAdd("It Happened");
}
}
public class ClassB
{
public ClassB()
{
var myClass = new ClassA();
myClass.OnAdd = Add;
}
private void Add(string Input)
{
//do something
}
}
//Event handlers
public class ClassA
{
public event Action<string> OnAdd;
private void SomethingHappened()
{
if (OnAdd != null)
OnAdd("It Happened"); //Should it be OnAdd.Invoke("It Happened") ???????
}
}
public class ClassB
{
public ClassB()
{
var myClass = new ClassA();
myClass.OnAdd += Add;
}
private void Add(string Input)
{
//do something
}
}
(As an aside, it's hard to explain things when you've used the same type names twice.)
When you use a public field, clients can not only subscribe to events - they can also completely remove other event handlers by assigning instead of adding:
myClass.OnAdd = Add;
They can also invoke the handler directly:
myClass.OnAdd("foo");
Both of these violate the normal pub/sub pattern, where the various subscribers are isolated from one another. Subscribers don't get to overwrite each other's subscriptions (only add or remove their own) and they don't get to raise the event themselves.
For more on events and delegates, see my article on the topic.
You can assign more than one delegates to one event (thus the += operator).
An Event acts like a wrapper around a Delegate to offer protection from reassigning/removing as John has pointed out. I found this quite a good read.