7

I'm using a MessageBox from time to time to pop up alert messages for the user. My particular application can be setup to run remotely so there are times where the user is in front of the computer and other times where the user may not be in front of the computer for hours or even days. Sometimes I popup an alert message using MessageBox but after some period of the time the alert is no longer relevant. For example, I popup an alert that a task can't be completed because of some criteria not being met. A few minutes later that criteria is met and the task begins. That MessageBox is no longer relevant.

I want to be able to programmatically close the MessageBox in these cases where the message is no longer relevant. Is this possible? Currently I create my MessageBox objects in a thread using:

new Thread(() => MessageBox.Show("Some text", "Some caption")).Start();

I do this so that the application can continue to work in the background without being halted by the MessageBox. Any suggestions?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Michael Mankus
  • 4,628
  • 9
  • 37
  • 63
  • 1
    maybe some custom message box is what you need. – King King Oct 28 '13 at 13:15
  • I'm leaning that way. Just curious if there is a way to do this with the existing `MessageBox`. I hate reinventing the wheel. – Michael Mankus Oct 28 '13 at 13:22
  • In fact that's not really the so-called `reinventing the wheel`, if you also want some custom look, that's the only way, it's also not really much work. I don't think there is any way to start from the standard `MessageBox`, you can also use some `Win32` function to create some *modeless* MessageBox although I've never tried it. – King King Oct 28 '13 at 13:27
  • 1
    BTW, I guess your message box would have only 1 button `OK`, so the interactivity with user is not really necessary, you can always try using some kind of `notification window` instead. – King King Oct 28 '13 at 13:30

6 Answers6

6

This worked for me

public partial class Form1 : Form
{
    [DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
    static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);

    [DllImport("user32.Dll")]
    static extern int PostMessage(IntPtr hWnd, UInt32 msg, int wParam, int lParam);

    const UInt32 WM_CLOSE = 0x0010;

    Thread thread;

    public Form1()
    {
        InitializeComponent();

        thread = new Thread(ShowMessageBox);
        thread.Start();
    }

    void CloseMessageBox()
    {
        IntPtr hWnd = FindWindowByCaption(IntPtr.Zero, "Caption");
        if (hWnd != IntPtr.Zero)
            PostMessage(hWnd, WM_CLOSE, 0, 0);

        if (thread.IsAlive)
            thread.Abort();
    }

    static void ShowMessageBox()
    {
        MessageBox.Show("Message", "Caption");
    }
}

Now you can use CloseMessageBox() to close the message box.

But have in mind, the captions must be the same in CloseMessageBox() and ShowMessageBox()!

Maybe through a global variable but that's up to you.

Andy
  • 3,997
  • 2
  • 19
  • 39
  • In my tests (Windows 7) it works with MessageBox.Show(this, "Abort?", AlertCaption, MessageBoxButtons.OKCancel) but not with MessageBox.Show(this, "Abort?", AlertCaption, MessageBoxButtons.YesNo). Nothing happens with the latter (the alert remains open). Any idea? – Alex Apr 21 '16 at 16:35
  • I noticed that hitting Esc doesn't work in YesNo dialogs (and therefore WM_CLOSE message does nothing). Any idea what to send to the window to make it respond as if No was pressed? – Alex Apr 21 '16 at 16:49
  • Well, it looks very complicated and someone mentioned that it wouldn't work on non-English Windows. I think I'll have to refactor my UI so that it would utilize OK/Cancel rather than Yes/No. – Alex Apr 22 '16 at 18:23
  • Andy is there a C++ method to move the MessageBox to the specified location? – Yoda Jun 08 '16 at 18:16
2

Why not make a custom message box? You could have it display for a fixed amount of time or until your app closes it through code.

Create an instance of your custom message box (child of Form class) and save it as a variable (ex. MyMessageBox), then show it with MyMessageBox.Show();.When you want to take it down, call MyMessageBox.Close();

If you have problems closing it if you opened it in another thread, try calling MyMessageBox.Invoke(new Action(() => {MyMessageBox.Close();})); That will run the command MyMessageBox.Close(); on the same thread MyMessageBox was created in, as to not cause issues.

Phoenix Logan
  • 1,238
  • 3
  • 19
  • 31
2

you can use below class to create a MessageBox easily:

using System;
using System.Runtime.InteropServices;

public class MsgBox
{
    [DllImport("user32.dll", EntryPoint = "FindWindow", CharSet = CharSet.Auto)]
    static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

    [DllImport("user32.dll")]
    static extern bool EndDialog(IntPtr hDlg, int nResult);

    [DllImport("user32.dll")]
    static extern int MessageBoxTimeout(IntPtr hwnd, string txt, string caption,
        int wtype, int wlange, int dwtimeout);

    const int WM_CLOSE = 0x10;

    public static int Show(string text, string caption, int milliseconds, MsgBoxStyle style)
    {
        return MessageBoxTimeout(IntPtr.Zero, text, caption, (int)style, 0, milliseconds);
    }

    public static int Show(string text, string caption, int milliseconds, int style)
    {
        return MessageBoxTimeout(IntPtr.Zero, text, caption, style, 0, milliseconds);
    }
    public static int Show(string text, string caption, int milliseconds)
    {
        return MessageBoxTimeout(IntPtr.Zero, text, caption, 0, 0, milliseconds);
    }
}

public enum MsgBoxStyle
{
    OK = 0, OKCancel = 1, AbortRetryIgnore = 2, YesNoCancel = 3, YesNo = 4,
    RetryCancel = 5, CancelRetryContinue = 6,

    RedCritical_OK = 16, RedCritical_OKCancel = 17, RedCritical_AbortRetryIgnore = 18,
    RedCritical_YesNoCancel = 19, RedCritical_YesNo = 20,
    RedCritical_RetryCancel = 21, RedCritical_CancelRetryContinue = 22,

    BlueQuestion_OK = 32, BlueQuestion_OKCancel = 33, BlueQuestion_AbortRetryIgnore = 34,
    BlueQuestion_YesNoCancel = 35, BlueQuestion_YesNo = 36,
    BlueQuestion_RetryCancel = 37, BlueQuestion_CancelRetryContinue = 38,

    YellowAlert_OK = 48, YellowAlert_OKCancel = 49, YellowAlert_AbortRetryIgnore = 50,
    YellowAlert_YesNoCancel = 51, YellowAlert_YesNo = 52,
    YellowAlert_RetryCancel = 53, YellowAlert_CancelRetryContinue = 54,

    BlueInfo_OK = 64, BlueInfo_OKCancel = 65, BlueInfo_AbortRetryIgnore = 66,
    BlueInfo_YesNoCancel = 67, BlueInfo_YesNo = 68,
    BlueInfo_RetryCancel = 69, BlueInfo_CancelRetryContinue = 70,
}

Usage:

MsgBox.Show("this is content", "this is caption", 3000);
LoliMay
  • 21
  • 1
  • 1
0

Make an exception for your criteria to know when to start msgbox or no. Example:

if (criteria)
{
    new Thread(() => MessageBox.Show("Some text", "Some caption")).Start();
}
else
{
    //do nothing
}
Andy
  • 3,997
  • 2
  • 19
  • 39
xeLin xel
  • 49
  • 1
  • 3
  • 8
  • 3
    But as he stated it is possible that the cirteria is not met at the current time, but later it will be, so the Message will not be relevant but will be still on. And the question is how to **Remove** a MessageBox not how to **Start** it. – Tafari Oct 28 '13 at 13:19
  • I doubt if there was any downvoter jumping in here, this answer would receive at least 2 downvotes. – King King Oct 28 '13 at 13:20
0

You might want to consider a LogFile for your messages, along with a richtextbox(or multiline Textbox) embeded in your main form, you could then post your messages in there(1 per line, along with a timestamp). As for your messagebox problem, im not sure there is a (nice) way to programmatically close them.(Aborting the thread won't work).

Samuel
  • 710
  • 5
  • 8
0

if you use DevExpress, then you can do following: Application have property OpenForms which contains all open forms of application. You steel can`t find specific messagebox but you can close all XtraMessageBox. Or if you run MessageBox in some Task/Thread check it before closing.

no.Oby
  • 107
  • 8