views:

33

answers:

2

I have a custom windows modal form with the following event code:

private void btnCancel_Click(object sender, EventArgs e)
{
  Close();
  _result = DialogResult.OK;
}

The problem is when I click OK, that triggers some process-intensive stuff (report generation) and the form becomes partially drawn on the screen. It's like the report generation is taking precedence over the window refresh. Is there something else I need to do in order to get it to disappear before the process-intensive code? This will most definitely annoy my users.

EDIT #1:

I'm trying to work Tergiver's method and pass in the dialog owner to ShowDialog. For my case, the calling method is not a form. So, I'm trying to create a IWin32Owner from the process's main window handle so that I can pass it into the ShowDialog method.

public class WindowWrapper : System.Windows.Forms.IWin32Window
{
    public WindowWrapper(IntPtr handle)
    {
        _hwnd = handle;
    }

    public IntPtr Handle
    {
        get { return _hwnd; }
    }

    private IntPtr _hwnd;
}

// In calling method
ShowDialog(new WindowWrapper(System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle));

However, the dialog owner is still not set after the call to ShowDialog. I stepped into the WindowWrapper and the handle is non-zero. Any more ideas as to how to get the current process's active form?

EDIT #2 I'm now using the following code to retrieve the active form and then calling Owner.Refresh() in the OnFormClosed event.

public static Form GetActiveForm()
{
  // Returns null for an MDI app
  Form activeForm = Form.ActiveForm;
  if (activeForm == null)
  {
    FormCollection openForms = Application.OpenForms;
    for (int i= 0; i < openForms.Count && activeForm == null; ++i)
    {
      Form openForm = openForms[i];
      if (openForm.IsMdiContainer)
      {
        activeForm = openForm.ActiveMdiChild;
      }
    }
  }    
  return activeForm;
}

// In code opening dialog.
ShowDialog(GetActiveForm());
+3  A: 

The obvious answer is not to do process-intensive code on the UI thread.

Use a BackgroundWorker or the ThreadPool to do the task.

Added

If you insist on doing it on the UI thread, you could use this.Owner.BeginInvoke to execute code after this closes.

Tergiver
Owner is null. Is this something I have to explicitly set?
bsh152s
Yes. You should never launch a modal dialog without an owner. The only exception (there are always exceptions) would be a modal dialog launched prior to the application's main window, but those are very rare. var Form form = new MyDialogForm(); form.ShowDialog(this);
Tergiver
Forgive me for my ignorance, but the code calling ShowDialog is not a form. I did a little research and am trying to create a IWin32Window (for ShowDialog call) from the handle returned from System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle. See edit of original question.
bsh152s
In order to call BeginInvoke, you need a Form object. Any Form will do, so long as it will still be running when the message queue processes the BeginInvoke message. BeginInvoke uses PostMessage to run code on the UI thread at some point later in time--when the message is retrieved from the queue. Are there no Forms open after this dialog closes?
Tergiver
Another option is to hide the form, perform the task, then close it. In any case, performing long operations on the UI thread is taboo because the user is left wondering why the application is non-responsive. You could build an intermediate dialog that is initially hiddden. This dialog can launch the one in question and then perform the long-running task with a BackgroundWorker, meanwhile it can show a progress bar, or at the very least a message saying "Please wait...".
Tergiver
I agree to the BackgroundWorker option. However, it's not my say. The powers above want a cheap band-aid, not a pure fix. Anyway, I've figured out how to get the active form (in EDIT #2)
bsh152s
A: 

Screw Backgroungthread! Dirty but easy (and minimal refactoring which in your case, rearranging to use multithreading may be impossible or a huge pain):

Sorry for the VB, easy translation... This would be the code calling the dialog in form the part the is 'not in the form:'

Sub showDialog()
    dim frm as new MyModalDlg
    if frm.ShowDialog = DialogResult.OK then
        DisableAnyFunctionalityOnAnyVisibleWindows()
        for i as integer = 1 to 100
             Application.DoEvents
        next i
        DoProcessIntensiveStuff()
        EnableAnyFunctionalityOnAnyVisibleWindows()
     end if
end sub

the DoEvents gives any visible windows (which you're not calling the dialog from, I know) in your app the chance to repaint. Another thing you could do is call the .Refresh properties of these windows before you do the doevents loop. Get to the windows by creating a public static variable in them of their own type and setting it to 'this' in the constructor. Then do something like frmMyMainWindow.MyStaticInstance.Refresh. (A sin, I know, but there are plenty of priests left who respect the seal of the confessional).

the Enable/Disable subs MUST find any windows that can take user input and temporarily turn them off (using the public static >:-). Otherwise the user can click on them and the events happen DURING your for loop due to cooperative multitasking within your app. BAD THING!

Happy hacking, Peace and good luck! Anybody downvoting me for Kludging is a poopyhead!!!

PS. I think it's kind of cute that cmdCANCEL_click returns dlgresult.ok!!! ;-)

FastAl