NET CF - Application Closes when hiding a modal form - c#

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.

Related

Windows forms Form closed event not working with opening a new form

I have some code meant to open a new windows form when one is closed, and yet I get nothing, no error.
I've tried a few different methods for opening a new form on a Form.FormClosed event.
This is the code I have right now:
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
Form1 myForm = new Form1();
myForm.Show();
}
But yet I get no error, nothing.
I'm expecting for a new windows form to be opened when I close another one.
Any help would be appreciated, thanks!
The problem is that as soon as the first Form1 instance closes, your application shuts down and exits because the application message loop is defined with the initial Form instance, and it is just waiting for events on that form until it closes. On closing, the application will exit, opening a new form doesn't stop this process.
You need to adjust the Main() method in Program.cs to look something like this:
[STAThread]
static void Main()
{
// ... Application configuration as required here
new Form1().Show(); // The first form instance is now no longer bound to the Application message loop. Start it before we begin the run loop
Application.Run(); // Don't pass in Form1
}
Your original code should now work. I might add however, this is not a great user experience. Carefully consider what you're trying to achieve, and perhaps consider alternatives - do you just need a "reset form" button? Or is the primary goal to prevent a user from closing the application? If the latter, you can remove the Close icon altogether.
Perhaps something simple to get your going forward.
private Form1 myForm = new Form1(); //Declare the form as a private member variable
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
e.Cancel = true; //Cancel the closing so this object stays alive
this.Visible = false; //Hide this form
myForm.Show(); //Show the next form
}
Please note #pcdevs comment. You'll need a way to indicate the form is being closed/application quiting vs progressing to the next step/form. You might want to look at some CodeProject articles about "C# Winform Wizards", those sequential dialog prompt apps...

Visual Studio c# DialogResult doesn't seem to work

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).

How to force close button to terminate all childforms?

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.

Minimize form to system tray

I want to hide my form while keeping my application running in background.
I've used notifyIcon and it remains always visible.
I've used "this.Hide();" to hide my form but unfortunately my application gets close (no exception).
I am also using threading and this form is on second thread.
Please tell me how can I solve it.
I am also using threading and this form is on second thread.
My crystal ball says that you've used ShowDialog() to show the form. Yes, calling Hide() on a modal dialog will close it. Necessarily so, a modal dialog normally disables all of the windows in the application. If you hide it then there's no way for the user to get back to the program, there are no windows left to activate. That this form runs on another thread otherwise doesn't factor into the behavior.
You'll need to call Application.Run(new SomeForm()) to avoid this. Now it isn't modal and you can hide it without trouble. But really, do avoid showing forms on non-UI threads. There's no reason for it, your main thread is already quite capable.
add the following event handlers for form resize and notify icon click event
private void Form_Resize(object sender, EventArgs e)
{
if (WindowState == FormWindowState.Minimized)
{
this.Hide();
}
}
private void notifyIcon_Click(object sender, EventArgs e)
{
this.Show();
this.WindowState = FormWindowState.Normal;
}
but this is not close you application

How Can I open a Winform Dialog when closing another?

I have a Dialog with showing some information about an object. I want to close this Dialog to show the same dialog but now with the object's sibling. It is a complex dialog that loads different components depending on the object assigned, I cant just change the reference to another object
I tried launching the new one in the Closed event, but the former hasn't disapear from the screen and keeps showing. Also tried a static method that is called whithin the Dialog, passing the same Dialog as a parameter, so it close the dialog 'dialog.Close()' and opens a new one with the new object to show. But still the former one keeps opened behind.
Is there a way to accomplish that, closing the first window and opening the second?
( THIS IS The Static approach, the window passed by parameter doesn't close until the new one created is closed)
// From the Dialog try to launch the second one closing this.
private void btnSibling_Click(object sender, EventArgs e)
{
SwitchToSibling(this);
}
private static void SwitchToSibling(SiblingDialog window)
{
try
{
double id = 0;
id = window.SelectedSibling();
if (id != 0)
{
// Get's the same Parent so to the new Dialog
Control owner = window.Owner;
window.Close();
Sibling sibling= Sibling.Get(id);
SiblingDialog.ShowSibling(sibling, false, owner);
}
}
catch (GroupException ex)
{
MessageBox.Show(ex.Message);
}
}
I had the same problem, and after a lot of troubleshooting I managed to solve it in an entirely different way: I first wait for the Forms.Application.LeaveThreadModal event to occur, and then I wait for the next Forms.Application.Idle event to occur. By that time the first dialog has completely disappeared, so it is possible to launch the second dialog without any problems.
Waiting only for the idle event, or only for leave-thread-modal event, or for both events in the opposite order, will not work.
CAUTION: Waiting only for the idle event APPEARS to work, but only for as long as the activation of the second dialog is done by clicking a button of the first dialog with the mouse. If you trigger the button by pressing the mnemonic key of that button on the keyboard, the second dialog appears above the first dialog! You have to go through the sequence I described in order to avoid this!
After thinking about it a bit more, it seems to me that my solution is kind of "magic", which means that it may stop working in a future release of the dotnet framework, so I think I will ditch it and follow the advice of Mike Hoffer, which, as far as I can tell, is essentially the same as the answer that jmayor gave for himself and marked as accepted. (The truth is that Mike Hoffer's answer is a bit hard to follow.)
Without knowing the specifics inside the Sibling and SiblingDialog classes, sure you can. The only constraint would be that if you close the window that is the application's main window, the application will exit.
You could, for instance, provide a method like so:
private static void CloseAndShow(Form formToClose, Form formToShow)
{
formToClose.Close();
formToShow.Show();
}
That would close formToClose and show formToShow.
If you've set the owner for the first dialog, and you know its type, you should be able to solve this problem as follows:
Write a custom event on the dialog's
owner.
When the user clicks the close
button, invoke the event.
Do not
close the dialog from the button.
Have the owning window close the
dialog when the event handler is
invoked. Then have the owning dialog
show the sibling dialog.
An extension of Fredrik Mork's answer, that addresses the fact the closing form may be the form controlling the exit status of the program.
private static void CloseAndShow(Form formToClose, Form formToShow)
{
Application.Run(formToShow);
formToClose.Close();
formToShow.Show();
}
Application.Run tells the program to exit when the form passed to it closes
As it seems that you are setting the owner of the dialog, you can try to use owner.BeginInvoke() in the FormClosed() event handler in MyForm class. :
public partial class MyForm : Form
{
static int count = 0;
public MyForm()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
this.Close();
}
public static void ShowMyDialog(MyForm form, IWin32Window owner)
{
count++;
form.Text = "My ID: " + count;
form.ShowDialog(owner);
}
delegate void MyDel(MyForm form, IWin32Window owner);
private void MyForm_FormClosed(object sender, FormClosedEventArgs e)
{
MyDel del = ShowMyDialog;
MyForm mySecondForm = new MyForm();
this.Owner.BeginInvoke(del, mySecondForm, this.Owner);
}
}
The only way I Could find a way around is by calling my dialog from a static method( using sort of a singleton Pattern) , then using a static variable to flag when the Dialog needs to be reopened. So when a dialog execution is ended inside the static method it checks if it needs to be reopened.
Otherwise I don't see a possible way.

Categories