How to make a modeless form “block”?

2019-06-01 19:32发布

问题:

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?

回答1:

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


回答2:

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.