In one class I'm adding objects to my ObservableCollection. And in another class, I'm doing stuff with my added object and then delete it from the collection.
Those two classes cannot communicate with each other, so I decided to go for static collection (I only have access to the class definition for some reason)
In my first class, all elements are added properly (I checked the Count property), in the second class I subscribe to the CollectionChanged event. However, the event is not raising. I think it's because of the statickeyword, but I'm not sure.
Here is a code sample:
static public class A
{
public static ObservableCollection<object> MyCollection = new ObservableCollection<object>();
}
public class B
{
public B()
{
A.MyCollection.CollectionChanged += Func_CollectionChanged;
}
void Func_CollectionChanged(...)
{
//Stuff
}
}
public class C
{
public void func()
{
A.MyCollection.Add(object);
}
}
Here it works fine for me:
class Program
{
static void Main(string[] args)
{
B obj = new B();
}
}
public class A
{
public static ObservableCollection<object> MyCollection = new ObservableCollection<object>();
}
public class B
{
public B()
{
A.MyCollection.CollectionChanged += Func_CollectionChanged;
A.MyCollection.Add(1);
}
private void Func_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
// do some stuff here
}
}
by using A.MyCollection.CollectionChangedline you are creating an EventHandler to handle the the collection change event. it fires when ever any changes(add/update/delete) made in the collection. since it is a delegate you are creating you have to specify the sender who own the event and the type of arguments(What it going to handle), in-order to get proper reporting of published event
Updates
You just look into your code. the instance of class b is not yet created, the constructor of this class will automatically invoked only when the new instance of the class is created. You are creating the Event handler inside the constructor of class b. So it is not yet published any event. that is the reason for the collection_Change event is not triggering in your code snippet.
Hence your Definition for class C will be like the following to register the event :
public class C
{
B obj = new B();
public void func()
{
A.MyCollection.Add(1);
}
}
Related
I think my question is best descirbed by a code snippet:
class A
{
public void FunctionToBeCalled();
}
class B
{
public void FunctionToBeCalledAfter();
}
Now, after a FunctionToBeCalledAfter() call, FunctionToBeCalled() needs to "know" it must be called. B cannot have an A member, but A can have a B member. Is there any way this can be implemented in C#?
Why i need this:
Class A is Application level on OSI stack. Classes B and C(unmentioned before) are Transport Level. C makes calls to FunctionToBeCalledAfter, and after this FunctionToBeCalled needs to be called. But sincer A is a higher level, B and C cannot depend(have a member A), i don't know how to call FunctionToBeCalled.
I see 2 ways to accomplish this, one easier but (arguably) less elegant, one a little more involved but (arguably) more elegant
The less elegant solution: Singleton
A Singleton pattern enforces that there can only ever be one instance of a class at any given time, this seems to line up with your description of A (which from here on out I'll call Foo, and I'll be calling B Bar). So let's implement it:
public class Foo
{
private static Foo _instance;
public static Foo Instance => _instance ?? (_instance = new Foo());
// Private constructor so no one else can instantiate Foo
private Foo() { }
public void FunctionToBeCalled() { /* your code here */ }
}
public class Bar
{
public void FunctionToBeCalledAfter()
{
// Your existing code here
Foo.Instance.FunctionToBeCalled();
}
}
Now, the problem here is if your requirements ever change and you need multiple Foos, that'll be quite a refactor to implement it. Another (larger) downside is that we explicitly reference (i.e depend on) Foo, which isn't great and a problem if Bar is inside a project/ library that cannot directly reference Foo. Luckily solution 2 fixes those problems:
The more elegant solution: Events
public class Foo
{
// We don't need Foo to be a singleton anymore
public void FunctionToBeCalled() { /* Your code here */ }
}
public class Bar
{
public delegate void FunctionToBeCalledAfterEventHandler();
public event FunctionToBecalledAfterEventHandler FunctionToBeCalledAfterEvent;
public void FunctionToBeCalledAfter()
{
// Your existing code here
OnFunctionToBeCalledAfterEvent(); // Fire the event
}
private void OnFunctionToBeCalledAfterEvent()
{
FunctionToBeCalledEvent?.Invoke();
}
}
Now, everywhere where you're creating an instance of Bar you need to have a reference to Foo and subscribe to the event like so:
// foo = instance of class Foo
var bar = new Bar();
// The compiler is smart enough to find out that 'FunctionToBeCalledAfterEvent'
// has the same signature as 'FunctionToBeCalledAfterEvent' and can call it directly
// If this just so happens to not be case, see second way to subscribe to events
bar.FunctionToBeCalledAfterEvent += foo.FunctionToBeCalled;
// Or
bar.FunctionToBeCalledAfterEvent += () => foo.FunctionToBeCalled();
Events are great
Class B can have an event that other parties can handle. At the end of B.FunctionToBeCalledAfter this event would be invoked. Anyone who registered for this event would then be notified. Usual boilerplate code involves one virtual method that invokes one event. It's the standard way of adding events. If there is no need for additional data in the event then EventArgs is used. If additional data is needed then you could replace EventArgs with EventArgs<YourData>, or as an alternative, introduce a class XxxArgs derived from EventArgs with this additional data.
Class B
{
public event EventHandler FinishedFunctionToBeCalledAfter;
protected virtual void OnFinishedFunctionToBeCalledAfter(EventArgs e)
{
EventHandler handler = FinishedFunctionToBeCalledAfter;
handler?.Invoke(this, e);
}
public void FunctionToBeCalledAfter()
{
...
OnFinishedFunctionToBeCalledAfter(EventArgs.Empty);
}
}
Now when class A gets a hold of an object of class B it would add its event handler to it:
class A
{
public void FunctionToBeCalled();
public void FinishedFunctionToBeCalledAfter(object source, EventArgs e);
public void IntroduceObject(B b)
{
b.FinishedFunctionToBeCalledAfter += FinishedFunctionToBeCalledAfter;
}
}
When this object b of class B should end its life class A must know about it so that it can remove its event handler:
b.FinishedFunctionToBeCalledAfter -= FinishedFunctionToBeCalledAfter;
Suppose I have two classes - one that is provided to me (but suppose I am not allowed to change it as it is maintained by someone else), and one that I control and can change.
// Class A is provided to me by someone else, and suppose I can't modify it
public class A
{
public A()
{
...
}
public void DoSomethingInA()
{
...
}
}
// Class B is what I control
public class B
{
public A MyClassAInstance;
public B(A myClassAInstance)
{
MyClassAInstance = myClassAInstance;
// *** HERE IS WHERE I NEED HELP
// NEED TO WRITE AN EVENT / EVENT HANDLER, WITH / WITHOUT REFLECTION
// THAT RUNS DoSomethingInB WHENEVER MyClassAInstance's DoSomethingInA
// METHOD IS CALLED (AND COMPLETED)
}
public void DoSomethingInB()
{
...
}
}
How can I define a Event / EventHandler in class B that kicks off its DoSomethingInB method whenever the class A instance MyClassAInstance's method DoSomethingInA is called (and completed).
I tried lot of options, but none seem to work.
For instance, I tried:
public class B
{
public A MyClassAInstance;
public B(A myClassAInstance)
{
MyClassAInstance = myClassAInstance;
var eventInfo = GetType().GetEvent("MyEvent");
var methodInfo = myClassAInstance.GetType().GetMethod("DoSomethingInA");
Delegate handler = Delegate.CreateDelegate(eventInfo.EventHandlerType, myClassAInstance, methodInfo);
eventInfo.AddEventHandler(this, handler);
MyEvent += DoSomethingInB;
}
public event EventHandler MyEvent;
public void DoSomethingInB()
{
...
}
}
But this doesn't work.
Any suggestions or help would be greatly appreciated.
The following classes
public class PagedItemList<T>
{
public delegate void PageChanged(int newPage);
public event PageChanged PageChangedEvent;
}
public class SomeClass
{
public void SetupWithPagedList<T>(PagedItemList<T> list)
{
list.PageChangedEvent += new PagedItemList<T>.PageChanged(NotifyPageChanged);
}
public void NotifyPageChanged(int newPage) { }
}
Throws the error:
InvalidCastException: Cannot cast from source type to destination type
When I try to add a listener to the event as seen in the example above.
The call to SetupWithPagedList is correctly parametrized:
obj.SetupWithPagedList<Monster>(pagedMonstersList)
I have done events and delegates a lot before, the only difference here is that there is a <T> involved in this class. Has anyone had issues doing events and delegates with templates?
I think your problem in this case is with, the instances. The first question I made when I saw your PagedItemListCode was, why SetupWithPagedList is not an static method, and I checked that you are calling the NotifyPageChanged method in the instance of the class. I do not know the logic behind the problem, but maybe the right way is like this:
public static void SetupWithPagedList<T>(PagedItemList<T> list)
{
list.PageChangedEvent += new PagedItemList<T>.PageChanged(list.NotifyPageChanged);
}
Note that the instance of the class maybe is not the same instance of the object that is passed to the SetupWithPagedList method. Maybe the class is PagedItemList<A> and the method parameter is PagedItemList<Monster>. Maybe the class do not need to be generic. Check it.
EDIT
I tried your code and works perfect:
var p = new PagedItemList<int>();
var sc = new SomeClass();
sc.SetupWithPagedList(p);
p.RaisPageChanged(5);
...
public class PagedItemList<T>
{
public delegate void PageChanged(int newPage);
public event PageChanged PageChangedEvent;
public void RaisPageChanged(int page)
{
if (PageChangedEvent != null)
PageChangedEvent(page);
}
}
public class SomeClass
{
public void SetupWithPagedList<T>(PagedItemList<T> list)
{
list.PageChangedEvent += new PagedItemList<T>.PageChanged(NotifyPageChanged);
}
public void NotifyPageChanged(int newPage)
{
Debug.WriteLine("Page: ",newPage);
}
}
Check it, maybe is something else.
public Class A
{
public A()
{
someotherclass.someevent += new EventHandler(HandleEvent);
}
private void HandleEvent(object sender,CustomEventArgs e)
{
if(e.Name == "Type1")
Method1();
else if(e.Name == "Type2")
Method2();
}
protected virtual void Method1(){}
protected virtual void Method2(){}
}
public class B: A
{
public B()
{ /*Something*/}
protected override void Method1(){/*some logic*/}
protected override void Method2(){/*some other logic*/}
}
public class C: A
{
public C()
{ /*Something*/}
protected override void Method1(){/*some logic*/}
protected override void Method2(){/*some other logic*/}
}
public class Main
{
private A;
public Main(){/*Something*/}
private void StartB()
{
A = new B();
}
private void StartC()
{
A = new C();
}
}
Now, what happens is, after I go through a cycle in which both the methods StartB(called first) and StartC(called second) are called, when the someevent is triggered, the code tries to execute the Method in Class B(and later Class C, I hope. I could not get there since it errors out when it calls method in Class B), instead which I want it to call only the method in Class C.
I think that, since the event is subscribed at constructor, Class B methods are still getting fired since it is subscribed initially on the call of StartB.
Question:
I want only the methods of the class that is instantiated the latest should be executed.
For Example: if StartB and StartC are called in order, when someevent is triggered the Methods in Class C should only get executed. Same Vice-Versa. How to do that?
I know am doing something terribly wrong. Any help is much appreciated.
You aren't unsubscribing from the event from your first instance so it will be called. If you don't want it to be called you need to unsubscribe. You could do something like this
class A
{
private static EventHandler lastHandler;
public A()
{
//warning, not thread safe
if(lastHandler != null)
{
someotherclass.someevent -= lastHandler;
}
lastHandler = new EventHandler(HandleEvent);
someotherclass.someevent += lastHandler;
}
but it seems pretty hacky. You are probably better off implementing a method (e.g. IDisposable) to clean up your last instance before a creating a new one.
If I understand you correctly you are saying the methods on B are being called after startC is called and you don't wish this to happen?
I'm guessing your issue is that someotherclass is a static class, or an instance is somehow being shared between all the created B's and C's - in which case you need to unregister the old event handler from someotherclass.someevent when you create the new class. If you don't unregister the handler then the someotherclass object will have a reference to the B or C object that registered with it, so even though you are overwriting the reference in the main class the object is still kept alive by the reference in the event and is still being called when the event is triggered.
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);