1

I am coding Windows Forms in C# under Visual Studio 2012 and I would like to open multiple instances of MessageBox and automatically close them after several seconds.

I have found (and upvoted) this answer here: SO: Close a MessageBox after several seconds.

However, this works if I only open 1 (one) MessageBox at a time, since it makes use of the function FindWindow, and the multiple instances of my MessageBox shall have all the same window title (caption).

[Optionally] In addition, I would like to present the user with a countdown, like This dialog will close in 5 seconds, This [...] in 4 seconds, This [...] in 3 seconds, ..., This [...] in 1 seconds and then finally close the MessageBox.

Is there a way to uniquely reference my multiple MessageBoxes and automatically close them (either using System.Timers.Timer or System.Threading.Timer or System.Windows.Forms.Timer - whichever is the best fit for this solution) after a certain period of time (say 5 seconds)?

Community
  • 1
  • 1
Sebastian
  • 471
  • 1
  • 10
  • 26

3 Answers3

4

I suggest not using a MessageBox for this task. Instead, make your own custom form. Make it the size, shape, and look you want. Then, in its code-behind file, you can create a timer, unique to that window itself. That way, you can spawn as many of them as you want, and they will manage their own timers and closing themselves, and you don't have to do anything like finding the window. It's possible to make a Form look very much like a MessageBox. And because you can call ShowDialog, you can make them behave similarly to MessageBoxes as well (though that would be somewhat counterproductive, because you can only interact with one dialog at a time).

Curtis Rutland
  • 776
  • 4
  • 12
  • Hi @Curtis Rutland thank you for your thoughts and the idea of simulating a MessageBox by using a custom form. I will definitly explore this idea further! – Sebastian Feb 04 '14 at 15:26
  • 1
    I've accepted this answer because it is really hassle free to implement (with the counter and also with my optional requirement to display / count down the time in seconds). Thanks again, @Curtis Rutland! – Sebastian Feb 04 '14 at 20:40
  • 1
    Glad to help. I find that more often than not, a second perspective gives you simple solutions you otherwise wouldn't have thought of, especially when you're going down the rabbit hole in a different direction. – Curtis Rutland Feb 04 '14 at 22:06
1

There is an undocumented MessageBoxTimeout function in windows that you could use: MessageBoxTimeout in user32.dll (use via PInvoke).

Example:

public class MessageBoxWithTimeout
{
  [DllImport("user32.dll", SetLastError = true)]
  [return: MarshalAs(UnmanagedType.U4)]
  private static extern uint MessageBoxTimeout(IntPtr hwnd,
    [MarshalAs(UnmanagedType.LPTStr)]  String text,
    [MarshalAs(UnmanagedType.LPTStr)] String title,
    [MarshalAs(UnmanagedType.U4)] uint type, 
    Int16 wLanguageId, 
    Int32 milliseconds);

  public static uint Show(IntPtr hWnd, string message, string caption, uint messageBoxOptions,Int32 timeOutMilliSeconds)
  {
     return MessageBoxTimeout(hWnd, message, caption, messageBoxOptions, 0, timeOutMilliSeconds);
  }
}

In your code:

MessageBoxWithTimeout.Show( your parameters here );

However, you should think about your design. A message box by definition is blocking your dialog, so multiple messageboxes do not make sense. If you post your actual problem, maybe there is a better solution.

nvoigt
  • 75,013
  • 26
  • 93
  • 142
  • Hi @nvoigt, thank you for you answer and code example. My applications has two modes of operation: 1) user actively makes input to a form, and 2) applications runs automatically (watches a folder for changes). In both modes, I need to show a dialog (MessageBox). When the app is in mode 1), the user needs to confirm to carry on. When in mode 2), the dialog should close in 5 seconds automatically. Hence, I am happy with a MessageBox blocking my dialog (this is wanted), all other tasks are run in the background using Threads... I will play with your code snippet for sure (: – Sebastian Feb 04 '14 at 15:23
1

The following code can be used a as a starting point. It's based on the related answer I gave recently.

async void MainForm_Load(object sender, EventArgs e)
{
    Func<Func<Form>, Task<Form>> showAsync = (createForm) =>
    {
        var tcs = new TaskCompletionSource<Form>();
        var form = createForm();
        form.Tag = Task.Factory.StartNew(
            () => form.ShowDialog(), 
            CancellationToken.None, 
            TaskCreationOptions.None,
            TaskScheduler.FromCurrentSynchronizationContext());
        form.Load += (sIgnore, eIgnore) =>
            tcs.TrySetResult(form);
        return tcs.Task;
    };

    var forms = new Stack<Form>();
    for (var i = 0; i < 4; i++)
        forms.Push(await showAsync((() =>
            new Form { Text = "Hello #" + i })));

    var closeFormTasks = forms.Select((form) => (Task)form.Tag);

    var delay = Task.Delay(5000);
    var task = await Task.WhenAny(delay, Task.WhenAll(closeFormTasks));

    if (task == delay)
    {
        while (forms.Any())
        {
            var form = forms.Pop();
            form.Close();
            await (Task)form.Tag;
        }
    }

    MessageBox.Show("All closed.");
}
Community
  • 1
  • 1
noseratio
  • 59,932
  • 34
  • 208
  • 486
  • 1
    Hi @Noseratio, thank you very much for sharing this piece of code! I really like the concept. However, I will go with the solution Curtis Rutland has suggested (mimicking MessageBoxes with plain old forms). – Sebastian Feb 04 '14 at 20:39