locked
How can I show a Messagebox on top of my worker-threaded splash screen? RRS feed

  • Question

  • Hi,

    My Windows Forms app shows a splash screen that is created on a worker thread in program.cs. Before showing the MainForm in it, I need to do a lot of stuff that includes showing error messages by using MessageBox.Show() functions. The problem is that the MessageBox window is displayed behind the splash screen. It behaves as expected when running with the Visual Studio debugger for some reason. I'd appreciate it if someone would let me know how I can show the message box on top of the splash screen. Below shows the repro steps:

    1. Start Visual Studio.
    2. Create a new Windows Forms app in C#.
    3. Visual Studio creates the Form1 class. Change its StartPosition property to CenterScreen. In this sample, Form1 is the splash screen.
    4. Add a new class named SplashFormFactory with the following code:
    namespace WindowsFormsApplication1
    {
        using System.Threading;
        using System.Windows.Forms;
    
        public class SplashFormFactory
        {
            private Form splashForm;
    
            public void ShowSplashForm()
            {
                ThreadPool.QueueUserWorkItem(this.ShowSplashForm);
            }
    
            public void CloseSplashForm()
            {
                this.splashForm.Invoke(new MethodInvoker(this.Close));
            }
    
            private void Close()
            {
                this.splashForm.Close();
            }
    
            private void ShowSplashForm(object stateInfo)
            {
                this.splashForm = new Form1();
                Application.Run(this.splashForm);
            }
        }
    }
    1. Modify the program.cs as follows:
    namespace WindowsFormsApplication1
    {
        using System;
        using System.Threading;
        using System.Windows.Forms;
    
        static class Program
        {
            [STAThread]
            static void Main()
            {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                var splashFormFactory = new SplashFormFactory();
                splashFormFactory.ShowSplashForm();
    
                Thread.Sleep(1000);
                MessageBox.Show("Yes, I'm sure I'm doing something wrong here. But can you tell me exactly what it is?");
                splashFormFactory.CloseSplashForm();
            }
        }
    }
    1. Press the F5 key. The message is displayed on top of the splash screen as expected when running with the Visual Studio.
    2. Now, double click the executable file in Windows Explorer. The message box is displayed behind the splash screen.

    Thanks,


    tetsu

    Saturday, August 24, 2013 8:35 PM

Answers

  • Since I was not able to solve the MessageBox problem with the worker-threaded splash window, I went ahead and changed the splash screen to run in a separate process, rather than on a thread. Voila! It worked. Now, the MessageBox appears in front of the splash screen with the desired animation as expected. It's really an overkill, but I have no choice. If I'm forced to show some status messages on the splash screen in future, I should still be able to do so by using Remoting.

    If anybody can figure out how the Visual Studio debugger does the trick, I would still appreciate it. Thanks everyone for paticipating the discussion.


    tetsu

    • Marked as answer by tetsu1 Tuesday, August 27, 2013 5:52 PM
    Tuesday, August 27, 2013 5:52 PM

All replies

  • Hello tetsu,

    Welcome to the MSDN forum.

    When showing MessageBox provide its owner as the first argument. For example when invoking from a Form instance call:

    MessageBox.Show(this, "Message");

    Provide a reference to the window owning it as the first argument. Message boxes (and modal forms in general) do not appear on top of all windows of your application. They only appear on top of their owner. If you want your message-box (or other modal forms) be on top of your splash screen, set their owner to the splash form instance.

    You are just did what this mentioned. Your message box only appear on top of its owner, that is Windows when you click the exe.

    To make it topmost,you can try refer to the following threads:

    http://social.msdn.microsoft.com/Forums/windows/en-US/cad8d804-e06f-451a-8b0f-0a37fb8dc932/how-to-make-messageboxshow-always-on-top-on-windows-desktop

    http://www.codeproject.com/Articles/18612/TopMost-MessageBox

    and this thread:

    http://stackoverflow.com/questions/16105097/why-isnt-messagebox-topmost.

    By the way, I'm still thinking of the reasons of debug mode.

    Regards,


    Barry Wang
    <THE CONTENT IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, WHETHER EXPRESS OR IMPLIED>
    Thanks
    MSDN Community Support

    Please remember to "Mark as Answer" the responses that resolved your issue. It is a common way to recognize those who have helped you, and makes it easier for other visitors to find the resolution later.

    Monday, August 26, 2013 10:01 AM
  • Hi Barry,

    Thanks for your reply. I tried to use MessageBox.Show(this, "…") only to fail. The Show() function ended up with an InvalidOperationException with the "Cross-thread operation not valid" message in Debug mode (for some reason and very interestingly at the same time, the exception is ignored when running in standalone mode and the app works just as expected). More specifically, I changed the private splashForm variable to public in the SplashFormFactory class and called MessageBox.Show(splashFormFactory.splashForm, "…") in program.cs.

    Is there any way to avoid the exception in Debug mode? I know there’s a function called Invoke() to marshal cross-thread UI calls. But there seems to be no way to apply it to the static MessageBox.Show() function.


    tetsu

    Monday, August 26, 2013 12:22 PM
  • "the exception is ignored when running in standalone mode and the app works just as expected"

    The Cross-Thread detection tool is one of the IDE debugging tools.  It helps you avoid extremely difficult to find problems, and often intermittent, with release software.

    Monday, August 26, 2013 12:56 PM
  • JohnWein,

    More correctly, I should have said that "the exception is NOT THROWN AT ALL when running in standalone mode". So, it's not like I catch it and do nothing in the catch block, which means "swallowing an exception" and, as you said, a real no-no.


    tetsu

    Monday, August 26, 2013 1:32 PM
  • JohnWein,

    More correctly, I should have said that "the exception is NOT THROWN AT ALL when running in standalone mode". So, it's not like I catch it and do nothing in the catch block, which means "swallowing an exception" and, as you said, a real no-no.

    I have no idea how this post relates to your use of the Cross-Tread detection tool.

    The Cross-Thread detection tool is a debugging tool, so it is only available when running in debug mode.

    The Cross-Tread detection tool has indicated that you have a Cross-Tread call.  To avoid nasty, hard-to-find problems with your application, you should modify your code to eliminate the Cross-Tread call.

    Monday, August 26, 2013 7:32 PM
  • JohnWein,

    So, do you have any idea how I can marshal the cross-thread call in this particular app?


    tetsu

    Monday, August 26, 2013 7:46 PM
  • JohnWein,

    So, do you have any idea how I can marshal the cross-thread call in this particular app?


    Show the MessageBox from the SplashScreen thread.
    Monday, August 26, 2013 8:05 PM
  • Hmmm. That would violate the SRP (Single Responsibility Principle) from the Splash screen point of view and the result would be an ugly code.

    tetsu

    Monday, August 26, 2013 8:17 PM
  • Hmmm. That would violate the SRP (Single Responsibility Principle) from the Splash screen point of view and the result would be an ugly code.

    tetsu

    Show all your forms on the same thread.  I don't see any need for 2 threads in the code you posted.  All you're doing is showing a splash screen while loading a form.
    Monday, August 26, 2013 8:36 PM
  • >I don't see any need for 2 threads in the code you posted.

    True. But there's a need for two threads; I have to show animation on the splash screen. I just wanted to simplify the sample. If I show the form on the same UI thread, it won't animate. Place a picture box in the Form1 with a GIF file and increase the Sleep() in program.cs to 10 seconds. Now, you should be able to understand I needed two threads. I don't want to do co-operative multi tasking between the splash form and the program.cs to show the animation.

    Since the Visual Studio debugger can do it, I thought there must be a way to achieve the same without drastically changing the code in program.cs and the splash form.


    tetsu

    Tuesday, August 27, 2013 12:22 AM
  • Since I was not able to solve the MessageBox problem with the worker-threaded splash window, I went ahead and changed the splash screen to run in a separate process, rather than on a thread. Voila! It worked. Now, the MessageBox appears in front of the splash screen with the desired animation as expected. It's really an overkill, but I have no choice. If I'm forced to show some status messages on the splash screen in future, I should still be able to do so by using Remoting.

    If anybody can figure out how the Visual Studio debugger does the trick, I would still appreciate it. Thanks everyone for paticipating the discussion.


    tetsu

    • Marked as answer by tetsu1 Tuesday, August 27, 2013 5:52 PM
    Tuesday, August 27, 2013 5:52 PM
  • I know, it's a very old thread, but maybe someone can use this solution:

    In the splash thread create a NativeWindow from the splash form:

                NativeWindow nativeSplashWindow = new NativeWindow();
                nativeSplashWindow.AssignHandle(splashForm.Handle);

    In the main thread, use this for the messagebox

              MessageBox.Show(nativeSplashWindow, "message");

    Friday, August 14, 2020 2:20 PM