I start with a form A.
The form A call a form B with .Show().
So form A and form B are both accessible.
Now, I wan't form B calling form C with ShowDialog(), because I don't want form B be accessible while form C is open.
The problem is : form A is inaccessible.
In this scenario, how can I have both form A and form C accessible, but not form B?
This is entirely by design, a dialog disables all windows in an application. There's a good reason for that, outlined in this answer.
In brief, the Drastic Failure scenario is that a user could use a command in form A that creates another instance of form B, one that isn't disabled. Which allows you to display C again, now you have two dialogs showing.
You may well sputter that this can never happen in your case. That's okay but you'll then have to undo what ShowDialog() does. Which requires gymnastics, you have to pinvoke EnableWindow() to re-enable the form A instance. But you have to do so after the ShowDialog() call but before it ends. Something you can do by using Control.BeginInvoke() on the UI thread. The trickorama looks like this:
private void ShowDialogButton_Click(object sender, EventArgs e) {
using (var dlg = new Form3()) {
// Find the main form instance
var main = Application.OpenForms.OfType<Form1>().Single();
this.BeginInvoke(new Action(() => {
EnableWindow(main.Handle, true);
}));
if (dlg.ShowDialog(this) == DialogResult.OK) {
// etc..
}
}
}
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern bool EnableWindow(IntPtr handle, bool enable);
You can also put the EnableWindow() call in the dialog's Shown event handler, that avoids having to use the BeginInvoke trick. But this way is more universal, dialogs should in general not be in the business of knowing what other windows run in an app. The use of Form1 in this code is ugly enough as-is.
Modal windows are such that they require users input and dismissal before allowing her to return to other part of applications. As such, you can't use them if you want to enable user to interact with more than one window.
You will have to manually handle this situation by disabling window B (setting Enabled to false) before showing window C, and enabling it back again once window C is dismissed.
Related
In my .NET application I am showing a form with a "Please wait" message if some task takes a while to tell the user that the program still works. This form has to run in it's own thread since the main thread is the one doing work and therefore busy. I read that one can use Form.Owner for this task, but I don't think that works if the forms run on different threads.
The problem is that the wait form can be hidden behind the main form in which case the user couldn't see it and can't bring it to the front, since it has no task bar button (an it musn't have one).
My question is if it is possible to let the wait form stay above the main form without making it an AlwaysOnTop form (which would stay above ALL windows)?
Your main thread should not be doing work. It should be handling UI and nothing else.
The right way to do this is to perform any and all time-consuming work in an asynchronous manner (e.g. in a separate thread), and keeping all of your user interface in the main thread. Then you can simply show the "Please wait" message form by calling the Form.ShowDialog() method. This will force focus to that dialog and keep it on top of its parent form (don't forget to pass the parent form reference to the ShowDialog() method).
Without a code example, I can't say exactly how this would look in your specific scenario. But the general idea looks something like this:
private void button1_Click(object sender, EventArgs e)
{
using (Form form = MyWaitMessageForm())
{
form.Shown += async (sender1, e1) =>
{
await Task.Run(() => MyLongRunningWork());
form.Close();
}
form.ShowDialog(this);
}
}
You can use the Form.TopMost property for this.
You can also use the following code:
protected void SetZOrder(IntPtr bottom, IntPtr top) {
const int flags = 0x0002 | 0x0001;
NativeMethods.SetWindowPos(bottom, top, 0, 0, 0, 0, flags);
}
bottom - the pointer of the main form, top - the pointer of the wait form. To get a pointer, use Form.Handle property. And call the SetZOrder via the BeginInvoke method of the parent form.
You could use the Form.ShowDialog(IWin32Window)
Form1 testDialog = new Form1();
testDialog.ShowDialog(this)
i got application, it shows 3 forms: log window, status window, and option window, option window calls some other forms and some of these forms are required to be called using ShowDialog() to return dialog result value for further decision making.
Using ShowDialog() raises problem, cause form called by that method excluding other forms from being accessible.
So my problem is i would like to be able to make atleast log window accessible no matter how much other forms has been called. Is there a way to make log window to independent form other forms, or could it be taken over by form called as last?
EDIT:
I failed to mention that the behaviour provided by ShowDialog() is quite usefull in my app and that i only lack ability to free that one or two forms from being locked. Switching to Show() is not option I'm considering as best while other forms, that are parent to form called by ShowDialog() are required to be still locked.
You'll have to make a choice between the two.
Use ShowDialog(), so that your parent form pauses execution, and only resumes when the second form is closed, or
Use Show(), so that your parent form continues execution after displaying the second form.
If you want to take some action, or read values from the second form when it's closed, then subscribe to its Closed event before you show it.
Let's assume your second form has a "First Name" TextBox, and a property to return that value:
public string FirstName
{
get { return yourFirstNameTextBox.Text; }
}
In your first form, you can subscribe to an event to take some action when the event occurs, like this:
var f2 = new SecondForm();
f2.Closed += (s, e) => MessageBox.Show(f2.FirstName);
f2.Show();
Now the user can continue on their way with both forms, and when the second form is closed, a message box will display the value of the "First Name" TextBox.
You'll probably want to do something more meaningful than this. Instead of displaying a message box, you could update a field on the first form, or take some any other action or set of actions that you want.
f2.Closed += (s, e) =>
{
MessageBox.Show(f2.FirstName);
nameLabel.Text = f2.FirstName;
// another action
// yet another action
};
So I now modeled your situation and found solution:
Use Show() instead ShowDialog() for dialog.
Write some code in the dialog to close it window after press the button (Ok, Cancel etc.). Because auto-closing works with ShowDialog only. But you don't need to set DialogResult manually.
At the Options form subscribe to FormClosed event to get DialogResult. For example:
dlg.FormClosed += (o, a) => { this.Text = dlg.DialogResult.ToString(); };
Handle Activate event of Options form to prevent focusing on parent window when dialog is opened:
if (dlg != null) dlg.Focus();
This solution has some difference with system behaviour of ShowDialog, but it works good.
I have two form classes (Form1 and Form2) in a Winforms App.
Form1 is like this:
Form2 is like this (ShowInTaskbar = false):
And this code on Form1:
Form2 someForm = new Form2();
private void btOpenAnotherWindow_Click(object sender, EventArgs e)
{
if (someForm.ShowDialog(this) == DialogResult.OK)
MessageBox.Show("OK!!!");
else
MessageBox.Show("Not OK.");
}
That is, a window with a button which opens modally another windows when clicked, and waits for the user to close the second window (by clicking the "OK" or "Cancel" button). And depending how it was closed, do alternating actions (represented here by the MessageBox.Show() calls).
I need:
The user can use only one window at a time. (Modal forms, That's why I used ShowDialog() instead of Show())
When the form closes, do something depending on how the form was closed (the "if (someForm.ShowDialog(this)...")
To be able (as a user) to minimize the WHOLE APP.
To be able to "unminimize" the app to the former state correctly.
The program to respond to WIN+M (minimize all) keys combination.
the above example fails in two ways:
(need 5) Doesn't respond to WIN+M
(need 3) The app seems to minimize when the Minimize title bar button is clicked, but it is an illusion because the main form (Form1) does not minimize and it is in fact just hidden behind the other opened windows. Only running the example with an empty desktop shows what really happens. Pics follow:
Before Minimize button is clicked:
After:
Note:
The Main form is not minimized
The Form2 is in the left botton corner of the screen.
Form2 is a full blown window (not a dialog window per se) and I need the user to interact with it only until it is closed and I also need the user to be able to miminize the whole app in case he needs it.
It is a shame I can't post here the real forms, It would be clearer than these mock-ups.
I need a solution that works with many levels of modal windows (not only two as this example shows). Any suggestions?
I may need a little more information about what you're trying to do here. I have a simple form (Form1) with a button on it, which calls this code:
private void button1_Click(object sender, EventArgs e)
{
Form1 form2 = new Form1();
form2.ShowDialog();
}
When I click the button, I get a second instance of the same form, but it's modal. I still have the option to minimize this second modal form (I obviously can't interact with the first form), and when I do minimize the second form it does minimize the entire application (both forms). Now obviously you're asking the question, so I don't think I'm understanding you. =) What about this scenario do you wish to change?
C
There is probably some way to hack this functionality using API calls, but I would probably suggest doing some type of overlay with a control inside your main form rather than an actual window. This would allow you to make it "modal" and still have the ability to minimize/resize the main window.
In my application I want to show a login form first and then the main form if the login has been successful. Currently I'm doing it something like this:
var A = new LoginForm();
if ( A.ShowDialog() == DialogResult.OK )
Application.Run(new MainForm());
But then I started wondering - what's the point of the Application.Run()? Why not just do (new MainForm()).ShowDialog() as well? What's the difference? And what would be the correct way to achieve what I want?
Application.Run(Form) starts a message loop on the current thread and displays the specified form. The message loop enables the form to receive Windows messages (eg, key presses, mouse clicks, paint invalidations) to allow it to appear responsive and have interaction with the user. When you call ShowDialog() on a Form instance, it actually does a similar thing and creates a modal message loop for the form on which ShowDialog has been called.
There is not much difference between the two calls. Application.Run does add some extra event handling enabling you to do some tidying up of resources when the main form is closed (see Application.ThreadExit).
The recommended way to start WinForms applications is using Application.Run, but I suspect this is more of a convention than a rule. The biggest reason to use Application.Run is if you want to open multiple non-modal forms. You can do this using:
new Form().Show();
new Form().Show();
Application.Run();
You could not achieve this using the ShowDialog() method as one of the forms would have to be modal.
As for your question of how to show a login form and then the main form if the login is successful, I think what you have is fine:
if (new LoginForm().ShowDialog() == DialogResult.OK)
{
Application.Run(new MainForm());
}
The alternative is to do the plumbing yourself and open an instance of MainForm in the closing event of the LoginForm if the login was successful.
From MSDN:
This method adds an event handler to
the mainForm parameter for the Closed
event. The event handler calls
ExitThread to clean up the
application.
http://msdn.microsoft.com/en-us/library/ms157902.aspx
From my testing, I noticed this main difference:
When Application.Run is used, the form's Close button (red X) returns DialogResult.None; however, when ShowDialog is used, the Close button produces DialogResult.Cancel.
Does this matter to you? In my code, I was testing for DialogResult.Cancel to determine the exit code of my application. That was broken when the red X was used to close the form. I now test for DialogResult.OK to indicate a successful exit.
return myForm.DialogResult == DialogResult.OK ? 0 : 1;
One key difference is that ShowDialog is usually a modal Dialog. If you wanted to create a user-friendly toolset, you would not want it to be comprised of modal dialog boxes.
Also, Application.Run() accepts more than just a form. It has a few overloads.
As for your application, I do not think it matters much. Application.Run makes sense to me because it denotes the start of your actual Application.
The documentation of the overload
public static void Run(
ApplicationContext context );
has a neat example with a different approach that involves two forms as well.
For a more concerete example of a difference:
If your main form is an MDI form, then the behavior on clicking the close button (the 'x' in the upper right, or Alt-F4) is different depending on which method you use to show the form.
With Application.Run(mainForm), the closing event of the child forms run, then the main form's closing event runs.
With mainForm.ShowDialog, the closing event of the main form runs, and the closing event of the child forms do not run.
Application.Run() is for the start of application while MainForm is part of the application and MainForm()).ShowDialog() used to display it only.
Application.Run() is the entry point for your Application. same as Main() method is for some class or ApplicationStart() for a WebApplication
Application.Run() has different overloads, one of which is without parameters. That Method starts application without an initial form.
From my testing I notice that using Application.Run buttons with DialogResult does not close the form (OnFormClosing is not hit) compare to ShowDialog in which the buttons with DialogResult hit OnFormClosing and the close the form.
I have a C# dll that when the main method is called, it opens a windows form. This dll is currently accessed through VBA by a vendor application. The problem is that when the dll is called and the form opens, the user loses the ability to interact with the vendor application. I was hoping that if I could modify the dll to support an asynchronus call, then the control could be returned to the calling application to allow the user to turn pages, zoom in or out, etc. Then once the user completes the pop-up form we could have a callback or something to return the information to the vba app customizations.
Rather than making the async call from VB, it would be far easier to modify the C# code to open the window asynchronously, and then return.
You could do this just by changing the line that opens the window from .ShowDialog() to .Show().
Obviously, this may not be an option if you don't have access to the C# dll's code, but I thought I would suggest it.
If you do decide to change to use Show() instead of ShowDialog() you may have to handle your dialog result differently.
You're new code will look something like this:
MyForm win = new MyForm();
win.Show();
A non modal dialog doesn't block the calling code, so your calling code will continue without waiting for a result.
The simplest way to handle this would be to attach an event handler to the form's .OnClosed event. This will then be triggered when the user closes the form.
MyForm win = new MyForm();
win.OnClosed += new EventHandler<FormClosedEventArgs>(YourEventHandlerMethod)
win.Show();
You can add a public boolean property to the form (with a private setter) that you set internally when the ok/cancel buttons are pressed (Along with calling the close method). You can then check this property after the form has closed from within your on closed handler. (The sender property will be your reference to the form, you'll just have to cast it to the correct type first).
So your event handler method will look like this:
private void EventHandler<FormClosedEventArgs> YourEventHandlerMethod(Object sender, FormClosedEventArgs e)
{
MyForm f = (MyForm)sender;
bool result = f.TheResultProperty;
// Now do your callback.
}
Finally, now you have some code that runs after the form closes, and has access to the forms result, you just need to let your VBA code know it's ready - I'll leave this up to you.