In C# if you create an object on a button click, at the end of the click event, the object is no longer in scope. How do I create an object on the click of a button, but release it on the click of another button. Here is what I am trying to do:
Build an application that accepts user data and adds it to a database. The application contains two classes: Personal information and employer information. The personal information class object should be created when the "Start Application" button is pressed. The application then opens a tab that requests personal information. If the "Add Employer" button is selected, an instance of Employer is created. After they press submit at the end, it should close the one or two objects and return to the main menu. When the next person presses the button, it should go through the process again.
I can create objects on click or in the class itself, but how do you limit the life of the object between to button presses?
Thanks!
Use a class-scoped data member. Very quick example:
public class MyClass
{
private Foo foo;
private void Button1_Click(object sender, EventArgs e)
{
this.foo = new Foo();
}
private void Button2_Click(object sender, EventArgs e)
{
// I can access this.foo here. I can also dispose of it
// if it is IDisposable and/or I can set it to null.
// To check if button 1 was pressed, check to see if the object
// is null (if it is, button 1 wasn't pressed)
}
}
You can do this by defining one of the event handlers anonymously inside of the other event handler:
void button1_Click(object sender, EventArgs e)
{
Foo data = new Foo();
EventHandler handler = null;
handler = (s, args) =>
{
//Use Foo here
DoStuff(data);
button2.Click -= handler;
};
button2.Click += handler;
}
Note that we need to do some extra work to make sure to remove the event handler when it is clicked here. If you don't need to do that, then it does simplify the code.
Here the anonymous method is closing over the local variable, which will extend that variable's lifetime for the lifetime of the anonymous method. This approach, unlike those using a field, actually creates a variable who's lifetime lasts until the next button is clicked, and doesn't leave a variable lying around with a meaningless value before the first button is clicked or after the second is clicked.
Sounds like you're wanting to use properties?
Here's a link to MSDN, but there's lots out there if you just Google'C# properties'
I can create objects on click or in the class itself, but how do you limit the life of the object between to button presses?
Sadly you're pretty much stuck with putting it at class scope. There is no simply way to limit the scope of an object to just a few methods within a class. If your 2 event handlers can access your Employer, then so can every other method within the class. You just have to not use it.
However, out of curiosity I managed to hack around and produce the effect you want. I did a class with 2 event handlers, one for each button, as well as a private Employer variable.
public class ButtonHandler
{
private Employer employerData;
public void button1_Click(object sender, EventArgs e)
{
employerData = "set data here";
}
public void button2_Click(object sender, EventArgs e)
{
employerData = "use / clear data here";
}
}
Then I went to my Form.Designer.cs and manually changed the button.Click event handler from the default this.btnLoadPlaylist_Click to my functions within the ButtonHandler class above. The problem with this is it gets deleted every time the Designer.CS is regenerated by Visual Studio. So the effect is possible, just not convenient with the tools we are given.
Related
So I've been following the Visual Studio tutorials that microsoft has available (more specifically the math quiz one found at https://msdn.microsoft.com/en-us/library/vstudio/dd492172.asp)
but I deviated a bit from the tutorial because I wanted to see if I could create an event and call it using the EventHandler delegate though it might not be the best solution.
public event EventHAndler quizStarted;
here is the code for creating the event.
now in the method
public Form1()
{
this.quizStarted += new System.EventHandler(this.showThatTheQuizStarted);
InitializeComponent();
}
I have initialized my event with an instance of the EventHanlder that points to my showThatTheQuizStarted method.
public void showThatTheQuizStarted(object sender, EventArgs e)
{
MessageBox.Show("Quiz Has Started");
}
and finally when the start button is pressed I call the quizStarted event as shown below.
private void startButton_Click(object sender, EventArgs e)
{
startButton.Enabled = false;
quizStarted(this, new EventArgs());
this.StartTheQuiz();
}
in this order the message box goes away after hitting okay once, also in StartTheQuiz() nothing calles a message box directly or indirectly.
but if I place the this.quizStarted += new System.EventHandler(this.showThatTheQuizStarted); line into the startButton_Click method, the message box appears twice one right after the other.
Though I found a solution I would like to know why this happens if I place this line of code out of the constructor.
If...
this.quizStarted += new System.EventHandler(this.showThatTheQuizStarted);
... gets called multiple times, as would happen if you move it inside a button Click event handler method, then you are in fact adding and registering a new event handler every time.
In other words, when quizStarted is invoked, it may call multiple event handlers, depending on how many you choose to register. If you register the same event handler multiple times, then it will get called as many times.
That's why you want to leave the registration in a place where you are guaranteed to register the event handler once and only once.
I'm working on a project and I'm in a situation where the administrator needs to accept new users into the system. I've got a form that only admins can access, which shows a list of all the waiting applicants. I've found out how to create buttons at run time and how to add an event handler for the click event, but the handler requires a method by the same name to run.
Obviously I can't just put code for a method inside a for loop, unless I'm mistaken. How would I give the program the ability to support an potentially infinite amount of applicants?
void AcceptUsersAdminLoad(object sender, EventArgs e)
{
//FOR LOOP - To be finished. Will read an xml file to find out # to loop.
Button newButton = new Button();
newButton.Click += new System.EventHandler(newButtonClick);
newButton.Text = "Accept";
Panel1.Controls.Add(newButton);
}
private void newButtonClick (Object sender, System.EventArgs e){
}
This works, but as I've said, only for one button. As relatively painless as it would be to copy the method and append it's name with a number a hundred times, I'd prefer to find a way with support for more.
You can use that same method for all of your buttons! The sender parameter will tell you which button is the source, simply cast it to a button. You can store an ID of some sort in the .Tag() property of the button so you know who you are working with (when you create them, assign it).
private void newButtonClick (Object sender, System.EventArgs e){
Button btn = (Button)sender;
// ... do something with "btn" in here ...
}
Answer to the titular question: You don't create methods in a loop. You will occasionally create anonymous methods in a loop, but save that for later :).
To do what you want though: When you generate these buttons, they should all be pointing to the same event handler. The logic you want to run is the same, but the data is different.
How you get the data to the function is not trivial, one (hackish) way to do it is to store the related object (or its index) in the Tag property of the button, which you can then retrieve via the sender argument of the event handler.
Assuming program logic changes a button tag based on something random - but using the UI thread.
Is the Button Tag value reliable to use in a click event? i.e. Will it be the same value as-at the time of the event handler as it was at the time of the click?
If not, what is the best method to pass an event specific parameter into a button click event that will be safe?
Update
Added an example as requested. (Remember this is just theoretical).
Windows.Forms.Timer timer = new Timer();
timer.Interval = 1;
timer.Tick += new EventHandler(timer_tick);
timer.Start();
void timer_tick(object sender, EventArgs e)
{
this.button.Tag = Random.NextInt(100).ToString();
}
void button_click(object sender, EventArgs e)
{
string s = (string)((Button)sender).Tag;
Console.WriteLine("Tag value as at button push: " + s);
}
Put another way, the question boils down to: can events be wedged into the GUI event queue that allow the state of the button to be changed between the button being pushed and the click event handling the push?
Assuming that you are using winforms.
IMHO you can use Tag property to pass control specific parameters but you must also remember that windows forms controls are not thread safe. To make a thread-safe call, you must use InvokeRequired. The following link gives an example to set text for TextBox in a thread-safe way but it should not be very different for Tag property.
http://msdn.microsoft.com/en-us/library/ms171728(VS.80).aspx
Hope this helps.
Hello
I have read that events can be raised the same way as methods. Well it works for my custom events (I create a delegate, the event and I am able to raise the event by calling it).
However I am not able to manually raise events like MouseClick and other, it keeps saying that it must appear on the left side of the += operator. What is the problem?
While I am certain you'll get other answers more informative than this one, basically you can't "raise" an event outside the class that contains it. MSDN has this to say about events
Events are a special kind of multicast
delegate that can only be invoked from
within the class or struct where they
are declared (the publisher class). If
other classes or structs subscribe to
the event, their event handler methods
will be called when the publisher
class raises the event.
If you wanted to literally raise the event for, say, a Windows Forms Control MouseClick, you'd have to create a subclass of that control and either invoke base.OnMouseClick() or override it.
If this is a button, you can programmatically click it using the PerformClick method.
Sadly, this only works on buttons and not other types of Controls... except MenuItem.
If you want to click button you should call:
button1.PerformClick();
If you want to call MouseClick please refer to this forum, there is solution in c# using windows api:
private void button1_Click(object sender, EventArgs e)
{
//Enter your code here
}
void Page_Load(object sender, EventArgs e){
this.button1.Click += new System.EventHandler(this.button1_Click);
this.button1_Click(this, e);
}
Let's say you want to manually raise the event "click". This works for me:
public partial class CustomButton : UserControl
{
public new event EventHandler Click;
private void lblText_Click(object sender, EventArgs e)
{
Click(this, e);
}
}
I am trying to determine if my application is closed through clicking the "X" on the windows form, or if they clicked an "Exit" button I have on it. Right now I am using StackTrace.GetFrame(someIndex) to determine how, but i am looking for a more definitive way since it looks like these frame orders arent guaranteed. Is there a better way to make the distinction? This is a .NET 3.5 WinForm, and Im writing in C#.
Use a different event to handle your own "Exit" button click. In your own "Exit" event handler do your extra logic, or set some state variable, and then call the normal application close method.
Post some samples of how your events are wired up and I get give a more specific example. In general it would look something like this:
private void btnMyExit_Click(object sender, EventArgs e)
{
// TODO: add any special logic you want to execute when they click your own "Exit" button
doCustomExitWork();
}
public static void OnAppExit(object sender, EventArgs e)
{
doCustomExitWork();
}
private void doCustomExitWork()
{
// TODO: add any logic you want to always do when exiting the app, omit this whole method if you don't need it
}
Use the FormClosing event and query the FormClosingEventArgs for the enum CloseReason value.