I'm using visual studio 2010
frmMain has a "Register" button which calls another form newReg
This is the code for the button in frmMain that calls the second form. The problem is that the MessageBox("So Far So Good") never gets called. The dialogResult doesnt seem to be recognized.
private void btnRegisterNew_Click(object sender, EventArgs e)
{
// newReg Constructor Call
newReg = new frmRegisterNew();
// show form
newReg.Show();
if (newReg.DialogResult.Equals(DialogResult.OK))
{
MessageBox.Show ("So Far So Good");
}
}
The second form has some fields to fill in and a button "register". I've set the dialogResult of this button to 'ok' in the properties window and also, I think, in the code. When the "register" button in the second form is clicked it checks the input, tries to update a database and closes if successful. Here is that bit of code:
dbConnection db = new dbConnection();
db.dbConnect();
if (db.dbRegisterVehicle(txtNewReg.Text, txtNewMake.Text, txtNewModel.Text, txtNewColour.Text, OwnerID))
{
// if insert worked close
this.DialogResult = DialogResult.OK;
this.Close();
}
db.dbDisconnect();
I'm sure what to try, or what I might be over looking.
Use ShowDialog
newReg = new frmRegisterNew();
var dialogResult = newReg.ShowDialog();
if(dialogResult==DialogResult.OK)
{
....
}
The ShowDialog method is a good way to go but be aware of the differences between Show and ShowDialog. The latter will be modal which means that you can't access your original form until the new form is closed. This is why it blocks the check and may or may not be what you want.
When you call Show, it doesn't block, so that's why your code was immediately checking to see if the DialogResult was equal to OK (it wasn't equal to OK because your new form had barely opened by the time the check was made).
The alternative to using ShowDialog, if you want to use Show, is to handle the new form's closed event.
frmRegisterNew newReg = new frmRegisterNew();
newReg.FormClosed += (s, o) =>
{
if (newReg.DialogResult == DialogResult.OK)
{
MessageBox.Show ("So Far So Good");
}
};
newReg.Show();
This means that your code will continue to work and your new form will not be modal, but when the new form it closed, the FormClosed event handler will be fired. Don't worry if you're not familiar with the event handler notation above (they're called anonymous methods) but you can still use the event handler as normal.
newReg.FormClosed += new FormClosedEventHandler(newReg_FormClosed);
void newReg_FormClosed(object sender, FormClosedEventArgs e)
{
MessageBox.Show ("So Far So Good");
}
Try instantiating the DialogResult class and using it this way:
DialogResult dr = new DialogResult();
newReg = new frmRegisterNew();
dr = frmResgisterNew.ShowDialog();
if ( dr == DialogResult.OK )
//Take an action here.
Form.Show() is non-blocking and will return very quickly. Your check on newReg.DialogResult.Equals(DialogResult.OK)) will therefore occur before the user has had a chance to press the button. Furthermore, note this warning about closing the window:
If a Form is displayed as a modeless window, the value returned by the DialogResult property might not return a value assigned to the form because the form's resources are automatically released when the form is closed.
(via the Form.DialogResult Property msdn library page)
You can either call From.ShowDialog() or, if you need to keep interaction on the main form, pass a delegate for the other form to call when it's completed.
Edit: A couple of points to keep in mind:
In addition to the warning above about closing the form, you have to be careful about trying to access the contents from a method dispatched from newReg's message loop (including the function that called Close()) after it's been disposed.
If you end up using ShowDialog() instead of Show(), however, this.Close() will not dispose the form. In fact, it essentially does nothing since setting DialogResult to anything other than None will automatically hide the form. If you need deterministic clean-up (presumably why you're calling Close() in the first place), you should call newReg.Dispose() after you're done with it in btnRegisterNew_Click. Otherwise, the form will be disposed at some unpredictable time in the future (provided your application doesn't end abnormally in the interim).
If you use an anonymous function as mentioned by keyboardP, be aware that it can get hard to debug when something goes wrong (especially if you're relatively new to the language and framework).
Related
On the click of a button (that's in the main form), I show a (second) form that does some parsing (decompress, extract, get information, etc) of an archive file. After this process is done, I close the second (parsing) form and show another (third) form that contains the parsed information from the archive.
Click callback looks like this:
private void ParseInputBackupButton_Click(object sender, EventArgs e)
{
Form PBF = new ParseBackupForm();
Form CBF = new CreateBackupForm();
PBF.FormClosed += delegate
{
CBF.ShowDialog();
};
PBF.ShowDialog();
}
and the second form:
private void ParseBackupForm_Load(object sender, EventArgs e)
{
new Thread((ThreadStart)delegate
{
// parse and update form
this.Invoke((MethodInvoker)delegate
{
this.Close();
});
}).Start();
}
The problem is when the third form (CreateBackupForm) appears, the second form (ParseBackupForm) doesn't close. They both appear. If I don't show the third form, the second form closes.
What am I doing wrong?
As you are calling ShowDialog on both windows, couldn't you just call them one after the other?
i.e.
PBF.ShowDialog();
CBF.ShowDialog();
The second call will not be made until the PBG dialog has closed.
Edit: The reason why the second form doesn't close is that the you are subscribing to an event raised by the form as it closes, then within that delegate calling ShowDialog which blocks the form from actually closing.
As far as I can remember .ShowDialog() is a blocking method, so would stop the other form from completing its close method until that form is also closed?
Pretty sure you can use .Show() to just make a form visible without blocking?
You could BeginInvoke the call of CBF.ShowDialog();, thus not blocking the completion of the FormClosed callback method and the closing of the PBF form.
We created a new form that we're showing via ShowDialog and added a "Cancel" button to it. Here's how we're opening the form from its parent:
// _modalForm is a class-level instance of our ModalForm class
var result = _modalForm.ShowDialog(this);
MessageBox.Show(result.ToString());
Here's the cancel button's Click event handler in ModalForm.
private void btnCancel_Click(object sender, EventArgs e)
{
Close();
}
In our FormClosing event, we have this code (based on this answer).
private void ModalForm_FormClosing(object sender, FormClosingEventArgs e)
{
e.Cancel = true;
Hide();
_parentForm.RefreshData();
}
Surprisingly, when we click the "Cancel" button (or use the "X" button at the top of the form), the FormClosing event is raised twice. Both times the CloseReason is UserClosing.
I double checked to make sure InitializeComponent isn't call twice and that we only subscribe to the event once. btnCancel is not set at the CancelButton property for the form. It also doesn't have DialogResult set in the designer. When I check the return value of ShowDialog though, it is set to DialogResult.Cancel.
Changing btnCancel_Click to just be DialogResult = DialogResult.Cancel instead of Close() and doing nothing except _parentForm.Refresh() in the FormClosing event fixes the issue of the event getting raised twice.
Does anyone know why in this particular scenario the FormClosing event gets raised twice?
That's because hiding a modal form will cause it to close with DialogResult.Cancel as dialog result. So if you call this.Hide() in FormClosing event, the event will be raised again.
Imagine if it didn't close the form, your application had been blocked by a hidden modal form!
Note: The answer describes about the reason of raising the event twice. But as described here and others mentioned, For modal forms (which you showed using ShowDialog), the Dispose method will not be called and the form exists after closing and you can use its properties to get some data or you can show it again. So you don't need to call hide method.
For more information take a look at: Do I need to Dispose a Form after the Form got Closed?
The workaround you mention is unnecessary, because modal dialogs are not disposed when closed. They are designed to keep their data after being closed (since it is required by the caller of the modal dialog), and to be reused as needed.
Just let it close properly. It causes no harm to a modal dialog :)
Note that this also means that unlike normal forms, you must dispose of modal dialogs manually if they are not persistent. In your scenario, this most likely means you'd want to dispose of the dialog when the parent form is disposed (this happens automatically if you added the dialog as a component, but needs to be done manually if you create it yourself).
I recently created a simple custom message-box. Its just another form called using ShowDialog().
I have two buttons YES / NO that sets the DialogResult value then Hides the form using this.Hide().
However, when I do this, the entire application closes. This does not happen when using this.Close(). The reason why I chose using Hide is because the response appears to be faster. When using Close, the Message Box Form lingers for 2-3 seconds before closing.
Below is some code:
public static void Init()
{
if (_instance == null)
{
_instance = new MQMessageBox();
_instance.MQButtonYes.Click += MQButtonYes_Click;
_instance.MQButtonNo.Click += MQButtonNo_Click;
}
}
public static DialogResult Show(string caption, string message)
{
Init();
_instance.Caption = caption;
_instance.Message = message;
DialogResult result = _instance.ShowDialog();
return result;
}
private void MQButtonYes_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Yes;
this.Hide();
}
private void MQButtonNo_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.No;
this.Hide();
}
From the main form, its being called as:
MQMessageBox.Show("Warning", "Hello World");
this.Show(); //Adding this call, will show the main form again. Without this call, the mobile will show the Today Screen making it appear the app has crashed.
Main method is:
MQMainForm mainForm = new MQMainForm();
Application.Run(mainForm);
I know it is not what you want to hear, but calling the following code is not really valid:
private void MQButtonNo_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.No;
this.Hide();
}
The reason for this is that setting DialogResult is not the same as calling Form.Close(). I examined the implementation of Form in both the Compact Framework and the regular Framework in Reflector. Unfortunately I was not able to see the exact implementation of Form in the Compact Framework, but I was able to look into the regular implementation which should be similar.
The implementation of Close sets a flag that the Form is to be closed and then sends WM_CLOSE to the window. Setting DialogResult only sets a private variable in the Form. Now, I know what you are thinking, "but, I know that setting DialogResult closes the form! everyone knows that!". The key to understanding this behavior is how this happens. When you call Form.ShowDialog() it creates a new window message loop for the modal form. This creates a loop which processes window messages. The termination condition for this loop involves checking whether the user called Close() from the boolean that was set during close and/or whether DialogResult is set. Therefore, setting DialogResult will cause the message loop to terminate and close the Form.
From what I can tell, the problem with Hiding the Form is that you are setting DialogResult, but then when you hide the form, I believe that the Window is no longer receiving Window messages. Therefore, the message loop is probably waiting for the next message before checking the DialogResult's value.
You could experiment with this by getting a handle to the Form and sending it WM_CLOSE, but I'd imagine that going around the intended method of closing the Form to shave a few seconds of how long it takes, is probably not worth the cost of the probably unknown behavior of such a hack.
My program has multiple forms. The fifth and final form has a button that when clicked closes the application using the method Application.Exit(). However every time I click the button I receive the error 'cannot access a disposed object' surrounding this code on my first form:
frm2 f2 = new frm2();
this.Hide();
f2.ShowDialog();
this.Show();
The compiler indicates that the statement this.show() is the problem. Could someone explain why I am receiving this error and how to fix it?
Ok edited my answer, I reproduced your issue. If you want to use Form.ShowDialog then you should set the DialogResult of the control that is closing the application. So in the buttons properties you should set the dialog result to something, for example Cancel.
Then on the buttons click event you would do something like this:
private void btnClose_Click(object sender, EventArgs e)
{
if (this.DialogResult == DialogResult.Cancel)
{
Application.Exit();
}
}
Otherwise if you don't need to use Form.ShowDialog, you can just show Form2. The above does not produce the error in my testing.
In your code example, did frm2 make a call to Application.Exit? If it did, then why are you trying to call this.Show again?
Anyway, you may have a problem related to how you started the application's message loop. Are you running Application.Run(), or Application.Run(form1)?
If you provided a form to Application.Run() when you started your message loop, then you should not be calling Application.Exit in order to exit the application. Instead, you should simply close your main window, that would cause the message loop to finish, the call to Application.Run to return, and your application will terminate cleanly.
I have program that opens subwindows inside of it (mdi.parent). I have made component that is in one window under it, however, i want that that window never actually disposed after its created because i want to keep only one instance of it.
This can be made with code:
// This prevents this window disposing from window close button, we want always show one and only
// one instance of this window.
FormClosing += (o, e) =>
{
Hide();
e.Cancel = true;
};
However, after this there is problem, closing program requires pressing close button press twice. First press closes subwindow and second terminates program. How this can be get around?
I am working with Winforms.
As Habib said, you can call Application.Exit, but:
The Form.Closed and Form.Closing events are not raised when the
Application.Exit method is called to exit your application
If this is important to you, you can do something like this (MDI parent code):
private Boolean terminating;
protected override void OnClosing(CancelEventArgs e)
{
if (!terminating)
{
terminating = true;
Close();
}
base.OnClosing(e);
}
Call Application.Exit() in the form close event.
Application.Exit - MSDN
Informs all message pumps that they must terminate, and then closes
all application windows after the messages have been processed.
The code inside of your FormClosing event handler method is a bit too terse. It does its job of preventing the user from closing the form, but as you've also noticed, it prevents you from closing the form programmatically as well.
This is easily solved by testing the value of the CloseReason property of the FormClosingEventArgs that are passed in each time the event is raised.
These will tell you the reason why the form is attempting to close. If the value is CloseReason.UserClosing, then you want to set e.Cancel to true and hide the form. If the value is something else, then you want to allow the form to continue closing.
// This prevents this window disposing when its close button is clicked by the
// user; we want always show one and only one instance of this window.
// But we still want to be able to close the form programmatically.
FormClosing += (o, e) =>
{
if (e.CloseReason == CloseReason.UserClosing)
{
Hide();
e.Cancel = true;
}
};
Use This
Form[] ch = this.MdiChildren;
foreach (Form chfrm in ch)
chfrm.Close();
You can use Application.Exit if there is no processing happening when the application is closed. Otherwise, you can check Application.OpenForms collection in MDI parent's closing event and close all the other forms that are open.