I have many instances in my program where in the main form1, I call another form2 and run an event when form2 closes by using a FormCloseEventHandler. This updates some data on form1.
However, now I need form1 to run a formclose event handler from a different form3 that was launched in another part of the program, not form1.
How is this achievable? I thought of having a timer run every 10 seconds on form1 for a public flag set by the form3 formclose event, however this isn't the most elegant soution.
While I have no Idea about what do you want to do exactly and why you are using this pattern, but here is a solution that satisfies what you need:
Create this method in Form1:
public void Form3_FormClosed(object sender, FormClosedEventArgs e)
{
MessageBox.Show("Form 3 Closed");
}
In Load event of Form3 you can do this:
var f1 = Application.OpenForms.OfType<Form1>().FirstOrDefault();
if(f1 !=null)
this.FormClosed += f1.Form3_FormClosed;
You generally don't let forms show forms, because then it's hard to keep track of which forms are opened in your application and what their status is.
You need to implement a design pattern and introduce some class that handles the workflow between your forms. This class then decides which form to show in which occasion, and it can communicate between forms for example by using custom events.
Executing long codes from an event handler is a typical anti-pattern.
Extract the code in Form3.FormClose into a common helper class so you can call it from different places.
Related
Let's say I have a Form called Form1 which will somehow (how is not relevant) calls another form Form2
(Form1)
Form2 f2= new Form2();
f2.ShowDialog();
This Form2 has a button that will do some operation everytime the user clicks on this button.
However, I want that the first time i.e. when Form2 is just shown, the code in the button (some operation) gets executed.
In other words I have to be able to call the code in Form2's button_Click which is private.
Now I can think of some ways to make this possible(making the click event public etc) , but my question is what is the most elegant (or correct) way to do this?
I would add a property to Form2 to tell the form I like to automatically executed an action.
class Form2
{
public bool AutoExecuteSomeOperation { get; set; }
}
In Form1, you would set that property and in Form2 you would check and execute appropriate code if the property was set.
I would recommend that you refactor button_Click to call another method which you can also call for automatic execution. I like to keep event handler simple and executed only for the event on the control that served to name the event handler. Thus, you know that button_Click is an handler for a Click event on a control named button. It makes the code easier to maintain.
You can decide if you want to reset the property once the code is executed or you can add some validation that the property changes are valid. For exemple, you might want to ensure that the property is called before displaying the form.
In all cases, you should avoid having any reference to a control from an external form. Only Form1 itself should know that it contains a button. Any use from outside world should be done through a public property or public event of the form. That way, if you decide that the button should be replaced by an hyperlink, a menu item, a checkbox or anything else Form1 does not need to be updated. This is very similar to what should be done for UserControl. The less internal details leak, the easier it will be to make internal changes without having to update all caller.
The easiest approach is just make it public, however its not the bastion of great design.
Decoupled messaging is probably where you want to be, event aggregator or any pub sub method messaging system. This is a more modern and scalable approach, the participants need not know about each other allowing you to make the methods private and giving you a more maintainable decoupled solution, and keep your classes self consistent.
Unity, MvvmLight both have these sorts of messaging systems, however there are lots of them.
Example of how this might work
public Form1()
{
InitializeComponent();
EventPublisher.Instance.Subscribe<NewUserCreated>
(n => listBoxUsers.Items.Add(n.User.Name));
}
...
// some other class
private void Form2()
{
var user = new User()
{
Name = textBoxUserName.Text,
Password = textBoxPassword.Text,
Email = textBoxEmail.Text
};
EventPublisher.Instance.Publish(new NewUserRequested(user));
}
Move the code from the OnClick event into its own method (e.g. "DoWork"), then call that method from the OnClick event.
Either call it when you create the form
var frm = new demoForm();
frm.DoWork();
frm.Show();
Or call it in the forms constructor.
public partial class demoForm : Form {
public demoForm() {
InitializeComponent();
DoWork();
}
private void button1_Click(object sender, EventArgs e) {
DoWork();
}
public void DoWork() {
//Code here
}
}
I have several modeless Forms with grids to show data in a MDI Windows Forms app (Form1 in example code). Each of these Forms has a button that opens another Form as modal using ShowDialog (Form2 in example code), which allows editing the data shown in Form1. These modal forms have a Cancel button that closes the form without saving changes by setting DialogResult to Cancel.
I want to implement a timer that closes all forms after a certain time has elapsed from user login into the app. The problem arises if the timer triggers while a modal Form is open.
public partial class Form1 :
{ //Constructor ommited for brevity
private void btnEditData_Click(object sender, EventArgs e)
{
var form2 = new Form2();
timer1.Tick += (_, __) =>
{
form2.DialogResult = DialogResult.Cancel;
this.Close(); // this executes before form2.ShowDialog returns
};
timer1.Interval = 5_000;
timer1.Enabled = true;
form2.ShowDialog();
reloadData(); // here Form1 is already disposed because Close
// has been called on it. An exception is thrown as a consequence
}
private void reloadData()
{
if (this.IsDisposed)
// simulate using a disposed form
throw new ObjectDisposedException(this.Name);
}
}
My intention would be to somehow schedule the call Form1.Close() after form2.ShowDialog() has returned and the btnEditData_Click method has finished, so that there would be no danger of using a disposed form.
EDIT
After seeing your code, I'd suggest to introduce a third option in your Tick event handler that would be used to decide if you have to close the form. For example, Abort.
timer1.Tick += (_, __) =>
{
form2.DialogResult = DialogResult.Abort;
};
if (form2.ShowDialog() == DialogResult.Abort) Close();
else reloadData();
First thought: keep it simple and keep it clean. You don't have to keep track of your open forms, in an MDI application there is MdiChildren property of the parent form that will give you all the forms.
Then, to handle the closing part, you can use existing functionalities in .Net.
A difficult approach is using P/Invoke. You load Windows DLLs and use their functions to enumerate through all your forms and close which one you want. You can also simulate a click on Cancel button. You have to start with EnumWindows function (to identify the forms) and EnumChildWindows for form's children. I like this option because you have full control of your forms and controls, but can give you headaches if not familiar with the concept.
You can have a look at FormClosing event and subscribe to it. This event fires before the form is closed and you can make the clean up using it.
Using OOP - inheritance and override. Either you create a base form with custom close function that does the cleaning (and then inherit all your forms through it), or override form's close functions to achieve your goal.
I know how to pass data using event and delegate from Form2 to Form1 (backwards actually).
But I would like to know how to do it appropriately from Form1 (main form) to Form2.
Imagine Form2 and some center form to show some progress (with a progressbar) which would be common for plenty of other forms.
This is how I would like to look like:
public partial class Form2 : Form
{
public delegate void ProgressEvent(object obj);
public event ProgressEvent OnProgressShowing;
public Form2()
{
}
private void ShowingProgress(object obj)
{
//calling this method from Form1
//But it must be PRIVATE!
}
}
How to do it?
Like?
Form2 f2 = new Form2();
f2.OnProgressShowing += new Forms.ProgressEvent(?? ?what to put inside here?? I cannot access a private method on form2 ???);
I know one option is to create delegate and event on Form1, and pass a Form1`s reference to Form2, and subscribe to an event from Form2. But this is not what I would like. I would then have plenty of delegates and events in each of the other forms which would call this form2.
Since your first form is creating an instance of your second form and "owning" that instance, it is appropriate design for the method on Form2 that updates the UI based on the current progress to be public, and for the other forms calling it to call that method. There is also no need for Form2 to have an event at all, since it is not the one informing other forms that something has happened.
In this case, when creating a design document to indicate the relationships between these forms, we can say that Form1, or other forms have a Form2. A HAS-A relationship makes sense here. For Form2 it's goal is simply to display progress based on information provided by another class, so having a public method for another class to tell it to display progress is correct.
You're missing up a reversed relationship. If the child form needed to inform the parent forms of something, then Form2 would have an event, and in the handler of that event the forms creating it (i.e. Form1) would generally be calling their own private methods in the handler, or possibly accessing other public methods of Form2 to pull or push data out of it.
The idea here is that is'a appropriate for Form1 to "know about" Form2. It's appropriate for it to have a reference to it, and to know about whatever it exposes publicly. Form2, however, shouldn't know anything about what form creates it. It should be able to display progress for any form that can tell it what progress to show. If it needs to accept a reference to Form1 or know anything about it at all, then that can't happen. By using an event on Form2 when it needs to pass information out to the form that creates it, it can avoid needing to know what form that is.
I have 2 forms ...when i start the application..and use the close "X" from the title bar the entire application closes...now when i select an option from the 1st form in my case it is a button "ADD" as its a phonebook application..it goes to the 2nd form as i have used 1stform.hide() and 2ndform.show()...now when i do "X" from the title bar it doesnt shutdown completely as the 1stform is not closed....how to program it in such a way tht any stage the entire application should close
Your first form is set as the startup form. That means whenever it gets closed, your entire application is closed. And conversely, your application does not close until it gets closed. So when you hide the startup form and show the second form, the user closing the second form does not trigger your application closing because they have only closed a secondary, non-modal dialog.
I recommend changing your design so that the startup form is also the main form of your application. No sense trying to work around built-in functionality that can actually be useful. You want the application to quit when the main form is closed, no matter what other child forms are opened.
But the quick-and-dirty solution in your case is to make a call to Application.Exit. That will close all of the currently open forms and quit your application immediately. As I said just above, I don't so much recommend this approach because having to call Application.Exit from every form's FormClosed event handler is a sign that something has gone seriously wrong in your design.
If the single startup form paradigm doesn't work out for you, you should look into taking matters into your own hands and customizing the Main method in your Program.cs source file. See the answers given to this related question for some ideas on how that might work for you.
What you can do is to use the Form's FormClosing event, and add the following code:
Application.Exit();
This will stop the entire application, and close all windows. However, if a background thread is running, the process itself will survive. In this case you can use:
Environment.Exit();
Add a Application.Exit on every forms's Closing event
like this:
Create an closing event handler first
private void Form_ClosingEventhandler()(object sender, CancelEventArgs e)
{
//Perform any processing if required like saving user settings or cleaning resources
Application.Exit();
}
then bind this event to any form you create.
//Where you create new form and show it.
Form1 frm= new Form1();
//set other properties
frm.Closing += new EventHandler(Form_ClosingEventhandler);
Form2 frm2= new Form2();
//set other properties
frm2.Closing += new EventHandler(Form_ClosingEventhandler);
Surely you don't want to shut down the entire application after the user adds a phone number? You just need to make sure that your main window becomes visible again. Write that like this:
private void AddButton_Click(object sender, EventArgs e) {
var frm = new AddPhoneNumber();
frm.StartPosition = FormStartPosition.Manual;
frm.Location = this.Location;
frm.Size = this.Size; // optional
frm.FormClosing += delegate { this.Show(); };
frm.Show();
this.Hide();
}
I Have one Form (LoginForm) which has a Background Worker Monitoring a database for new entries.
Then I have another Form (AdminForm) which I need to signal to update its datagrids whenever new data is available.
I could poll the database in the AdminForm too, but considering that LoginForm is already doing some polling, which can be costly, I just want to signal AdminForm to update the DataGridViews with the new data.
You may ask, why is LoginForm doing the polling when youre showing the stuff in the AdminForm? Well, LoginForm is actually processing the data and sending it over the Serial Port :$ heheh. I want it to be able to process the data without having an administrator logged in all the time.
You could have your background worker signal progress (WorkerReportsProgress property set to true, then calling ReportProgress).
In that event handler (OnProgressChanged), invoke another public event you create that signals receivers that data should be updated. Your admin form could subscribe to that event.
As I'm not quite sure whether the OnProgressChanged event is invoked in the context of the main thread or in the context of the background worker's thread, I'd suggest you use this.Invoke when actually doing the UI update.
EDIT
Using OnProgressChanged and another event, do the following:
In the Form1 class, declare a new event like
public event EventHandler DataChanged;
Also, declare a method that raises the event:
protected void OnDataChanged()
{
if (DataChanged != null)
DataChanged(this, EventArgs.Empty);
}
Then, in the OnProgressChanged method, call your OnDataChanged method.
All you need to do now is:
Attach Form2 to the event in Form1 using something like form1.DataChanged += new EventHandler....
In that event handler, update your controls in Form2
You might for example pass the current instance of Form1 to the constructor of Form2 when it is created and attach the event handler in the constructor of Form2. There are other options as well.
EDIT 2
Thinking about it: Why don't you put the polling code into a separate class that provides an event whenever data changes. Then you can attach both Form1 and Form2 to these events. This would make Form2 independant from Form1.