1

I have been given a application to multi-thread and this has been done. All message boxes in this application are correctly called by a single static method in a utility class. This method is:

public static void ErrMsg(String strMsg, Exception ex = null)
{
    ...
    if (ex == null)
        MessageBox.Show(strMsg, "MyApp", MessageBoxButtons.OK, MessageBoxIcon.Warning);
    ...
}

Clearly this does not provide the IWin32Window owner and this is now causing me a problem when I invoke an error message from a background thread-pool thread using this method. The problem is a known one of the message box showing behind my main form. See:

  1. Popping a MessageBox for the main app with Backgroundworker in WPF

et al. I could pass in the SynchronisationContext of the current thread to ErrMsg and do

synchronizationContext.Send(callback => 
    MessageBox.Show("Some error message for the user"), null);

But there are 700+ calls to this method, the majority of which are on the UI thread and do not cause a problem. My question is: how can I amend the ErrMsg method so that my message box appears in front reguardless of the current SynchronisationContex and without having to ammend all 700+ calls to the method?

Thanks for your time.


Edit. @Dmitry's idea was great. However, if the active form does not have focus or is an MdiChild form then the Form.ActiveForm will return null. To get around this I use Application.OpenForms.Cast<Form>().Last() to get the last active form, beit MdiChild or whatever. The code is now...

Form activeForm = Application.OpenForms.Cast<Form>().Last();
if (ex == null)
{
    activeForm.Invoke(new ShowErrMsg(text => 
        MessageBox.Show(activeForm, 
            text, 
            "UserCost",
            MessageBoxButtons.OK, 
            MessageBoxIcon.Warning)), 
        strMsg);
}
Community
  • 1
  • 1
MoonKnight
  • 23,214
  • 40
  • 145
  • 277

1 Answers1

1

Try to use the Form.ActiveForm static property as a first argument for MessageBox.Show(...). Also the thread-safe invocation should be used.
Example:

private delegate void ShowErrMsgMethod(string text);

public static void ErrMsg(String strMsg, Exception ex = null)
{
    ...
    if (ex == null)
    {
        Form activeForm = Form.ActiveForm ?? Application.OpenForms.Cast<Form>().Last();
        activeForm.Invoke(new ShowErrMsgMethod(text => MessageBox.Show(activeForm, text, "MyApp", MessageBoxButtons.OK, MessageBoxIcon.Warning)), strMsg);
    }
    ...
}

EDIT: Improved for a circumstances when other forms are opened. The reference to main form was eliminated.
EDIT2: Improved for a circumstances when Form.ActiveForm == null.

Dmitry
  • 13,797
  • 6
  • 32
  • 48
  • Thanks for your reply. This looks like a decent approach and I have thought of this before; however, other forms other than the main form call this method - the question is will this cause a problem? One thing that concerns me is if the main form launches a sub-form and this form calls `ErrMsg`... Thanks again I will implement and test this tomorrow. – MoonKnight Nov 14 '13 at 17:35
  • I've updated the example for circumstances when other forms are opened. The solution is to use the **Form.ActiveForm** static property as a MessageBox owner. – Dmitry Nov 14 '13 at 18:58
  • Damn. Fair play. I will check this out tomorrow. I am impressed by the knowledge of a newcomer! Did not know `Form` had this static method. Thanks for your help... – MoonKnight Nov 14 '13 at 21:32
  • This is a great idea, however, if the top form does not have focus or is an MdiChild, `Form.ActiveForm` will return null. I have amended the code to suit. See my edited question. Feel free to copy into your answer... – MoonKnight Nov 15 '13 at 10:53
  • It makes sense to use both `Form.ActiveForm` and, if the one is null - last `Application.OpenForms`: `Form activeForm = Form.ActiveForm ?? Application.OpenForms.Cast
    ().Last();` Reason: the order of the `Application.OpenForms` array is undefined, and there is no guarantee that the last form is now active. For example, you can open `Form1` and `Form2` without setting the owner in `Show()` method, and then switch between them in UI. But the last form will always be the same - `Form2`.
    – Dmitry Nov 15 '13 at 11:19