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.
Related
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.
I have different Labels on my Form1 and want to change the text of a single Label using the same method.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Class1 change = new Class1();
change.ChangeLabelText();
}
public void ChangeLabel(string msg, Label label)
{
if (label.InvokeRequired)
label.Invoke(new MethodInvoker(delegate { label.Text = msg; }));
else
label.Text = msg;
}
}
how can i pass label1 or label2 as i need?
class Class1
{
public void ChangeLabelText()
{
Form1 frm1 = new Form1();
frm1.ChangeLabel("Surname", label2);
}
}
label2 is underlined of red: NOT EXIST IN THE ACTUAL CONTEST
I dont know what is the context in which you are trying to change the label... however I have a few observations seeing your sample code.
First of all, to your question, one of the approaches could be as follows,
private void button1_Click(object sender, EventArgs e)
{
Class1 change = new Class1(this);
change.ChangeLabelText();
}
public void ChangeLabel(string msg, Label label)
{
if (label.InvokeRequired)
label.Invoke(new MethodInvoker(delegate { label.Text = msg; }));
else
label.Text = msg;
}
// dont allow UI elements to be accessed directly from outide
public string Fullname
{
get
{
return label1.Text;
}
set
{
label1.Text = value;
}
}
// dont allow UI elements to be accessed directly from outide
public string Surname
{
get
{
return label2.Text;
}
set
{
label2.Text = value;
}
}
You see that object of Class1 gets reference to the Form1.
Inside Class1.ChangeLabelText you may do something like the following,
class Class1
{
Form1 _mainForm;
public Class1(Form1 form)
{
_mainForm = form;
}
public void ChangeLabelText()
{
//Form1 frm1 = new Form1();
//frm1.ChangeLabel("Surname", label2);
_mainForm.Surname = "Surname";
}
}
Observations after seeing your sample
It is a good practice not to expose/allow UI elements to be accessed by non-owner (Form1 is the owner in this case)
In button1_Click, you are already in a Form1 instance (object), however in ChangeLabelText, you are trying to create another instance of Form1.
Hope this helps you.
Another approach is to make Class1 raise an Event with the Name of the Label and the String to change it to. Your Form will receive the event, find the Label itself, and update the value. This way Class1 never needs a reference to Form1 or any of the Labels. It's still a bad design as you need to know the name of the Label, but this is the direction you went down anyways.
Here's what Class1 would look like:
public class Class1
{
public delegate void dlgChangeLabel(string lblName, string newValue);
public event dlgChangeLabel ChangeLabel;
public void ChangeLabelText()
{
if (ChangeLabel != null)
{
ChangeLabel("label2", "SurName");
}
}
}
Back in Form1, we need to subscribe to the ChangeLabel() event when we create our instance of Class1:
public partial class Form1 : Form
{
private void button1_Click(object sender, EventArgs e)
{
Class1 change = new Class1();
change.ChangeLabel += Change_ChangeLabel;
change.ChangeLabelText();
}
private void Change_ChangeLabel(string lblName, string newValue)
{
// see if we have a Label with the desired name:
Control[] matches = this.Controls.Find(lblName, true);
if (matches.Length > 0 && matches[0] is Label)
{
Label lbl = (Label)matches[0];
ChangeLabel(newValue, lbl); // update it in a thread safe way
}
}
private void ChangeLabel(string msg, Label label)
{
if (label.InvokeRequired)
label.Invoke(new MethodInvoker(delegate { label.Text = msg; }));
else
label.Text = msg;
}
}
Alternate version showing the creation of Class1 and wiring up of its Event during Form Load() so it's not attached to the Button handler:
public partial class Form1 : Form
{
private Class1 change = new Class1();
private void Form1_Load(object sender, EventArgs e)
{
change.ChangeLabel += Change_ChangeLabel;
}
private void Change_ChangeLabel(string lblName, string newValue)
{
Control[] matches = this.Controls.Find(lblName, true);
if (matches.Length > 0 && matches[0] is Label)
{
Label lbl = (Label)matches[0];
ChangeLabel(newValue, lbl);
}
}
private void ChangeLabel(string msg, Label label)
{
if (label.InvokeRequired)
label.Invoke(new MethodInvoker(delegate { label.Text = msg; }));
else
label.Text = msg;
}
}
Now you simply need to raise that event somehow in Class1. Just make sure it has a subscriber (is not null) before you raise it:
// in Class1
private void Foo()
{
if (ChangeLabel != null)
{
ChangeLabel("label2", "SurName");
}
}
You need to pass label as a parameter in your method
class Class1
{
public void ChangeLabelText(Label lbl)
{
Form1 frm1 = new Form1();
frm1.ChangeLabel("Surname", lbl);
}
}
And in your button click -
change.ChangeLabelText(label2);
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Class1 change = new Class1();
change.ChangeLabelText(this);
}
public void ChangeLabel(string msg, Label label)
{
if (label.InvokeRequired)
label.Invoke(new MethodInvoker(delegate { label.Text = msg; }));
else
label.Text = msg;
}
}
class Class1
{
public void ChangeLabelText(System.Windows.Forms.Form form)
{
if(form != null)
{
var labelIdWhoseTextNeedsToChange = "label2"; // Or any dynamic logic to determine which label will have to be updated.
var labelControl = form.Controls.Find(labelIdWhoseTextNeedsToChange, false);
if(labelControl != null)
{
form.ChangeLabel("Surname", labelControl);
}
}
}
}
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.
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"
I have Multiple class that need to subscribe to a delegate.
How is that possible? If I do declare delegate in my mainForm is that well coded?
Example code:
internal delegate void DEL_SetSingleVal(Single value);
public partial class Form1 : Form
{
Single Data;
ClasswithDel ClassDelegate;
TryToAccess AccessDelegate;
public Form1()
{
InitializeComponent();
ClassDelegate = new ClasswithDel();
AccessDelegate = new TryToAccess();
ClassDelegate.SetValCbk += new DEL_SetSingleVal(SetValCbkFn);
ClassDelegate.SetValCbk += new DEL_SetSingleVal(AccessDelegate.SetValCbkObj2Fn);
}
internal void SetValCbkFn(Single value)
{
Data = value;
}
private void SetValueLabel(String value)
{
label1.Text = value;
}
private void button1_Click(object sender, EventArgs e)
{
ClassDelegate.SetValue(Convert.ToSingle(textBox1.Text));
}
}
public class TryToAccess
{
private Single Data2;
internal void SetValCbkObj2Fn(Single value)
{
Data2 = value;//value come from From1
}
}
public class ClasswithDel
{
internal DEL_SetSingleVal SetValCbk;
public void SetValue(Single valuesent)//value to dispatch to Form1 and TryToAccess
{
SetValCbk(valuesent);
}
}
Is this looking good? Thanks for help!