I am working on an LOB application in C# using a WinForms tabbed MDI interface. I have various forms with DataGridViews to allow the user to select an object they are interested in, which they can then view/edit in a new form.
Each of my main business objects inherit from Entity, which is defined as below:
public abstract class Entity
{
public static event Action Saved;
internal virtual void OnSaved()
{
if (Saved != null)
{
Saved();
}
}
}
I then have the objects that populate the grid (these are actually auto-generated classes from Linq-to-SQL, although I can replicate the problem with normal classes):
class Class1 : Entity
{
//Stuff
}
class Class2 : Entity
{
//Stuff
}
I want to know when an object of a given class is modified, but i don't care which instance (hence the static action) so that i can refresh the grid and perform other activities.
The problem comes when the event is fired from a derived class instance - it fires for all other derived classes too. For example:
Class1.Saved += new Action(s1);
Class2.Saved += new Action(s2);
private void TestIt()
{
Class2 o2 = new Class2();
o2.OnSaved();
}
This would fire s1 and s2, but I only want the specific one to be fired (i.e. s2). What is the best way to do this? I have quite a few classes that need this behviour and would like to avoid having to add any code to each class if possible.
Update:
Thank you for all your responses, they have been very helpful.
I have opted for a slightly different option, which I admit seems quite hacky, but works well for my purposes. This involves passing the type with the action and letting a handler filter and call relevant operations.
Entity Class:
public abstract class Entity
{
public static event Action<Type> Saved;
internal void OnSaved()
{
private Action<Type> SavedCopy = Saved;
if (SavedCopy != null)
SavedCopy(this.GetType());
}
}
Hook up handler:
Entity.Saved += new Action<Type>(Handler);
Example Handler method (this will vary from form to form):
void Handler(Type obj)
{
if (obj==typeof(Class1))
UpdateGrid();
else if (obj==typeof(Class2))
UpdateBasicInfo();
else if (obj == typeof(Class3))
DoAnotherThing();
}
Using generics could be a work around; each generic class gets a copy of the static fields.
public abstract class Entity<T>
{
public static event Action Saved = delegate { };
internal virtual void OnSaved()
{
Saved();
}
}
class Class1 : Entity<Class1>
{
//Stuff
}
class Class2 : Entity<Class2>
{
//Stuff
}
I'm not sure doing it like this is a good idea, but you could specify the type when you subscribe and when you save the data:
public abstract class Entity
{
private static Dictionary<Type, Action> Subscribers
= new Dictionary<Type, Action>();
internal virtual void OnSaved()
{
OnSaved(GetType());
}
private OnSaved(Type type)
{
Action subscribed;
Subscribers.TryGetValue(type, out subscribed);
if (subscribed != null)
subscribed();
}
public Subscribe(Type type, Action action)
{
Action subscribed;
Subscribers.TryGetValue(type, out subscribed);
Subscribers[type] = subscribed + action;
}
public Unsubscribe(Type type, Action action)
{
Action subscribed;
Subscribers.TryGetValue(type, out subscribed);
Subscribers[type] = subscribed - action;
}
}
Keep in mind that this code is not thread-safe, so if you want to use it from different threads at the same time, you need to add locking.
You will need to have an event per type, because can't determine for which type the delegate is registered when the event is defined on the base type.
public abstract class Entity
{
internal abstract void OnSaved();
}
class Class1 : Entity
{
public static event Action Saved = () => { };
internal override void OnSaved()
{
this.Saved();
}
//Stuff
}
class Class2 : Entity
{
public static event Action Saved = () => { };
internal override void OnSaved()
{
this.Saved();
}
//Stuff
}
Why does it have to be static? Make it an instance event instead.
public event Action Saved;
You have to hook it up for each instance instead of just once per class (or, in your current case, once), but it will separate the events.
Related
This problem has been keeping me busy for half a day now and I start to lose my sanity:
I'm using Items for UI Logic stuff. There are "parent" Items, that can contain ObservableCollections of other Items. (Both inherit from the same ItemBase, picture nodes with nodes, sort of recursive)
For not having to recreate Observer logic on each "parent" item class, I wanted to add the functionality to the common baseclass, called ItemBase. The idea is, that the parent can just register its ObservableCollections and the baseclass takes care of the event routing and all. The problem is, that I can't seem to find a way to save a reference to these ObservableCollections (of different types with the same baseclass) for the way that generics work.
Here's the code:
public abstract class ItemBase : ViewModelBase
{
private List<ObservableItemCollection<ItemBase>> _trackedChildItemsList = new List<ObservableItemCollection<ItemBase>>();
public event EventHandler<ItemPropertyChangedEventArgs> ChildItemPropertyChanged;
public event EventHandler<IsDirtyChangedEventArgs> ChildItemIsDirtyChanged;
public override bool IsDirty
{
get { return base.IsDirty || AreAnyChildItemsDirty; }
set { base.IsDirty = value; }
}
private bool AreAnyChildItemsDirty
{
get
{
return _trackedChildItemsList.Any(i => i.Any(l => l.IsDirty));
}
}
protected void RegisterItemCollection<T>(ObservableItemCollection<T> collection)
where T : ItemBase
{
_trackedChildItemsList.Add(collection); // intellisense underlines 'collection'; cannot convert from 'ObservableItemCollection<T>' to ObservableItemCollection<ItemBase>:
collection.ItemPropertyChanged += Collection_ItemPropertyChanged;
collection.ItemIsDirtyChanged += Collection_ItemIsDirtyChanged;
}
public override void Dispose()
{
foreach (ObservableItemCollection<ItemBase> collection in _trackedChildItemsList)
{
collection.ItemPropertyChanged -= Collection_ItemPropertyChanged;
collection.ItemIsDirtyChanged -= Collection_ItemIsDirtyChanged;
}
base.Dispose();
}
private void Collection_ItemPropertyChanged(object sender, ItemPropertyChangedEventArgs e)
{
OnChildItemPropertyChanged(e);
}
protected virtual void OnChildItemPropertyChanged(ItemPropertyChangedEventArgs e)
{
ChildItemPropertyChanged?.Invoke(this, e);
}
private void Collection_ItemIsDirtyChanged(object sender, IsDirtyChangedEventArgs e)
{
OnItemIsDirtyChanged(e);
}
protected virtual void OnItemIsDirtyChanged(IsDirtyChangedEventArgs e)
{
ChildItemIsDirtyChanged?.Invoke(this, e);
}
}
As you can see, I'm using a derived, custom type of the ObservableCollection, namely ObservableItemCollection, which takes care of the ItemPropertyChanged and ItemIsDirtyChanged invokation for the collection itself. This allows one to catch those events from the outside.
Now, instead of having that 'catching the events' logic in each parent item itself (duplicated), I wanted it to be in a centralized spot, namely the baseclass.
Now the main problem is, that upon registering the ObservableItemCollections, I cannot possibly keep a reference to them since there's no common base. ObservableItemCollection<CustomItem> does not inherit from ObservableItemCollection<ItemBase>, since its a collection. I tried solving the whole thing with generics, however, the above is as far as I got. It fails to compile where i wrote the 'cannot convert from 'ObservableItemCollection' to ObservableItemCollection' comment.
I understand why it fails to compile, however, I can't seem to find a workaround/working solution.
I absolutely need a direct reference to the collections (casted as my custom type ObservableItemCollection), else the whole thingy won't work. You can see in the code that I'm accessing both the events of the collection itself, as well as properties of the ItemBase.
Either way, I can't seem to find a common base for the collections. I tried using dynamics and reflection based casting, Interfaces, a Custom generic ParentItem type, neither worked (i might have overlooked something) and even if it did, it would be rather ugly.
Is it really not possible to achieve what I want with a limited amount of hacking things together? I can't believe that I didn't find a good solution after all the time I've invested in this.
Additional info:
In the parent item i have the following ObservableCollections:
public ObservableItemCollection<SomeItem1> Collection1 { get; set; } = new ObservableItemCollection<SomeItem1>();
public ObservableItemCollection<SomeItem2> Collection2 { get; set; } = new ObservableItemCollection<SomeItem2>();
Where both item types inherit from ItemBase. Then i call the base method RegisterItemCollection in the parent item constructor like so:
RegisterItemCollection(Collection1);
RegisterItemCollection(Collection2);
WPF collection controls have the same problem: How do you define a property which can hold a reference to any kind of generic collection? Answer: Make the property a reference to a non-generic interface that all the collections implement. This is a very general question, and it's the reason why non-generic System.Collections.IEnumerable and System.Collections.IList are still in heavy use throughout the .NET framework, all these years after generics were introduced.
Nothing you're doing in RegisterItemCollection(), IsDirty, or Dispose() needs to care about the type of item in the collection. So take whatever methods and properties you need that code to interact with, and put it all in a non-generic interface or base class. Your base class is already generic (ObservableCollection<T>, I presume), so use an interface.
public interface IObservableItemCollection
{
event EventHandler<ItemPropertyChangedEventArgs> ItemPropertyChanged;
event EventHandler<IsDirtyChangedEventArgs> ItemIsDirtyChanged;
bool IsDirty { get; }
}
public interface IDirtyable
{
// I'm pretty sure you'll want this event here, and I think you'll want your collection to
// implement IDirtyable too.
//event EventHandler<IsDirtyChangedEventArgs> IsDirtyChanged;
bool IsDirty { get; }
}
public class ObservableItemCollection<T>
: ObservableCollection<T>, IObservableItemCollection
where T : IDirtyable
{
public bool IsDirty => this.Any(item => item.IsDirty);
public event EventHandler<ItemPropertyChangedEventArgs> ItemPropertyChanged;
public event EventHandler<IsDirtyChangedEventArgs> ItemIsDirtyChanged;
}
public class ViewModelBase : IDisposable, IDirtyable
{
public virtual bool IsDirty => true;
public virtual void Dispose()
{
}
}
public class ItemBase : ViewModelBase
{
private List<IObservableItemCollection> _trackedChildItemsList = new List<IObservableItemCollection>();
public override bool IsDirty
{
get
{
return base.IsDirty || _trackedChildItemsList.Any(coll => coll.IsDirty);
}
}
protected void RegisterItemCollection<T>(ObservableItemCollection<T> collection)
where T : ItemBase
{
_trackedChildItemsList.Add(collection);
collection.ItemPropertyChanged += Collection_ItemPropertyChanged;
collection.ItemIsDirtyChanged += Collection_ItemIsDirtyChanged;
}
public override void Dispose()
{
foreach (IObservableItemCollection collection in _trackedChildItemsList)
{
collection.ItemPropertyChanged -= Collection_ItemPropertyChanged;
collection.ItemIsDirtyChanged -= Collection_ItemIsDirtyChanged;
}
base.Dispose();
}
private void Collection_ItemIsDirtyChanged(object sender, IsDirtyChangedEventArgs e)
{
}
private void Collection_ItemPropertyChanged(object sender, ItemPropertyChangedEventArgs e)
{
}
}
public class ItemPropertyChangedEventArgs : EventArgs
{
}
public class IsDirtyChangedEventArgs : EventArgs
{
}
You could also do this by making _trackedChildItemsList a collection of IDisposable, and have the collections clear their own event handlers, but a class clearing its own event handlers is pretty gruesome. Shun reflection when conventional OOP can be used to do the job in a readable and maintainable way. And you'd still have to think of something for IsDirty.
You can not do this since if you could you could do something like
class A {}
class B : A { }
class C : A { }
var list = new List<List<A>>();
var sublist_b = new List<B>();
sublist_b.Add(new B());
list.Add(sublist_b);
var sublist = list.Single();
sublist.Add(new C()); // <- now a List<B> contains an object that ist not if type B or derived B
I would suggest that you only use ObservableItemCollection<ItemBase> to hold your objects.
I am not sure how to decide about how to refactor some production code. This code works as select records top 1 from db and decided to column containing value under the below.
switch(column_value):
case send_email:
send_email.DoIt();
case enable_somexx:
enable_somexx.DoIt();
case response_email:
response_email.DoIt();
Showing the below examples, there are created classes for every events (records) including a DoIt() method(SendMail, DecideMail, ResponseMail, MailXXX, enable_somexx). The classes include 3 subfolders actions named action, decision, response (actually these classes irrelevant which other because code select top 1 record)
I'm thinking of refactoring this code logic like this:
Create base class named Behaviour
other 3 main classes will inherit from this base class
Code:
public abstract Behaviour
{
public virtual void DoIt(string type) {
}
}
--Also another classes Decision, Response will inherit from Behaviour.
public class Action : Behaviour
{
override void DoIt(string type) {
}
}
public class Email : Action
{
override void DoIt(string type)
{
if(type == SEND)
call_sendmethod
else if(xxx_operation_about_mail)
call_xxx_operation_about_mail
}
}
But I cannot handle (actually I don't like my solution because I don't want to create same class every operations like EmailAction, EmailResponse, EmailDecision or another operations)
If you make this code block refactoring, how would you do it?
Thank you.
Using your idea of refactoring ... this is how I would code it:
Here is an outline:
Create an abstract class for Behavior
Create an action class which inherits Behavior
Then you can code like this to trigger desire "action".
Notice how I override the "Send" behavior to customize it to "special sent".
Here is the fiddle: https://dotnetfiddle.net/m3tjWl
Blockquote
public class Program : Action
{
public static void Main()
{
Console.WriteLine("Hello World");
var command = Console.ReadLine();
//trigger send from Action class
Action x = new Action();
x.DoIt(command);
//trigger send from behavior class
//the lines below are added to show how you can still access the parent behavior, remove or use where appropriate
Behaviour y = x;
y.Send();
}
}
public abstract class Behaviour
{
public virtual void Send()
{
Console.WriteLine("sent");
}
public virtual void EnableX()
{
Console.WriteLine("enabled");
}
public virtual void Reply()
{
Console.WriteLine("replied");
}
public abstract void DoIt(string type);
}
public class Action : Behaviour
{
public override void DoIt(string type)
{
if(type.ToUpper() == "SEND")
this.Send();
else if (type.ToUpper() == "ENABLEX")
this.EnableX();
else if (type.ToUpper() == "REPLY")
this.Reply();
else
Console.WriteLine("Unknown Command");
}
new public void Send()
{
Console.WriteLine("Special Sent");
}
}
How,does one should call an event declared by interface so that all the classes that has implemented that interface get notified??
For example in structure like this,
public delegate void myDel(int value);
interface IEventCaller{
event myDel myDelEventCall;
}
public Class One : IEventCaller {
public event myDel myDelEventCall;
}
public Class Two : IEventCaller {
public event myDel myDelEventCall;
}
I want both class One and Two to get notify and act as event gets called, I am feeling somewhere I am going wrong direction , is it possible to do?
Actually what you want doesn't involve events. Events would be used by an object implementing IEventCaller to notify some object holding a reference to that object of some change. To invoke something on the object implementing IEventCaller would just require a method, for example Hello();
First, you need code that informs all the objects that implement this interface. To make that possible, you somewhere need to store a list of instances that want to get notified.
One solution would be to create a class that manages that list. Let's say like this
private static List<IEventCaller> eventCallers = new List<IEventCaller>();
public static void AddEventCaller(IEventCaller c)
{
eventCallers.Add(c);
}
public static void RemoveEventCaller(IEventCaller c)
{
eventCallers.Remove(c);
}
public static IEventCaller[] EventCallers
{
get { return eventCallers.ToArray() }
}
Of course this code needs to be thread safe, etc. I'd put all this into a singleton to be globally available.
Then, all objects that implement IEventCallers need to register/unregister accordingly. Thus, I'd also have them Implement IDisposable so that in the constructor you can do
public EventCallable()
{
Singleton.Instance.AddEventCaller(this);
}
and in the Dispose method you can do this:
public void Dispose(bool disposing)
{
Singleton.Instance.RemoveEventCaller(this);
}
Now the code that should notify every instance could just do this:
public void NotifyAll()
{
foreach (IEventCaller caller in Singleton.Instance.EventCallers)
caller.Hello();
}
I think you might be looking at this the other one around.
With events, you want to have an object which is the publisher, which is responsible for publishing the event and saying "hey guys, something just occurred and you should know about it", and you have your subscribers, which are the guys who say "Yo dawg, let me know when that thing occurs, so i can act on it".
What you can do is have the object which is responsible for the event occurring implement your interface:
public class Publisher : IEventCaller
{
public event MyDel MyDeleteEvent;
public void OnDeleteOccured()
{
var myDeleteEvent = MyDeleteEvent;
if (myDeleteEvent != null)
{
MyDeleteEvent(1);
}
}
}
And then have your One and Two objects register to that event occurring, where they pass a method which signature matches the delegate type of MyDel:
public class SubscriberOne
{
public void OnSomethingOccured(int value)
{
Console.WriteLine(value);
}
}
public class SubscriberTwo
{
public void OnSomethingOccured(int value)
{
Console.WriteLine(value);
}
}
And the registration goes:
void Main()
{
var publisher = new Publisher();
var subscriberOne = new SubscriberOne();
var subscriberTwo = new SubscriberTwo();
publisher.MyDeleteEvent += subscriberOne.OnSomethingOccured;
publisher.MyDeleteEvent += subscriberTwo.OnSomethingOccured;
}
I have this case where I'm creating 2 different event handlers placed in a base class and subscribing to them accordingly from Quotes and Charts classes. Problem I'm having is that the first subscription triggers fine for the first event but any following subscriptions don't get executed. I have included an example of 2 different handlers, Quotes and Charts, Quotes executes first time with no problems, but Charts does not trigger when data is received.
Base Class:
public abstract class MyBaseClass
{
protected virtual void RaiseOnQuoteData(string item) { }
protected virtual void RaiseOnChartData(string item) { }
void OnDataReceived(object sender, DataEventArgs e)
{
if (e.Item == "QUOTE")
RaiseOnQuoteData(e.Item);
else if (e.Item == "CHART")
RaiseOnChartData(e.Item);
}
}
Quote and Chart Classes:
public class Quote : MyBaseClass
{
public event EventHandler<DataEventArgs<quoteRecord>> OnQuoteData;
protected override void RaiseOnQuoteData(string item)
{
OnQuoteData.Raise<DataEventArgs<quoteRecord>>(this, new DataEventArgs<quoteRecord>(item));
}
}
public class Chart : MyBaseClass
{
public event EventHandler<DataEventArgs<chartRecord>> OnChartData;
protected override void RaiseOnChartData(string item)
{
OnChartData.Raise<DataEventArgs<chartRecord>>(this, new DataEventArgs<chartRecord>(item));
}
}
Subscription:
public class QuoteSubscription
{
public static void SubscribetoQuoteData()
{
Quote Q = new Quote();
Q.OnQuoteData += new EventHandler<DataEventArgs<quoteRecord>>(q_OnQuoteData);
}
static void q_OnQuoteData()
{
//Executes fine
}
}
public class ChartSubscription
{
public static void SubscribetoChartData()
{
Chart C = new Chart();
C.OnChartData += new EventHandler<DataEventArgs<chartRecord>>(q_OnChartData);
}
static void q_OnChartData()
{
//Does not execute
}
}
This is implemented in ASP.NET 4.0, Is there any chance that instantiating the derived classes could be the problem since both classes do share the same base class? Any help pointing to the cause would be greatly appreciated.
What is there in Raise? This must be an extension method, since EventHandler per se doesn't define such a method. Therefore, you can put a breakpoint inside and see, what's going on. (And you could perhaps put a breakpoint inside RaiseOnChartData as well.)
Could it be that you are creating the object within the method scope and has gone out of scope. You may get the first message by coincidence just because it hasn't been GC-ed. Try creating the quote and chart objects as static class member object
I put "volatile" because it's only vaguely so.
I have a class which has a property called StopRequested. This flag can be set by other threads at any time, and needs to indicate to my code that it should stop what it's doing and exit (this is a Windows Service based process, and when Stop is called, all processing needs to clean up and stop).
I wish to create some other classes to do the actual brunt of the processing work, however these classes also have to be aware of the "stop" flag. You can't just pass the flag because it will pass a copy, and you can't pass properties as ref types.
So how do you propagate a property that might change at any time into other classes?
The only thing I can think of is to pass a reference to the parent class, but I dislike coupling the worker classes to the parent for one flag. Any suggestions would be appreciated.
EDIT:
Here's a basic example:
public class A
{
public bool StopRequested { get; set; }
private Worker = new Worker();
public void DoWork();
{
worker.DoWork();
}
}
public class Worker
{
public void DoWork()
{
while(!StopRequested)
{
....
}
}
}
You could have each of your worker classes have their own StopRequest property and then just set that whenever StopRequest is flagged.
private List<IStopable> WorkerClasses = new List< IStopable > ()
public Bool StopRequest{
get
{
return _stopRequest;
}
set
{
_stopReqest = value;
foreach (var child in WorkerClasses)
child.StopRequest = value;
}
}
Like Rubens said, use an event. What you described basically defines event to a T:
Propagate a property change to other classes.
There is actually a facility in .NET that provides this already, albeit in a generic way: INotifyPropertyChanged. This interface provides a single event, PropertyChanged, that allows a class to notify any listeners of any property change.
In your case, you could easily provide your own interface that is more specific:
interface IStopNotifier
{
event EventHandler StopRequested;
}
This interface would be implemented by your main work manager (whatever it is), and could propagate itself like so:
class WorkManager: IStopNotifier
{
public event EventHandler StopRequested;
protected void OnStopRequested()
{
if (StopRequested != null) StopRequested(this, new EventArgs());
}
public void StopAllWorkers()
{
OnStopRequested();
}
public Worker CreateWorker<T>()
where T: Worker
{
var worker = new T(this);
return worker;
}
}
class abstract Worker: IDisposable
{
public Worker(IStopNotifier stopNotifier)
{
stopNotofier.StopRequested += HandleStopRequested;
}
private IStopNotifier m_stopNotifier;
private bool m_stopRequested = false;
internal void HandleStopRequested(object sender, EventArgs e)
{
m_stopRequested = true;
}
public void Dispose()
{
m_stopNotifier.StopRequested -= HandleStopRequested;
}
}
Why don't to create an event to handle stop requests?