I have an class with an event, then in another .cs file i have another class where I subscribe to the event. But the event is never successfully triggered, and for some reason the event is null. What am I doing wrong?
First class:
class TestClass
{
public static void CountStart()
{
int CountVal = 0;
do
{
CountVal = CountVal + 1;
if (CountVal % 5 == 0)
{
SimpleEventSender EventSender = new SimpleEventSender();
EventSender.StartEvent();
}
Thread.Sleep(1000);
} while (CountVal < 100);
}
}
The event class:
public class SimpleEventSender
{
public event EventHandler NewEvent;
public void StartEvent()
{
if (NewEvent != null)
{
NewEvent(this,null);
}
}
}
And the class where I subscribe to the event:
public partial class Form1 : Form
{
public Form1()
{
SimpleEventSender newevent1 = new SimpleEventSender();
newevent1.NewEvent += new_event;
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
TestClass class1 = new TestClass();
TestClass.CountStart();
}
public void new_event(object sender, EventArgs e)
{
MessageBox.Show("multiple of 5 reached");
}
}
I have tried following the answers at "Notify when event from another class is triggered" but it didnt seem to work.
The event handlers are associated with an instance of the class (SimpleEventSender in this case).
You're creating multiple SimpleEventSender instances:
One in the Form1 constructor, where you subscribe to the event
One every 5 iterations of CountStart, where you raise the event - but on a new instance of SimpleEventSender, that doesn't have any subscribers
You almost certainly want to use a single instance of SimpleEventSender, e.g.
// The form now retains a reference to the instance of SimpleEventSender
public partial class Form1 : Form
{
private readonly SimpleEventSender eventSender;
public Form1()
{
eventSender = new SimpleEventSender();
eventSender.NewEvent += new_event;
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
TestClass class1 = new TestClass();
TestClass.CountStart(eventSender);
}
public void new_event(object sender, EventArgs e)
{
MessageBox.Show("multiple of 5 reached");
}
}
// TestClass now *accepts* an EventSender rather than creating its own instances
class TestClass
{
public static void CountStart(SimpleEventSender eventSender)
{
// Variable name modified to be more conventional
// A "for" loop would be more idiomatic too
int count = 0;
do
{
count++;
if (count % 5 == 0)
{
eventSender.StartEvent();
}
Thread.Sleep(1000);
} while (count < 100);
}
}
That's because you're assigning this function to the different event of instance of SimpleEventSender.
public Form1()
{
SimpleEventSender newevent1 = new SimpleEventSender();
// You are subscribing to the event of this instance
newevent1.NewEvent += new_event;
InitializeComponent();
}
-
public static void CountStart()
{
int CountVal = 0;
do
{
CountVal = CountVal + 1;
if (CountVal % 5 == 0)
{
// But here you are creating another instance of SimpleEventSender so it doesn't have anything subscribed to it
SimpleEventSender EventSender = new SimpleEventSender();
EventSender.StartEvent();
}
Thread.Sleep(1000);
} while (CountVal < 100);
}
In other words - you are subscribing to one object in the Form1() constructor and you are calling "StartEvent()" function from totally different object in CountStart() function.
Related
This is my first file, form1.cs:
namespace EventsNDelegates
{
public delegate void DelEventHandler();
public partial class Form1 : Form
{
public event DelEventHandler add;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
add += new DelEventHandler(Initiate);
//invoke the event
add();
}
public void Initiate()
{
textBox1.Text = "Hello";
}
}
}
and this is my second file class1.cs:
namespace EventsNDelegates
{
class Class1
{
}
}
and this is my form design:
Now currently on button click the event is firing in form1.
What I need is that the events gets fired on button click form form1.cs in class1.cs and the output gets displayed in the textbox of form1.
If you have to subscribe on event from another class, and show result in form1 class you can try this:
public class Class1
{
public void Foo(Form1 form) // Method with your cycle in class1
{
for(int i = 0; i < 100; i++)
{
var str = i.ToString(); // Write iterator in local inloop variable for correct capture in lambda
form.add += () => // subscribe on form1 event
{
Thread.Sleep(10);
return str;
};
}
}
}
public delegate string DelEventHandler(); // change return type to string
public partial class Form1 : Form
{
public event DelEventHandler add;
public Class1 class1;
public Form1()
{
InitializeComponent();
class1 = new Class1();
class1.Foo(this);
}
private void button1_Click(object sender, EventArgs e)
{
foreach (var handler in add.GetInvocationList().Cast<DelEventHandler>()) // Get all subscribers for correct invoke
{
textBox1.Text = handler(); // invoke all subscribers and write result to textBox1
Refresh();
}
}
}
You can pass an instance of form1 in a class1 to sign up for its event. Also, you should not simply call an event. You should be call all subscribers of event on form1 button click. Do not forget to copy the iterator in the capture within the loop in order to avoid incorrect operations in class1 cycle.
To not have the obligation of instanciating for each Form a new class I would like to have it static.
My problem is that it's a class that regroup all the signature fonction that defines my delegates used in my Code (for all form when I need it).
As Delegate are function pointers I'd like to know if declaring the delegate inside a static class is possible, I mean it works I'va tried but I want to know if there is a known problem about it.
Here is the code:
public static class DEL_Prototype
{
public delegate void DEL_delegate1(Double AValue);
}
and I call it :
DEL_Prototype.DEL_delegate1 += new DEL_delegate1(myfunctiontopoint);
EDIT: Here is the broader delegate use (for instance a debugger to know how much subscriber are attached to each delegates or anything else that contains ALL the delegates used)
Code
public partial class Form1 : Form
{
private DEL_Prototype.DEL_delegate1 m_SetValueCbk;
private Form2 FormwithLabel;
public Form1()
{
InitializeComponent();
FormwithLabel = new Form2(this);
FormwithLabel.Show();
}
public event DEL_Prototype.DEL_delegate1 SetValueCbk
{
add { m_SetValueCbk += value; DEL_Prototype.InvokationListChanged(1, m_SetValueCbk); }
remove { m_SetValueCbk -= value; DEL_Prototype.InvokationListChanged(-1, m_SetValueCbk); }
}
}
Form2
public partial class Form2 : Form
{
private Form1 ThisForm1;
public Form2() { }
public Form2(Form1 Form1link)
: this()
{
ThisForm1 = Form1link;
InitializeComponent();
}
protected void SetValueCbkFN(Double value)
{ ; }
private void button1_Click(object sender, EventArgs e)
{
ThisForm1.SetValueCbk += new DEL_Prototype.DEL_delegate1(this.SetValueCbkFN);
}
private void button2_Click(object sender, EventArgs e)
{
ThisForm1.SetValueCbk -= new DEL_Prototype.DEL_delegate1(this.SetValueCbkFN);
}
}
public static class DEL_Prototype
{
public delegate void DEL_delegate1(Double AValue);
public static void InvokationListChanged(int dir, Delegate Name)
{
string msg = dir < 0 ? "Someone unsubscribed from the event" : "Someone subscribed to the event";
if (Form1.ActiveForm.InvokeRequired)//thread safe implementation
{//if control created in the same thread that label1 its else otherwise invoke method the change text asynchronously
msg = string.Concat(msg + " subscribe number: " + "0");
Form1.ActiveForm.Invoke(new MethodInvoker(() => { MessageBox.Show(msg); }));
}
else
{
if (Name != null) msg = string.Concat(msg + " subscribe number: " + Name.GetInvocationList().Count().ToString());
else msg = string.Concat(msg + " subscribe number: " + "0");
MessageBox.Show(msg);
}
}
}
Does it make sens?
Thanks a lot.
Here is my problem : I have one delegate which I subscribe to from another class, that's alright. What I'd like is each time something subscribe to this delegate it raise an event that tells me the invocation list has changed and how +1 or -1...
I searched for an Onchange event in invocationlist but didn't find anything..
Form1:
namespace EventsOnDelegates
{
public delegate void DEL_delegate1(Double AValue);
public delegate void DEL_delegate2(Boolean AsecondValue);
public partial class Form1 : Form
{
public DEL_delegate1 SetValueCbk;
public EventHandler InvocationListChange;
private Form2 FormwithLabel;
int invoclength;
public Form1()
{
InitializeComponent();
FormwithLabel = new Form2(this);
FormwithLabel.Show();
/*the question part*/
/*I'd like to add an onchange event that tells me if the invocation list has changed and how + or -*/
InvocationListChange += new EventHandler(SetValueCbk.GetInvocationList(),InvocationListHaschanged);
}
protected virtual void InvocationListHaschanged(object sender, EventArgs e)
{
invoclength = SetValueCbk.GetInvocationList().Length;
label1.Text = Convert.ToString(invoclength);
}
private void button1_Click(object sender, EventArgs e)
{
Random newRandNum = new Random();
Double newNumber = newRandNum.NextDouble();
SetValueCbk(newNumber);
}
}
}
Form2:
public partial class Form2 : Form
{
public Form2(){}
public Form2(Form1 Form1link)
:this()
{
InitializeComponent();
Form1link.SetValueCbk += new DEL_delegate1(this.SetValueCbkFN);
}
protected void SetValueCbkFN(Double value)
{
label1.Text = Convert.ToString(value);
}
}
Thanks for help!!
You can use explicit event declaration for that event field :
private EventHandler meEvent;
public event EventHandler MeEvent
{
add { meEvent += value; MeEventInvocationListChanged(); }
remove { meEvent -= value; MeEventInvocationListChanged(); }
}
EDIT : ( Fitting this into your question )
instead of your InvocationListHasChanged method you can create :
void InvokationListChanged(int dir)
{
string msg = dir < 0 ? "Someone unsubscribed from the event" : "Someone subscribed to the event";
if(InvokeRequired)
{
Invoke( new MethodInvoker( () => { label1.Text = msg; });
}
else
{
label1.Text = msg;
}
}
And then change public DEL_delegate1 SetValueCbk; to :
private DEL_delegate1 m_SetValueCbk;
public event Del_delegate1 SetValueCbk
{
add { m_SetValueCbk+= value; InvokationListChanged(1); }
remove { m_SetValueCbk-= value; InvokationListChanged(-1); }
}
Now whenever some other object subscribe to SetValueCbk your label1.Text will change to "Someone subscribed to the event" and whenever some object unsubscribe from SetValueCbk your label1.Text will change to "Someone unsubscribed from the event"
Update: I am pretty sure i found out the reason why, i will update this question with my findings as soon as i got it for sure. It is related to having 2 usercontrol's placed above each other and i draw on the one behind thats why i don't see the changes! I had to check the hashcode of the timer to figure out that 2 timers are involved.
I am developing a winforms application and i have applied the MVP-pattern to separate the responsibilities and stuff... (applied the passive-view approach, and also fire events to require the corresponding presenter to perform an action).
I have also checked other questions here and many other relevant topics, but nothing helped...
Problem description: I added a UserControl (SubView) into a SplitContainer's panel which is placed inside a Form (MainView). This SubView has its own presenter (SubPresenter) that requires it to switch its background (SwitchBackground(), picturebox) whenever that is triggered by the user from the MainView.
Problem: The SwitchBackground() method gets executed (i debugged that) when that is triggered from the MainView (actually from the MainPresenter), but the changes are not displayed on the SubView. I have also examined (or at least tried to do that correctly :) ) if the method requires switching the context into the GUI thread or something by checking the InvokeRequired.
I would be glad to have any recommendations regarding this issue, because i am stuck there and can't go further with programming...
Here is an example application i wrote to illustrate the problem (for the complete example project, i uploaded it on github: link):
SubView:
public interface ISubView
{
void StartSwitchingBackground();
void StopSwitchingBackground();
}
public partial class SubView : UserControl, ISubView
{
private Bitmap plot;
private Brush brush1;
private Brush brush2;
private int drawCount;
private Timer timer;
public SubView()
{
InitializeComponent();
brush1 = new SolidBrush(Color.Yellow);
brush2 = new SolidBrush(Color.Blue);
timer = new Timer();
timer.Tick += OnTick;
}
private void OnTick(object sender, EventArgs e)
{
SwitchBackground();
}
private void SwitchBackground()
{
if (InvokeRequired)
Console.WriteLine("InvokeRequired"); // never happens, so i assume it is not considered a call from another thread...
if (plot == null || plot.Size != pictureBox.Size)
plot = new Bitmap((int)(pictureBox.Width*0.8), (int)(pictureBox.Height*0.8));
using (Graphics g = Graphics.FromImage(plot))
{
int x = plot.Width / 2;
int y = plot.Height / 2;
int w = plot.Width / 4;
int h = plot.Height / 4;
if (drawCount % 2 == 0)
{
g.Clear(Color.White);
g.FillRectangle(brush1, (x - w) / 2, (y - h) / 2, w, h);
}
else
{
g.Clear(Color.Black);
g.FillRectangle(brush2, (x - w) / 2, (y - h) / 2, w, h);
}
drawCount++;
}
pictureBox.Image = plot;
}
public void StartSwitchingBackground()
{
if (!timer.Enabled)
timer.Start();
Console.WriteLine("started");
}
public void StopSwitchingBackground()
{
if (timer.Enabled) timer.Stop();
Console.WriteLine("stopped");
}
private void btnStartSwitching_Click(object sender, EventArgs e)
{
StartSwitchingBackground();
}
private void btnStopSwitching_Click(object sender, EventArgs e)
{
StopSwitchingBackground();
}
}
SubPresenter:
public class SubPresenter
{
private ISubView view;
public SubPresenter(ISubView view)
{
this.view = view;
}
public void SwitchBackground()
{
view.StartSwitchingBackground();
}
public void StopSwitching()
{
view.StopSwitchingBackground();
}
}
MainView:
public interface IMainView
{
ISubView SubView { get; }
event EventHandler SwitchEventTriggered;
event EventHandler StopSwitchEventTriggered;
}
public partial class MainView : Form, IMainView
{
private SubView subView;
public MainView()
{
InitializeComponent();
subView = new SubView();
splitContainer1.Panel2.Controls.Add(subView);
subView.Dock = DockStyle.Fill;
subView.Anchor = AnchorStyles.Top & AnchorStyles.Left;
splitContainer1.Resize += splitContainer1_Resize;
}
void splitContainer1_Resize(object sender, EventArgs e)
{
subView.Size = splitContainer1.Panel2.Size;
}
private void btnStart_Click(object sender, EventArgs e)
{
StartSwitching();
}
private void StartSwitching()
{
OnStartSwitchingTriggered();
}
private void OnStartSwitchingTriggered()
{
var handler = SwitchEventTriggered;
if (handler == null) return;
handler(this, EventArgs.Empty);
}
private void btnStop_Click(object sender, EventArgs e)
{
StopSwitching();
}
private void StopSwitching()
{
OnStopSwitching();
}
private void OnStopSwitching()
{
var handler = StopSwitchEventTriggered;
if (handler == null) return;
handler(this, EventArgs.Empty);
}
public ISubView SubView { get { return this.subView; } }
public event EventHandler SwitchEventTriggered;
public event EventHandler StopSwitchEventTriggered;
}
MainPresenter:
public class MainPresenter
{
private readonly IMainView mainView;
private readonly SubPresenter subPresenter;
public MainPresenter(IMainView view)
{
this.mainView = view;
this.subPresenter = new SubPresenter(mainView.SubView);
mainView.SwitchEventTriggered += OnSwitchEventTriggerd;
mainView.StopSwitchEventTriggered += OnStopSwitchEventTriggered;
}
private void OnStopSwitchEventTriggered(object sender, EventArgs e)
{
subPresenter.StopSwitching();
}
private void OnSwitchEventTriggerd(object sender, EventArgs e)
{
subPresenter.SwitchBackground();
}
}
Entry Point:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
MainView mainView = new MainView();
MainPresenter mainPresenter = new MainPresenter(mainView);
Application.Run(mainView);
}
}
You have two instances of SubView in MainView.
Use only subView1, you can delete all occurrences of subView;
The minimum fix is to replace:
public ISubView SubView { get { return this.subView; } }
with:
public ISubView SubView { get { return this.subView1; } }
I wants to access Form1's EventHandler In Form2
Form1 EventHandler is:-
private void dataGridView1_CellEndEdit(object sender, DataGridViewCellEventArgs e)
How to achieve it?.
You are doing something wrong.
If you want to expose functionality, you should create a public method/function to do so. You can call this from your event handler and from your other form.
Answer updated by your question in comment, I didn't checked that it works fine may be there is a bug with it:
It's useful when you have a similar event, Also you can pass different EventArgs, easiest way is to have a different Property which determines each form and add event in their set methods but bellow is general
public abstract class FormBase : Form
{
public virtual event EventHandler MyEventHandler;
}
public class Form3 : FormBase
{
public override event EventHandler MyEventHandler;
Form2 instance;
public Form3()
{
instance = Form2.Instance;
instance[this.GetType().ToString()] = this;
// or
//instance["Form3"] = this;
}
private void dataGridView1_CellEndEdit(object sender, EventArgs e)
{
// todo
if (MyEventHandler != null)
MyEventHandler(this, e);
}
}
public class Form2
{
Dictionary<string, FormBase> dic = new Dictionary<string,FormBase>();
public FormBase this[string index]
{
get
{
FormBase retVal = null;
if (dic.TryGetValue(index, out retVal))
return retVal;
return null;
}
set
{
FormBase retVal = null;
if (value == null)
return;
if (dic.TryGetValue(index, out retVal))
{
try
{
value.MyEventHandler -= MyEventHandler1;
}
catch
{
}
retVal = value;
retVal.MyEventHandler += MyEventHandler1;
return;
}
value.MyEventHandler += MyEventHandler1;
dic.Add(index, value);
}
}
private static Form2 instance;
public static Form2 Instance
{
get
{
if (instance == null)
{
instance = new Form2();
}
return instance;
}
}
private Form2()
{
}
private void MyEventHandler1(object sender, EventArgs e)
{
}
}
Change the access modifier to public instead of private.
Another option is to parse the eventhandler on Form1 to Form2:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
DataGridViewCellEventHandler dataGridViewCellEventHandler = new DataGridViewCellEventHandler(this.dataGridView1_CellEndEdit);
this.dataGridView1.CellEndEdit += dataGridViewCellEventHandler;
Form2 form2 = new Form2();
form2.CellEndEdit += dataGridViewCellEventHandler;
}
private void dataGridView1_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
//Do something
}
}
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
public event DataGridViewCellEventHandler CellEndEdit
{
add { dataGridViewOnForm2.CellEndEdit += value; }
remove { dataGridViewOnForm2.CellEndEdit -= value; }
}
}
This does however require that Form1 has access to Form2