MessageBox and subtleties

Gloops

Well-known member
Joined
Jun 30, 2022
Messages
137
Programming Experience
10+
Hello everybody,

My MessageBox displayed the message, but behind other windows, and with the mouse cursor in the middle of the message window instead of the middle of the button.
Furthermore, after I positioned the mouse cursor, the previous position was still visible, so I saw two mouse cursors on the screen.

So, I corrected that, it works good, but I wonder whether a much more simple solution does not exist.

And also, to evaluate the correct position of the cursor, on WinForms you have Control.PointToScreen, but I am not sure that can be used on a MessageBox. I put values hardcoded, that is not sure to adapt if I change the sizes of the fonts in Windows.
Manage a MessageBox:
       private void Button_Click(Object sender, EventArgs e)
       {
                if (fic.FullName.Equals(strPath))
                {
                    bFicPresent = true;
                    Form messageForm = new Form();
                    messageForm.TopMost = true;
                    strWindowToPosition = "File already present";
                    timer2.Enabled = true;
                    MessageBox.Show(messageForm, String.Format("{1} :\n{0}", fic.FullName, strWindowToPosition), strWindowToPosition);
                    messageForm.Close();
                    break;
                }
       }

        string strWindowToPosition;

        private void timer2_Tick(object sender, EventArgs e)
        {
            timer2.Enabled = false;
            //System.Threading.Thread.Sleep(1000);
            RECT rect = new RECT();
            IntPtr hWnd = FindWindow(null, strWindowToPosition);
            SetForegroundWindow(hWnd);
            SetActiveWindow(hWnd);
            GetWindowRect(hWnd, out rect);
            int x = rect.Right - (btnShortcut.Width + 60);
            int y = rect.Bottom - 70;
            //lblInfo.Text = x.ToString();
            SetCursorPos(x, y);
            #region mask old position of cursor
            for (int i = 0; i < 10; i++)
            {
                System.Threading.Thread.Sleep(100);
                SetCursorPos(x--, y--);
            }
            #endregion
        }

The timer is also used at another place, where I do not use messageForm to manage the TopMost attribute. It some of works, but the MessageBox appears behind the calling form. I found a solution by hiding the calling form until the user has validated the message.

I repeat the title of the MessageBox at the top of the message, as during several months my Windows did not show the titles of the messages, either in C# or in Javascript. Having it repeated looks strange but works, if it is not displayed at all you miss some information.
 
Are you sure all your forms are being created and shown on the UI thread? Are you sure that you trying to show the message box while running on the UI thread? Are you sure that all your windows are owned by your main form?

If you could post a minimally complete code that demonstrates the problem, it would be a better way to try to figure out what is happening.
 
Are you sure all your forms are being created and shown on the UI thread? Are you sure that you trying to show the message box while running on the UI thread? Are you sure that all your windows are owned by your main form?
No, I am just sure I cannot post my code, and I wonder why.
 
Let us try from Notepad2...
Code of a button:
        private void btnPlus_Click(object sender, EventArgs e)
        {
            bool bRepPresent = false;
            bool bFicPresent = false;
            FileInfo fi = new FileInfo(strFileName);
            string[] splContenu = { "" };
            int i = 0;
            StringBuilder sbContenu = new StringBuilder();
            btnShortcut_Click(btnShortcut, new EventArgs());
            string strPath = Clipboard.GetText();
            FileInfo fic;
            try
            {
                fic = new FileInfo(strPath);
            }
            catch(Exception ex)
            {
                return;
            }
            if (fic.Exists)
            {
                using (StreamReader sr = new StreamReader(strFileName))
                {
                    splContenu = File.ReadAllLines(strFileName, Encoding.GetEncoding("Utf-32"));
                    for (int i1 = 0; i1 < splContenu.Count(); i1++)
                    {
                        if (splContenu[i1].Replace("[", "").Replace("]", "").Equals(fic.DirectoryName))
                        {
                            bRepPresent = true;
                        }
                        if (fic.FullName.Equals(strPath))
                        {
                            bFicPresent = true;
                            Form messageForm = new Form();
                            messageForm.TopMost = true;
                            strTitreFenetreAPositionner = "Fichier déjà présent";
                            timer2.Enabled = true;
                            MessageBox.Show(messageForm, String.Format("{1} :\n{0}", fic.FullName, strTitreFenetreAPositionner), "Fichier déjà présent");
                            messageForm.Close();
                            break;
                        }
                    }
                }
                if (i > splContenu.Count())
                {
                    while (splContenu[i].StartsWith("["))
                    {
                        sbContenu.AppendLine(splContenu[i]);
                        i++;
                    }
                }
            }
            if(!fic.Exists)
            {
                MessageBox.Show(String.Format("{0} : fichier non trouvé", strPath));
            }
            else
            {
                if (!bRepPresent)
                {
                    sbContenu.AppendLine(String.Format("[{0}]", fic.DirectoryName));
                }
                for(int ln=i; ln<splContenu.Count();ln++)
                {
                    if (!String.IsNullOrEmpty(splContenu[ln]))
                    {
                        sbContenu.AppendLine(splContenu[ln]);
                    }
                }
                if (!bFicPresent)
                {
                    sbContenu.AppendLine(fic.Name);
                }
                //using (StreamWriter sr = new StreamWriter(strFileName, true, Encoding.GetEncoding("utf-32")))
                //{
                //    string input = sbContenu.ToString();
                //    System.Diagnostics.Debug.Print("«{0}»",input);
                //    byte[] utfBytes = Encoding.UTF32.GetBytes(input);
                //    StringBuilder builder = new StringBuilder();
                //    for (int ib = 0; ib < utfBytes.Length; ib+=4)
                //    {
                //        builder.Append(utfBytes[ib].ToString());
                //    }
                //    Console.WriteLine(builder.ToString());
                //}
                using (StreamWriter sr = new StreamWriter(strFileName, false, Encoding.UTF8))
                {
                    string input = sbContenu.ToString();
                    input = ConvertEncoding(Encoding.Default, Encoding.UTF8, input);
                    sr.WriteLine(input);
                }
            }
            Point pt = new Point(btnPlus.Left + (btnPlus.Width / 2), btnPlus.Top + (btnPlus.Height / 2));
            pt = btnPlus.PointToScreen(pt);
            int x = pt.X;
            int y = pt.Y - 40;
            SetCursorPos(x,y);
            #region     hide previous position of cursor
            for (int i1 = 0; i1 < 10; i1++)
            {
                System.Threading.Thread.Sleep(100);
                SetCursorPos(x++, y--);
            }
            #endregion
        }

Oh, this time it went through, by pack of 6 or 7 lines ...
I did not even try and understand what I was copying.
 
So, that code displays the message boxes OK, but with a code that is not very clean.
As I remember, if there is only a MessageBox in the code of a button, it is displayed OK in the front, either it declares an owner for the MessageBox or not.
But at line 38 here above, if there is no owner declared, the message box is only displayed if I execute step by step.
It is so laborious that I was not really thinking while doing it.
 
Got one try where I got it, if I put no owner the message is behind the form, if I put "this", it is above it.
I look at that this afternoon ...
 
Well, the syntax should be this one, no ?
C#:
                            MessageBox.Show(this,
                                strMessage,
                                strTitle,
                                MessageBoxButtons.OK,
                                MessageBoxIcon.Information,
                                MessageBoxDefaultButton.Button1);

If this is correct, there seems to be a bug . When opening the dialog box, I obtain two mouse cursors on it :
  • one in the middle of the message box
  • one in the middle of the button
Until there, it is not nice but it can be operational. Suspense ...
But when I move the mouse, it is the first cursor that remains, whereas MessageBoxDefaultButton.Button1 means I require the second.

Anything I missed?

If I activate my timer then the position of the cursor is correct, but then it is as if I did not open this thread.

If I put two buttons (MessageBoxButtons.OKCancel), and MessageBoxDefaultButton.Button1 then the second cursor is on the second button, whereas it still should be on the first one.
 
Last edited:
Remove your SetCursor() calls. Let Windows place the cursor as the user wishes -- either at the current cursor position, or if the user changed the settings to move the cursor to the default button. You are basically running afoul of the Windows, or graphics driver, or mouse driver's cursor painting optimizations.
 
I created a brand new project called TestMessageBox, with instruction of message 22 in the Form_Load (and values in the two strings of course).
In fact, the cursor is not positioned in the middle of the MessageBox, but in the middle of the screen.
In any case, that can only rarely be near the button and I saw no example of this.
Do I have a bad thing on my machine?

I created a test project in .Net Core (Net 6), and one in .Net Framework (4.7.2).
I made the same observation in both, and I have a bigger problem that perhaps will need another thread: there are so numerous inactive controls in the Toolbar, that I do not find the active controls.

There they are, with the scrollbar at 3/4.

So I could try from a button, and I realize that the problem is different from what I said: there is no cursor positioning by the MessageBox, despite the default button. I verified in the Windows parameters of the mouse that the checkbox to position the cursor on the default button is checked.

Microsoft Windows [version 10.0.19044.1826](21H2)
Visual Studio Community 2022 (64 bits) version 17.2.6

The information hereafter appears localized, I wonder whether there is an option somewhere to get it in English, I presume it would be more readable for you.

Information copied from 'About' Visual Studio:
Microsoft Visual Studio Community 2022
Version 17.2.6
VisualStudio.17.Release/17.2.6+32630.192
Microsoft .NET Framework
Version 4.8.04084

Version installée : Community

Visual C++ 2022   00482-90000-00000-AA078
Microsoft Visual C++ 2022

ADL Tools Service Provider   1.0
This package contains services used by Data Lake tools

ASA Service Provider   1.0

ASP.NET and Web Tools 2019   17.2.393.26812
ASP.NET and Web Tools 2019

Azure Data Lake Tools for Visual Studio   2.6.5000.0
Microsoft Azure Data Lake Tools for Visual Studio

Azure Functions and Web Jobs Tools   17.2.393.26812
Azure Functions and Web Jobs Tools

Azure Stream Analytics Tools for Visual Studio   2.6.5000.0
Microsoft Azure Stream Analytics Tools for Visual Studio

Common Azure Tools   1.10
Provides common services for use by Azure Mobile Services and Microsoft Azure Tools.

Cookiecutter   17.0.22089.1
Fournit des outils pour rechercher, instancier et personnaliser des modèles au format cookiecutter.

Débogage de .NET Core avec WSL   1.0
Débogage de .NET Core avec WSL

Extensibility Message Bus   1.2.6 (master@34d6af2)
Provides common messaging-based MEF services for loosely coupled Visual Studio extension components communication and integration.

Gestionnaire de package NuGet   6.2.1
Gestionnaire de package NuGet dans Visual Studio. Pour plus d'informations sur NuGet, visitez [URL='https://docs.nuget.org/']NuGet documentation[/URL]

Microsoft Azure Hive Query Language Service   2.6.5000.0
Language service for Hive query

Microsoft Azure Stream Analytics Language Service   2.6.5000.0
Language service for Azure Stream Analytics

Microsoft Azure Tools pour Visual Studio   2.9
Prise en charge des projets Azure Cloud Services

Microsoft JVM Debugger   1.0
Provides support for connecting the Visual Studio debugger to JDWP compatible Java Virtual Machines

Mono Debugging for Visual Studio   17.2.20 (482eb2a)
Support for debugging Mono processes with Visual Studio.

Node.js Tools   1.5.40404.3 Commit Hash:dd524a7d0ee653de8b7b32a93ca22f2d57cd2419
Ajoute la prise en charge du développement et du débogage d'applications Node.js dans Visual Studio

Outils Azure App Service v3.0.0   17.2.393.26812
Outils Azure App Service v3.0.0

Outils C#   4.2.0-4.22281.5+8d3180e5f00d42f0f0295165f756f368f0cbfa44
Composants C# utilisés dans l'IDE. Selon votre type de projet et vos paramètres, une version différente du compilateur peut être utilisée.

Outils TypeScript   17.0.10418.2001
Outils TypeScript pour Microsoft Visual Studio

Outils Visual Basic   4.2.0-4.22281.5+8d3180e5f00d42f0f0295165f756f368f0cbfa44
Composants Visual Basic utilisés dans l'IDE. Selon votre type de projet et vos paramètres, une version différente du compilateur peut être utilisée.

Python - Prise en charge du profilage   17.0.22089.1
Prise en charge du profilage pour les projets Python.

Python avec Pylance   17.0.22089.1
Fournit IntelliSense, des projets, des modèles, le débogage, des fenêtres interactives et du support pour les développeurs Python.

Razor (ASP.NET Core)   17.0.0.2218101+885a343b00bcab620a90c1550c37dafd730ce984
Fournit des services de langage pour ASP.NET Core Razor.

SQL Server Data Tools   17.0.62204.01010
Microsoft SQL Server Data Tools

ToolWindowHostedEditor   1.0
Hosting json editor into a tool window

Visual F# Tools   17.1.0-beta.22329.1+702b8e77f5fbfe21e6743324c1750503e02f182d
Microsoft Visual F# Tools

Visual Studio IntelliCode   2.2
Développement assisté par intelligence artificielle pour Visual Studio.

Visual Studio Tools pour Unity   17.2.3.0
Visual Studio Tools pour Unity

VisualStudio.DeviceLog   1.0
Informations sur mon paquet

VisualStudio.Mac   1.0
Mac Extension for Visual Studio

VSPackage Extension   1.0
VSPackage Visual Studio Extension Detailed Info

Xamarin   17.2.0.177 (d17-2@2f6c881)
Extension Visual Studio permettant de développer pour Xamarin.iOS et Xamarin.Android.

Xamarin Designer   17.2.0.244 (remotes/origin/d17-2@197e1a0b7)
Extension Visual Studio pour activer les outils Xamarin Designer dans Visual Studio.

Xamarin Templates   17.2.15 (2e3b60e)
Templates for building iOS, Android, and Windows apps with Xamarin and Xamarin.Forms.

Xamarin.Android SDK   12.3.3.3 (d17-2/4e061b7)
Xamarin.Android Reference Assemblies and MSBuild support.
    Mono: dffa5ab
    Java.Interop: xamarin/java.interop/d17-2@9760f0a9
    ProGuard: Guardsquare/proguard/v7.0.1@912d149
    SQLite: xamarin/sqlite/3.38.2@7b1e016
    Xamarin.Android Tools: xamarin/xamarin-android-tools/d17-2@fc3c2ac


Xamarin.iOS and Xamarin.Mac SDK   15.10.0.5 (96b3edb6d)
Xamarin.iOS and Xamarin.Mac Reference Assemblies and MSBuild support.
 
Last edited:
Here's a simple test to see if that "Snap To" feature works. Start notepad. Select, File.Open. The mouse cursor should move automatically to the "Open" button of the File Open dialog.

As a quick aside, the version of the .NET framework that you use shouldn't make a difference on whether "Snap To" works or not since the feature is at the OS level, not at the application level.
 
Regarding "snap to", can confirm MessageBox with only OK button doesn't work, at least not in Windows 11. Two or more buttons works. No difference for .Net framework or .Net 6.
A custom dialog form with one button and AcceptButton set works too.
 
Same for Win10 regarding the message box with just a single button.
 
Hello,
I tested with Notepad2, in File Open, the mouse cursor comes on the button.
Same thing with Firefox, File Open a file.
The problem is in my applications.
At the OS level, there is a checkbox that gives instruction to the applications to put the cursor on the default button. I shall have to search how to know whether it is checked. After that, it is up to the applications to do the job.
But in C# it is supposed to be simplified by the instruction MessageBox, that has a default button parameter.

As I said I made a test with two buttons, MessageBoxButtons.OKCancel and MessageBoxDefaultButton.Button1, but the mouse cursor was not positioned. I also tried the other values of MessageBoxDefaultButton, with no success.
 
Regarding "snap to", can confirm MessageBox with only OK button doesn't work, at least not in Windows 11. Two or more buttons works. No difference for .Net framework or .Net 6.
A custom dialog form with one button and AcceptButton set works too.
Oh, so it seems that more simply I should reactivate my timer.
But you(*) are right, I have to look in the Windows settings, to conform to the user's choice. And I activate the timer only if he checked the checkbox.
Supposing he has a reason to make a different choice for the application, then I have to provide a configuration dialog box, with default to the choice at the system level.

OK, going to open a ticket by the Visual Studio team, for the MessageBox default button.


(*) you in the generic meaning, as it was SkyDiver's reaction
 
Last edited:
Back
Top Bottom