1

I have an application that displays two forms/windows at the same time. The first one is shown with the Form.Show() method, so it is modeless and floats off to one side. The second form is shown with Form.ShowDialog(), so it is modal, and it blocks.

The fact that it blocks is important, because unlike the first form (which is basically just decoration), the second form acquires important information, so I don't want my program to continue running until it closes.

Unfortunately, I now need to allow the user to have some limited interaction with the first form (the ability to resize it, and other minor visual adjustments) while the second form is also displayed.

Obviously, that doesn't work while that second dialog is modal. So either I need to find a way to make that second form modeless yet still blocking while it is open...or else I need to make the first form somehow accessible while the second form is modally visible.

I'm an experienced Java Swing programmer, but I'm fairly new to .NET forms, so maybe there's an obvious answer here that I'm missing simply because I'm not very familiar with the .NET api?

Xanatos
  • 1,576
  • 18
  • 28

2 Answers2

5

It is possible by doing it the other way around, keeping the helper form enabled. That however requires a few tricks. The ShowDialog() method call iterates the toplevel windows in the app and calls EnableWindow() on them to disable them. You can undo this by calling EnableWindow yourself. One thing that's tricky is that you can't, ShowDialog() blocks. BeginInvoke() can fix that. Here's an example:

  public partial class Form1 : Form {
    public Form1() {
      InitializeComponent();
    }
    protected override void OnLoad(EventArgs e) {
      mHelper = new Form2();
      mHelper.Show();
      mHelper.FormClosed += (s, ea) => mHelper = null;
    }
    Form2 mHelper;

    private void EnableForm2() {
      if (mHelper.IsHandleCreated) EnableWindow(mHelper.Handle, true);
    }
    [System.Runtime.InteropServices.DllImport("user32.dll")]
    private static extern bool EnableWindow(IntPtr hWnd, bool enable);

    private void button1_Click(object sender, EventArgs e) {
      this.BeginInvoke(new Action(() => EnableForm2()));
      using (var dlg = new Form3()) {
        if (dlg.ShowDialog(this) == DialogResult.OK) {
          // etc...
        }
      }
    }
  }
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Hmmm...I found a way to do what I wanted by opening the forms on a separate thread and calling Application.Run() on those threads. But what you've suggested here gives me an interesting alternative to play around with! – Xanatos Mar 08 '10 at 16:50
  • Excellent method. Got a query, how woud you pit this approach against `Application.DoEvents` approach [found in another answer here](http://stackoverflow.com/a/8567719/661933)? I know about the hazards of DoEvents (something I can easily manage), but I am trying to know if the above approach is any better – nawfal Sep 05 '12 at 15:13
  • @nawfal have a read of [use-of-application-doevents](http://stackoverflow.com/a/5183623/495455) – Jeremy Thompson Dec 16 '13 at 01:03
  • 3
    Oh dear, 3 1/2 years ago I still indented code with 2 spaces. Time flies. – Hans Passant Dec 16 '13 at 01:08
  • @JeremyThompson yes I did. I think I kind of alluded that in my comment above. Thanks nevertheless. – nawfal Dec 16 '13 at 03:01
1

I don't believe that you can do this in the way you're asking. Without knowing anything else about the program or the forms, the best advice I have is to either modify the forms so that you can make one form modal, OR come up with some mechanism to stop the user from proceeding without completing all of the required information in the currently modal form.

I'm assuming a bit here with what I'm about to say.. If I'm wrong, I'll withdraw this, but I'm assuming you have a main form, and the two forms you're talking about are both forms that are shown when some event happens in the main form. Finally, I'm assuming that there is some process in the main form that cannot be completed properly without the required information from form A.

Assuming this is the case, and Form A is currently the modal form, and form B is the non-modal form, I would suggest doing the following:

  • In Form A, add a public boolean property to track if all required information has been filled out.
  • Change form A so it's not opening using the ShowDialog() so it's not modal. (This will allow interaction with Form B)
  • In Form A's OnClosing event, check to see if the required info has been filled out, and cancel the event if not.
  • in the main form, check the property for Information IsAllFilledIn on Form A at whatever point is appropriate. If ht's false, show Form A again (or refocus it)

Again, I'm not sure of your requirements, but you should be able to find some trick to get you around this. I offered the previous mostly to get your thoughts flowing out of the box.

All of that said, the simpler approach would be to just modify the form so that all of your fields are on the modal form.

David
  • 72,686
  • 18
  • 132
  • 173