This is more a design question, and I hope I express my question clear.
I have a complex process, that needs to log messages. When some of these logs are written, a event is raised and I need to respond to this.
The problem is that the CreateLog method are called from different classes during the process and currently my code only catches the events raised from the first class (MyForm).
Is there a better design where I can respond to all events from that class?
Hopefully the pseudo code below will shed some light on my problem.
File 1:
Class CreateLog
{
Log Message
Raise event
}
File 2:
Class MyForm
{
private CreateLog _log = new CreateLog();
void StartProcess()
{
Do stuff
Call _log.LogMessage //1
Call MethodB()
Call MethodC()
}
void MethodB()
{
Call _log.LogMessage //2
}
void RespondToEvent
{
//Show Progress on screen
}
}
File 2:
Class AnotherClass
{
void MethodC()
{
_log2.LogMessage() //3
}
}
So, when I run this, the message log at nr 1 and 2 will fire the RespondToEvent() method, but obviously not at nr 3, because it's a different instance of the CreateLog class.
One solution is to pass _log as a parameter to MehodC, but that does not feel like the best solution.
Any other clean way of achieving this besides adding everything into 1 class?
You can add an event to AnotherClass and raise that event in MethodC. And in MyForm handle the event and call _log.LogMessage
Example:
Class MyForm
{
//your code
RespondToEventFromAnotherClass() //handles delegateToForm Event from 'AnotherClass'
{
Call _log.LogMessage
}
}
Class AnotherClass
{
Event delegateToForm;
void MethodC()
{
_log2.LogMessages(); // you can Eleminate this line if it is not necessary
Raise Event delegateToForm
}
}
Related
I have a controller class and a manager class.
I want my manager class to call a function from my controller to trigger an event.
My controller looks something like this:
public class Controller{
public Controller(Mgr mgr)
{
m_mgr = mgr;
m_mgr.schedulerReload += reloadScheduler;
}
void reloadScheduler(object sender, EventArgs e)
{
ReloadSchedulerQueue();
}
}
where ReloadSchedulerQueue is the function I want to trigger from my event.
my manager class looks like this
public class Manager{
public event EventHandler schedulerReload;
public void somefunction() {
//do something
schedulerReload(this, EventArgs.Empty);
}
}
Whenever I run this I can see that the schedulerReload event is getting a null reference exception, im assuming because there is no one subscribed to my event. However, I can clearly see while stepping through my code that the constructor, and the line
m_mgr.schedulerReload += reloadScheduler;
is clearly hit multiple times before somefunction() is called. I know I can handle this exception by checking my eventhandler before calling it, but I don't know why my eventhandler is not getting subscribed to.
What am I missing here?
Here is an example...
public class CodeGenerator
{
public delegate void GeneratorCalculatorEventHandler(decimal Fond);
public event GeneratorCalculatorEventHandler eventName;
public CodeGenerator()
{
eventName?.Invoke(0);
}
}
How could I catch the event? If I do this:
CodeGenerator CodeGen = new CodeGenerator();
CodeGen.eventName += CodeGen_eventName;
The event is fired when the handler is not subscribed to it yet. Is it possible to subscribe to an event on initialiazation?
As the comments said, this is a bad code smell. It sounds like you're doing something very unusual here and you should consider trying to find a different way to solve your problem.
One of the reasons it smells bad is: if the calling code knows to pass the handler to the constructor, then that code already knows that the constructor is being invoked. The caller could simply invoke the handler itself with the constructed instance! Events are typically for situations where something happens that the handler could not predict or control, like the user clicking a button.
I would not pass the handler to the constructor, were I in your situation. I would use a static event.
You should pass the handler to the constructor and attach it to the event, something like:
public class CodeGenerator
{
public delegate void GeneratorCalculatorEventHandler(decimal Fond);
public event GeneratorCalculatorEventHandler eventName;
public CodeGenerator(GeneratorCalculatorEventHandler listener)
{
eventName += listener;
eventName?.Invoke(0);
}
}
public class Test
{
public Test()
{
CodeGenerator gen = new CodeGenerator((sen) => { return; });
}
}
This has been asked probably many times, but looking through all the other questions I still was not able to solve my issue. I want to update a Datagridview on a form using an update function on this form. The Update function is called by a subscriber.
Overview:
static class MainClass
{
static void Main()
{
// The Main form is called.
MainForm = new frmMain();
Application.Run(MainForm);
//Application.Run(new frmMain());
}
}
A Delegate
public delegate void Delagate_UpdateDataView();
The subscriber that subscribed to publisher that fires an event every 500 ms.
public class SubscriberFrmMain
{
// Constructor
public SubscriberFrmMain()
{
}
// Subscribe to the Publisher
public void Subscribe(PublisherTimedEvent mUpdateHMIData)
{
//attach listener class method to publisher class delegate object
mUpdateHMIData.TickUpdateHMIData += UpdateHMIData;
}
// The Event, fired when the Publisher raises an event.
private void UpdateHMIData(PublisherTimedEvent mUpdateHMIData,EventArgs e)
// Calling the Update function on the Form MainForm.
{
MainClass.MainForm.Process_UpdateDataView(new
Delagate_UpdateDataView(MainClass.MainForm.UpdateDataView));
}
}
The Update function in the Form
public void Process_UpdateDataView(Delagate_UpdateDataView update)
{
update();
}
public void UpdateDataView()
{
try
{
TagTableAdapter.Fill(uDataSet.PLC_Tag);
}
catch
{
}
}
Updating the TagTableAdapter manually works without any problem. Updating using the subscriber does nothing.
Probably there are easier ways to achieve this but I would like to use this type of construction also for other parts of the program.
Thanks for your suggestions.
Event's can only be risen from inside the class. If you could do that it would defeat the purpose of events.You can subscribe to this event from other class tho.
public event EventHandler someEvent;
EventContainer obj = new EventContainer();
obj.someEvent += handler;
where handler is a method according to the signature of someEvent. One is able to subscribe to the event from the outside just fine, but it can only be risen from inside the class defining it.
I am new to C# and programming in general and am trying to figure out how to use events. Previously I have been programming with ActionScript3, and there events are a special class that you inherit from if you want to create your own events, and then that event can be called by any other class.
With C# I have tried to do something similar, like so:
public class EventManager
{
public delegate void TempDelegate();
public static event TempDelegate eSomeEvent;
}
public class SomeOtherClass
{
//doing some stuff, then:
if (EventManager.eSomeEvent != null)
{
EventManager.eSomeEvent();
}
}
This gives me a compiler error CS0070: The event 'EventManager.eSomeEvent' can only appear on the left hand side of += or -= (except when used from within the type 'EventManager')
The information about this error over on the msdn indicates that I should use += instead of trying to call the event, but I don't really understand this. I'm not trying to subscribe anything from SomeOtherClass to the event delegate, I am just trying to call this event so that it starts executing those functions that are already subscribed to that event.
So is it possible to do it this way? If not, is it at all possible to call an event that is of one class, from another class? I simply wish to reuse certain events in my classes rather than creating many similar ones in multiple classes.
Any advice with this would be greatly appreciated!
You can wrap the event call in a public method and use that from your other classes.
public void OnSomeEvent()
{
var handler = eSomeEvent;
if (handler != null) handler(this, null);
}
However you might want to look at the design again, if you are really sure the event should be on a different class than the one triggering it.
Well, the typical solution is to put eSomeEvent invocation into the EventManager class
public class EventManager
{
public delegate void TempDelegate();
public static event TempDelegate eSomeEvent;
// Not thread safe as well as your code
// May be internal, not public is better (if SomeOtherClass is in the same namespace)
public static void PerformSomeEvent() {
if (!Object.ReferenceEquals(null, eSomeEvent))
eSomeEvent(); // <- You can do it here
}
}
public class SomeOtherClass
{
//doing some stuff, then:
EventManager.PerformSomeEvent();
}
I have class A that is data context of my main window.
I have class B that is view model of one of the windows of the application.
No relation between the classes.
I need somehow to raise an event in class B , while the event handler is located in class A.
Is it possible to do so ?
Example of what I am doing is: I am tring to change the main window Title from class B.
Thanks for help
In my book you should be looking at messenger / event aggregator for pub/sub. PRISM comes with one, so do things like the MVVM Light toolkit, or you can grab my own "drop in cs file" one from Github - TinyMessenger.
This allows decoupled communication - the only thing the publisher and subscriber have in common is the aggregator and the message format itself. If the classes are unrelated, and probably constructed separately, you shouldn't be coupling them with an event. If you do use an event you need to be aware of the GC implications of subscribing to an event, if the lifetimes of the two classes are expected to be different.
So, in a very simple example, in class A you'd do something like
this.messenger.Subscribe<TitleChangeMessage>(m => <do some stuff>);
Then in class B, whenever you want to fire the "event" you'd do:
this.messenger.Publish(new TitleChangedMessage("new title"));
You just need to make the event in class B public and attach a delegate from class A to it.
Here is an example: B's constructor starts a background task, which waits for 2 secs and then raises the event. A's constructor creates an object b of type B and attaches its delegate to b's event. In the main function an object of class A is created and then the thread is put to sleep for 5 sec. Before that time passes the event has already been fired and handled.
class A
{
private B b;
public A()
{
b = new B();
b.MyEvent += MyHandler;
}
public void MyHandler(string s)
{
}
public static void Main(string[] args)
{
A a = new A();
Thread.Sleep(5000);
}
}
class B
{
public B()
{
Task.Factory.StartNew(() =>
{
Thread.Sleep(2000);
MyEvent("");
});
}
public delegate void MyDelegate(string s);
public event MyDelegate MyEvent;
}