Declare and Initialize a ToolStripMenuItem with an Event Handler - c#

I have a ToolStripMenuItem that I want to declare and instantiate with a String, a null value for an image, and an Event Handler for its Click event. This is the format Intellisense is expecting:
ToolStripMenuItem(string text, System.Drawing.Image image, EventHandler onClick).
But I am unable to assign the Event Handler and I do not know the proper syntax to do so. As a workaround, I assign the .Click event in the constructor like so...
class Timer
{
//The other WinForms objects and my methods are omitted.
private ToolStripMenuItem StartButton = new ToolStripMenuItem("Start Timer");
public Timer()
{
//I want the assignment of StartButton_Click in my declaration and initialization of StartButton, not here.
StartButton.Click += new EventHandler(StartButton_Click);
}
public void StartButton_Click(object sender, EventArgs e)
{
//The logic here is not relevant.
}
}
I tried the syntax below but I keep getting the error:
"CS0236 A field initializer cannot reference the non-static field, method, or property 'Timer.StartButton_Click(object, EventArgs)'"
new ToolStripMenuItem("Start Timer", null, new EventHandler(StartButton_Click));
Intelliense suggests I use the format
EventHandler(void(object,EventArgs)target)
but I do not know how to fill out the expected syntax property. How do I write the declaration of StartButton so that the method StartButton_Click is called after a Click event?

The correct place to instantiate it is in the constructor. Do it all at once, like this:
private ToolStripMenuItem StartButton;
public Timer()
{
StartButton = new ToolStripMenuItem("Start Timer", null, StartButton_Click);
}
As for that compiler error, you can read more about it here, although it's sparse on the details.

From Stack Overflow: You cannot use an instance variable to initialize another instance variable. Why? Because the compiler can rearrange these - there is no guarantee that reminder will be initialized before StartButton_Click, so the above line might throw a NullReferenceException.
Make the method static and you should be good to go.

Related

Passing a variable between forms WINFORMS [duplicate]

Wierd behaviour when passing values to and from second form.
ParameterForm pf = new ParameterForm(testString);
works
ParameterForm pf = new ParameterForm();
pf.testString="test";
doesn't (testString defined as public string)
maybe i'm missing something? Anyway I'd like to make 2nd variant work properly, as for now - it returns null object reference error.
Thanks for help.
Posting more code here:
calling
Button ParametersButton = new Button();
ParametersButton.Click += delegate
{
ParameterForm pf = new ParameterForm(doc.GetElementById(ParametersButton.Tag.ToString()));
pf.ShowDialog(this);
pf.test = "test";
pf.Submit += new ParameterForm.ParameterSubmitResult(pf_Submit);
};
definition and use
public partial class ParameterForm : Form
{
public string test;
public XmlElement node;
public delegate void ParameterSubmitResult(object sender, XmlElement e);
public event ParameterSubmitResult Submit;
public void SubmitButton_Click(object sender, EventArgs e)
{
Submit(this,this.node);
Debug.WriteLine(test);
}
}
result:
Submit - null object reference
test - null object reference
pf.ShowDialog(this); is a blocking call, so pf.Submit += new ParameterForm.ParameterSubmitResult(pf_Submit); is never reached: switch the order.
Submit(this,this.node); throws a null object reference because no event is assigned to it (see above). Generally, you should always check first: if (Submit != null) Submit(this,this.node);
You should change ``pf.ShowDialog(this);topf.Show(this);` so that your main form isn't disabled while your dialog box is open, if that's what you want, or use the model below (typical for dialog boxes.)
I'm not sure what pf_Submit is supposed to do, so this might not be the best way to go about it in your application, but it's how general "Proceed? Yes/No" questions work.
Button ParametersButton = new Button();
ParametersButton.Click += delegate
{
ParameterForm pf = new ParameterForm(testString);
pf.ShowDialog(this); // Blocks until user submits
// Do whatever pf_Submit did here.
};
public partial class ParameterForm : Form
{
public string test; // Generally, encapsulate these
public XmlElement node; // in properties
public void SubmitButton_Click(object sender, EventArgs e)
{
Debug.WriteLine(test);
this.Close(); // Returns from ShowDialog()
}
}
When you want to use your second variant, you have to use a getString()-Method, where you can put the e.g. "testString". The way you wrote it, "testString" should be a method (and got brackets).
EDIT (a bit more precise):
You could write:
pf.getString(testString);
, if "pf" is an instance of your own class, otherwise you had to look up, whether you can retrieve a String in this class.
the thing was in line order :)
pf.Submit += new ParameterForm.ParameterSubmitResult(pf_Submit);
and
pf.Test = "test";
should have been set before
pf.ShowDialog(this);
my mistake thingking that parameter can be passed after 2nd form was displayed
thnx for answers

C# - dll event handler

I am setting a .dll file to create a new form and a new button, but i want that button to do something. Is it possible to create a event handler in a dll file?
public static byte sbuton( string er, int by,int re)
{
Form fg = new Form();
fg.Show();
Button b1 = new Button();
fg.Controls.Add(b1);
b1.Text = er;
b1.Location = new Point(by, re);
return 0;
}
This is the code that creates a form with a button in it.
When I try to create a new event handler, as I would in a form, I get this error: "An object reference is required for the non-static field, method or property".
public static byte sbuton( string er, int by,int re)
{
Form fg = new Form();
fg.Show();
Button b1 = new Button();
fg.Controls.Add(b1);
b1.Text = er;
b1.Location = new Point(by, re);
b1.Click += new EventHandler(b1_click);
}
private void b1_click(object sender , EventArgs e)
{
}
This is the code from the form where I want use the dll
private void button1_Click(object sender, EventArgs e)
{
if (richTextBox1.Text.Contains("add") && richTextBox1.Text.Contains("buton") && richTextBox1.Text.Contains("text"))
{
form.sbuton("buton", 10, 10);
}
}
This creates a button, but nothing happens when the button is clicked, because no event handler is assigned to it in the .dll file.
And also,sorry for the bad english,it is not my native language.
What can i do?
Thanks!
It's not clear from your question what the context is. Without a good, minimal, complete code example it's difficult to provide a really good answer.
But in your example, it appears that your event handler is in the same DLL (and I assume, the same class) as the sbuton() method. If that's the case, then all you need to do in order to use the event handler is make it a static method:
private static void b1_click(object sender , EventArgs e)
{
}
Now, since you didn't post any of the code in the method, never mind the full context, it's not certain that would work. I.e. if there is a good reason for that method being a non-static method, then you will have to subscribe the event handler by referring to the method with a reference to an actual instance of the containing class. If that's the case, then the question commenter Daniel Kelley suggests, An object reference is required for the non-static field, method, or property?, may turn out to be appropriate for your needs after all.
Finally, note that none of this has anything to do with the code being in a DLL. You would have run into this same problem had your sbuton() method been in the same project from which you're calling it.

Trouble with raising custom event handling between 2 forms

New to C#. Like the title, I'm having difficulty trying to raise an event. It will eventually then be consumed on another form.
What I'm trying to do is have many instances of a custom user control (my event raising form(s)) that creates a tcp client, connects, and then closes. When this tcp client has an "error", be it a catch exception, I want an event to be raised. I'm forcing the error right now by having my internet turned off to test. My first problem is I can't even get the event to be raised at all. I'll show the event code I'm working with on my custom user control:
public delegate void TaskCompleteEventHandler(object sender, TaskCompleteEventArgs e);
public event TaskCompleteEventHandler TaskComplete;
public class TaskCompleteEventArgs : System.EventArgs
{
// add local member variables to hold text
private string errorString;
// class constructor
public TaskCompleteEventArgs(string ErrorString)
{
this.errorString = ErrorString;
}
// Property
public string ErrorString
{
get
{
return errorString;
}
set
{
errorString = value;
}
}
}
This is my method that processes the exception and ideally would raise the event and allow the host form to print the string and exception accordingly.
private void ErrorLogging(string ex)
{
errorString = String.Format(/*...errorString formatting...*/);
// instance the event args and pass it the errorString value
TaskCompleteEventArgs args = new TaskCompleteEventArgs(errorString);
// raise the event with the updated arguments
TaskComplete(this, args); //----> THIS IS WHERE I GET AN ERROR!! <----
this.Dispose();
}
The error is Object reference not set to an instance of an object.
Here's the Watch screen of my TaskComplete(this, args)
I can't seem to debug this... I'm just not strong enough yet to know what I've done wrong. How is it causing side effects?
I'm sure I'm going to have more issues on my main form when I get this going... Does anyone have a clue what's causing this? Thanks in advance.
EDIT: On my main form:
public Form1()
{
InitializeComponent();
// Start control disabled and subscribe each control the event
foreach (var control in controlList)
{
control.Enabled = false;
control.TaskComplete += new dev_emu_project.dev_emu_widget.TaskCompleteEventHandler(OnTaskComplete);
}
}
List<dev_emu_project.dev_emu_widget> controlList = new List<dev_emu_project.dev_emu_widget>();
public void OnTaskComplete(object sender, dev_emu_project.TaskCompleteEventArgs e)
{
//.... work for processing
}
}
You are getting a NullReferenceException because you're invoking an empty event, meaning no delegate has been registered to it. You need to make sure TaskComplete isn't null before invoking it.
Add a null check before invoking to make sure someone did register to your event:
if (TaskComplete != null)
{
TaskComplete(this, args);
}
From MSDN Event Tutorial:
Invoking an event
Once a class has declared an event, it can treat that event just like a field of the indicated delegate type. The field will either be null, if no client has hooked up a delegate to the event, or else it refers to a delegate that should be called when the event is invoked. Thus, invoking an event is generally done by first checking for null and then calling the event

How to get access to the progressBar?

In my program on the WindowsForms I have a MainForm, which contains ProgressBar. In the ChildForm I want to change it's value, but I'm getting an error:
"int" does not contain a definition for "Value". Unable to find an extension method "Value", taking first argument of type "int" (missing a using directive or an assembly reference?)
Main Form:
public int ProgressBar5
{
get { return progressBar5.Value; }
set { progressBar5.Value = value; }
}
Child Form:
static MainForm mainForm = new MainForm();
private void button1_Click(object sender, EventArgs e)
{
mainForm.ProgressBar5.Value++; // Error
}
Edit1:
In the main form the value of the progressBar5 is always equals 1. What can be reason of it? I thought, that static can fix it.
Main Form:
private void button9_Click(object sender, EventArgs e)
{
Child child = new Child();
child.ShowDialog();
MessageBox.Show(progressBar5.Value.ToString()); // All time Value = 1
}
Your ProgressBar5 property already takes care of accessing the Value property of the progress bar. It doesn't return the progress bar, but rather the current progress. This means that the caller doesn't need to access the Value property of the result (since there is none).
Just write:
mainForm.ProgressBar5++;
Of course, for clarity's sake, it would probably be better to call such a property CurrentProgress rather than ProgressBar because it doesn't return a progress bar, it returns (or sets) the current progress.
Of course, better still would be for the child for to have no knowledge of the main form at all. Rather than passing in the main form itself the child form can accept an IProgress instance. It can then report progress to whomever is creating this form, whether that be your main form, or anything else. The main form is then responsible for doing whatever it wants when progress is reported, which it can define in either an event handler or a delegate passed into the Progress constructor.
The problem is that you have two variables with similar names, causing quite a bit of confusion. When you try and access the ProgressBar5 variable you are actually getting the int property you declared in the first snippet.
Since int does not have a Value property, you get the compile time error. Change your code to:
static MainForm mainForm = new MainForm();
private void button1_Click(object sender, EventArgs e)
{
mainForm.ProgressBar5++; // Fixed!
}

Initializing events with initializer syntax

I often want to write something like this:
new Form
{
Text = "Caption",
Controls =
{
new Button { Text = "Button 1", Click = (s, e) => MessageBox.Show("Button 1 Clicked"), Location = new Point(10, 10) },
new Button { Text = "Button 2", Click = new EventHandler(Button2Clicked), Location = new Point(10, 40) },
new Button { Text = "Button 3", Click = Button3Clicked, Location = new Point(10, 70) },
},
}
Initializer syntax is just sugar, so why can't the compiler figure out how to generate code for an event subscription?
Gimme some sugar, baby!
When initializer syntax was invented, someone must have thought about events and rejected them. I've been trying to imagine what the rationale might have been and am coming up blank.
Is it because an event is a multi-cast object that might have more than one subscriber? No, this is an initialization process; There can be no other subscribers. [Updated] Not true, initializers are applied post-construction and an object can subscribe to its own events.
A note to Eric: I've heard the Why doesn't C# implement feature X speech. In this case, someone was already there, implementing initializers.
Updated
There seems to be contention/confusion because I used Click = in my example. The actual syntax is not relevant to the question. It could just as easily be Click += which mirrors the way you have to add a handler normally. I prefer the former because it's consistant with the rest of the initializer syntax, but ultimately I don't care, just so long as I can subscribe to an event in an initializer list.
Another Update
I do realize that adding the feature now is probably unlikely. The first issue that comes to mind is that Intellisense has to be updated. There are probably many other things that would hinder adding this feature now. My question is: Why didn't they add it in the first place. There must have been something compelling that warrented the 'nay' vote.
I cannot see any reason why they could not have provided this small teaspoon of sugar, I guess they just didn't!
There is already quite a lot of syntactic sugar involved in events, if simply declare an event on a class without providing your own implementation, the compiler is providing a delegate backing field for you, plus add / remove 'method' implementations. ALso, when you add an event handler, the compiler uses delegate inference, allowing you to simply point to a method, rather than create a delegate that represents the method.
Interestingly, Mono C# does allow you to add an event handler in an object initializer:
http://tirania.org/blog/archive/2009/Jul-27-1.html
Time to switch to Mono ;-)
Try simply assigning an event:
Click = (o,e) => { <CODE> }
Doesn't work. Initializers work only with things you can directly assign like that. This is because events need to be able to notify anyone they want (you shouldn't be allowed to remove someone else's registration for that event on accident).
I'm not sure if this is their reasoning, but it works for me.
There's a big difference between fields and events. There's an excellent article here outlining the differences, but that's the answer to your question: A field can be assigned a value; an event looks like a field but is a very different beast.
Edit
From the article I linked to:
We have seen that the event keyword is a modifier for a delegate declaration that allows it to be included in an interface, constrains its invocation from within the class that declares it, provides it with a pair of customizable accessors (add and remove), and forces the signature of the delegate
Remember that event is a shortcut; behind the scenes, the compiler creates an object with add() and remove() methods. Like:
public class Button {
public event EventHandler Click {
void add {...}
void remove {...}
}
}
Perhaps this will offer some insight... :
Button btn = new Button {Click += (s, e) => MessageBox.Show("hello")};
The error message you get is "Cannot initialize type 'Button' with a collection initializer because it does not implement IEnumerable"
Still another note... if you assign the event handler from within the form, you can do this:
this.button1.Click += (s, e) => this.textBox1.Text = e.ToString();
You couldn't access form variables from the code you've created. I get where you're coming from, and I don't disagree... what you're doing could be made to work. I guess my point is that there are reasons why the decision was made not to make it work.
Yep, should be part of the language!
But, here's a tricky workaround that lets you subscribe to events within an initializer list...
public class TestClass
{
public class MyButton : Button
{
public EventHandler ClickSubscriber
{
get { return null; }
set { Click += value; }
}
}
public static void RunTest()
{
new Form
{
Text = "Caption",
Controls =
{
new MyButton
{
ClickSubscriber = (s, e) =>
MessageBox.Show("Button 1 Clicked"),
},
},
};
}
}

Categories

Resources