dotnet / winforms

Windows Forms is a .NET UI framework for building Windows desktop applications.
MIT License
4.38k stars 971 forks source link

Multiple help links opened, when clicking the Help button in the MessageBox within modal form #11510

Open snechaev opened 3 months ago

snechaev commented 3 months ago

.NET version

net8.0.300

Full `dotnet --info` output

.NET SDK: Version: 8.0.300 Commit: 326f6e68b2 Workload version: 8.0.300-manifests.9e3391ed MSBuild version: 17.10.4+10fbfbf2e Runtime Environment: OS Name: Windows OS Version: 10.0.19045 OS Platform: Windows RID: win-x64 Base Path: C:\Program Files\dotnet\sdk\8.0.300\ .NET workloads installed: [maccatalyst] Installation Source: SDK 8.0.300, VS 17.10.34928.147 Manifest Version: 17.2.8053/8.0.100 Manifest Path: C:\Program Files\dotnet\sdk-manifests\8.0.100\microsoft.net.sdk.maccatalyst\17.2.8053\WorkloadManifest.json Install Type: FileBased [macos] Installation Source: SDK 8.0.300, VS 17.10.34928.147 Manifest Version: 14.2.8053/8.0.100 Manifest Path: C:\Program Files\dotnet\sdk-manifests\8.0.100\microsoft.net.sdk.macos\14.2.8053\WorkloadManifest.json Install Type: FileBased Host: Version: 8.0.5 Architecture: x64 Commit: 087e15321b .NET SDKs installed: 2.1.526 [C:\Program Files\dotnet\sdk] 2.1.701 [C:\Program Files\dotnet\sdk] 2.1.818 [C:\Program Files\dotnet\sdk] 3.1.100 [C:\Program Files\dotnet\sdk] 3.1.120 [C:\Program Files\dotnet\sdk] 3.1.426 [C:\Program Files\dotnet\sdk] 5.0.102 [C:\Program Files\dotnet\sdk] 5.0.104 [C:\Program Files\dotnet\sdk] 5.0.201 [C:\Program Files\dotnet\sdk] 5.0.214 [C:\Program Files\dotnet\sdk] 5.0.303 [C:\Program Files\dotnet\sdk] 5.0.405 [C:\Program Files\dotnet\sdk] 5.0.408 [C:\Program Files\dotnet\sdk] 6.0.202 [C:\Program Files\dotnet\sdk] 6.0.203 [C:\Program Files\dotnet\sdk] 6.0.301 [C:\Program Files\dotnet\sdk] 6.0.321 [C:\Program Files\dotnet\sdk] 7.0.410 [C:\Program Files\dotnet\sdk] 8.0.300 [C:\Program Files\dotnet\sdk] .NET runtimes installed: Microsoft.AspNetCore.All 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.1.15 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.1.18 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.1.19 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.1.20 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.1.30 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.App 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.1.15 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.1.18 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.1.19 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.1.20 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.1.30 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.18 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.20 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.22 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.26 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.27 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.28 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.29 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.32 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 5.0.2 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 5.0.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 5.0.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 5.0.14 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 6.0.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 6.0.6 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 6.0.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 6.0.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 6.0.26 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 6.0.30 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 7.0.19 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 7.0.20 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 8.0.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.NETCore.App 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.15 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.18 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.19 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.20 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.30 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.11 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.13 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.18 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.20 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.22 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.26 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.27 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.28 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.29 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.32 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 5.0.2 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 5.0.4 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 5.0.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 5.0.14 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 6.0.4 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 6.0.6 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 6.0.8 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 6.0.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 6.0.15 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 6.0.26 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 6.0.30 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 7.0.19 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 7.0.20 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 8.0.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.WindowsDesktop.App 3.1.0 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 3.1.11 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 3.1.13 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 3.1.18 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 3.1.20 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 3.1.22 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 3.1.26 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 3.1.27 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 3.1.28 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 3.1.29 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 3.1.32 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 5.0.2 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 5.0.4 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 5.0.9 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 5.0.14 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 6.0.4 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 6.0.6 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 6.0.8 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 6.0.9 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 6.0.15 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 6.0.26 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 6.0.30 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 7.0.19 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 7.0.20 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 8.0.5 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Other architectures found: arm64 [C:\Program Files\dotnet] registered at [HKLM\SOFTWARE\dotnet\Setup\InstalledVersions\arm64\InstallLocation] x86 [C:\Program Files (x86)\dotnet] registered at [HKLM\SOFTWARE\dotnet\Setup\InstalledVersions\x86\InstallLocation] Environment variables: Not set global.json file: Not found Learn more: https://aka.ms/dotnet/info Download .NET: https://aka.ms/dotnet/download

Did it work in .NET Framework?

No

Did it work in any of the earlier releases of .NET Core or .NET 5+?

Does not work in net6 and net8. Not tested in other net core versions.

Issue description

I have a PropertyGrid with a custom modal UITypeEditor. In this editor I use MessageBox with a help button.

           MessageBox.Show(
               "Click Help. You will get 2 browsers opened (instead 1 as expected).",
               "MessageBox",
               MessageBoxButtons.OK,
               MessageBoxIcon.Information,
               MessageBoxDefaultButton.Button1,
               0,
               "https://github.com/dotnet/winforms/");

When I click the Help button, I got two tabs with a help link opened.

Steps to reproduce

Expected: the single browser/tab opened with the help link

Actual: two tabs with the help link opened.

Additional notes:

(Added later): the problem is not related to the UITypeEditor, it is related to the any modal windows, so the nested editor below may be replaced by the nested modal dialogs/Form.ShowDialog() calls.

If we will add the nested editor (e.g. put another PropertyGrid in the CustomForm) and call the custom editor from this grid, then we will get the 3 tabs with a help link. So, it looks like each occurrence of the IWindowsFormsEditorService.ShowDialog on the stack will add the extra 1 unwanted help link tab.

elachlan commented 3 months ago

@Olina-Zhang can your team please test this?

Olina-Zhang commented 3 months ago

Tested that project in .NET 9.0 and .NET framework 4.8, they have same result: this website "https://github.com/dotnet/winforms/" is opened twice. But if I put the same MessageBox in a button control on form, it is just opened one time.

merriemcgaw commented 3 months ago

This didn't actually work in .NET Framework and we've got the work around. @LeafShi1 can your team to do a brief investigation to see if we can identify the cause. Otherwise, since it didn't work in Framework we may not be able to address it in Core.

Epica3055 commented 3 months ago

this is the callstack of MessageBox.Show

image

image

I think we are not able to debug Help button click in Winform repo.

snechaev commented 3 months ago

@Epica3055, the native MessageBox() is used here. As per docs,

When the user clicks the Help button or presses F1, the system sends a WM_HELP message to the owner.

So, you need to track down, where the WM_HELP is processed and why it is not marked as handled after the first help link opened.

And from the brief code analysis, it is most likely that such a help requests will end somewhere in one of the Help.ShowHelp() overloads: .

snechaev commented 3 months ago

I did some other experiments and found out, that the problem is not related to the UITypeEditor at all. The problem can be reproduced by simply opening the multiple forms with Form.ShowDialog(). So, here is a simplified test code.

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    protected override void OnActivated(EventArgs e)
    {
        base.OnActivated(e);
        using var t = new CustomForm();
        t.ShowDialog(this);
    }
}

public class CustomForm : Form
{
    protected override void OnActivated(EventArgs e)
    {
        base.OnActivated(e);
        MessageBox.Show(
                this,
                "Click Help. You will get 2 browsers opened (instead 1 as expected).",
                "MessageBox",
                MessageBoxButtons.OK,
                MessageBoxIcon.Information,
                MessageBoxDefaultButton.Button1,
                0,
                "https://github.com/dotnet/winforms/");
    }
}

I can also confirm, that we have two calls to the Help.ShowHelp() - the first one from the modal CustomForm (which is the owner of the MessageBox) and the second one from the Form1 (which is the owner/parent of the CustomForm via this Form.ShowDialog()).

snechaev commented 3 months ago

The documentation of the MessageBox states then

The form that owns the message box (or the active form) also receives the HelpRequested event.

So, looks like the control flow is following

Conclusion

I can't say for sure if the current behaviour is correct or not. The WM_HELP bubbling makes sense in non-messagebox cases.

It looks like we can force the MW_HELP bubbling to stop for the MessageBox case. This will still be fully compliant with the documentation - the owner form of the MessageBox will get HelpRequested (as the documentation says), but other/parent forms will not. But this will at least make the HelpRequested behavior inconsistent in different scenarios.

The other solution might be to introduce an additional state to store information about "this help link has already been processed". But there is the problem that the user can really click the same help button in the same MessageBox multiple times, so it will be absolutely fine to process the same HelpInfo again.

Workaround

The possible workaround - add the empty HelpRequested handler for the MessageBox owner for (the CustomForm in the test project.

    public CustomForm()
    {
        HelpRequested += (sender, hlpevent) => { };
    }
Epica3055 commented 3 months ago

@snechaev , thanks for you information. 👍 I will look into it.

Epica3055 commented 3 months ago

@snechaev thanks you for the analysis ,I add some details here.

Root cause about this issue

When you try to open a MessageBox with a helpFilePath parameter, and the Window that contains the MessageBox has a parent Window, and click Help button like this image

you will open two web page in browser.

Actually, when there are n ancestor windows, there will be n additional web page in browser.

This happens because:

  1. when you click that Help button, WM_HELP will be emitted. https://github.com/dotnet/winforms/blob/b0051e354c0e48009444fb4907a6ce61ac497910/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs#L12918-L12970

  2. After the event is handled, WM_HELP event will also be sent to ancestor window, see the the document here windows/win32/shell/wm-help and code https://github.com/dotnet/winforms/blob/b0051e354c0e48009444fb4907a6ce61ac497910/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs#L12007-L12038 So the event will be handled multiple times accordingly.

If you want it to be opened one time when you click it once

  1. see the code here, line 120307 - 12038.We can see that when hevent.Handled is true the event will not be sent to ancestor window. https://github.com/dotnet/winforms/blob/b0051e354c0e48009444fb4907a6ce61ac497910/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs#L12007-L12038

  2. See OnHelpRequested method line 8097-8106, We can see that when there is a handler, the Handled will be updated to true so that the event won't be sent to ancestor Window. https://github.com/dotnet/winforms/blob/b0051e354c0e48009444fb4907a6ce61ac497910/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs#L8095-L8115

workaround

Add a HelpEventHandler to the window that contains the MessageBox with a helpFilePath parameter, even if it is empty.

    public class CustomForm : Form
    {
    ...
          public CustomForm()
          {
              HelpRequested += (sender, hlpevent) => { };
          }
    ...
    }
snechaev commented 3 months ago

Sorry, but it looks like the above workaround won't work in the slightly extended scenario :(

Let's say that we have a form-wide help topic for the CustomForm that can be accessed via the F1 key on the form. The URL will be, for example, http://form-wide-help.com. The HelpRequested event handler implemented to handle help request

        private void CustomForm_HelpRequested(object sender, HelpEventArgs hlpevent)
        {
            Help.ShowHelp(this, "http://form-wide-help.com");
        }

Then for the MessageBox we have a dedicated help topic with the URL http://message-box-help.com.

So when you click the Help button (or F1) inside the MessageBox, the following will happen:

So, we have two help links opened again (even though we used the workaround suggested above). And I have not found any way to modify the CustomForm_HelpRequested() handler to distinguish between situations where the help was requested directly on the form, or where the event was propagated from the nested message box.

Updated test code

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        protected override void OnActivated(EventArgs e)
        {
            base.OnActivated(e);
            using (var t = new CustomForm())
            {
                t.ShowDialog(this);
            }
        }
    }

    public sealed class CustomForm : Form
    {
        public CustomForm()
        {
            Text = "Click me open the MessageBox";
            //HelpRequested += (sender, hlpevent) => { };
            HelpRequested += this.CustomForm_HelpRequested;
        }

        private void CustomForm_HelpRequested(object sender, HelpEventArgs hlpevent)
        {
            Help.ShowHelp(this, "http://form-wide-help.com");
        }

        protected override void OnClick(EventArgs e)
        {
            base.OnClick(e);
            MessageBox.Show(
                this,
                "Click Help. You will get 2 browsers opened (instead 1 as expected).",
                "MessageBox",
                MessageBoxButtons.OK,
                MessageBoxIcon.Information,
                MessageBoxDefaultButton.Button1,
                0,
                "http://message-box-help.com/");
        }
    }

Steps:

Epica3055 commented 2 months ago

I will investigate it.

Epica3055 commented 2 months ago

@snechaev

I modified your code a little bit, hope it could help.


        private void CustomForm_HelpRequested(object sender, HelpEventArgs hlpevent)
        {
            if (!_messageBoxShown)
            {
                Help.ShowHelp(this, "http://form-wide-help.com");
            }
        }

        private bool _messageBoxShown = false;

        protected override void OnClick(EventArgs e)
        {
            _messageBoxShown = true;
            base.OnClick(e);
            MessageBox.Show(
                this,
                "Click Help. You will get 2 browsers opened (instead 1 as expected).",
                "MessageBox",
                MessageBoxButtons.OK,
                MessageBoxIcon.Information,
                MessageBoxDefaultButton.Button1,
                0,
                "http://message-box-help.com/");
            _messageBoxShown = false;
        }
snechaev commented 2 months ago

@Epica3055 , your improved workaround will work in very this very minimal example, but it is obvious that it is completely unusable in the real life, when the components are loosely coupled and may belong to the different 3rd party vendors. So, you can't know if some code you call will show message box, when will it show and will such a messagebox contain the help button. In this case you simply do not have enough information to set your _messageBoxShown flag.

Epica3055 commented 2 months ago

@snechaev Do you think this could help you?

public sealed class CustomForm : Form
{

    public CustomForm()
    {
        Text = "Click me open the MessageBox";
        HelpRequested += CustomForm_HelpRequested;
        KeyDown += CustomForm_KeyDown;
    }

    private void CustomForm_KeyDown(object? sender, KeyEventArgs e)
    {
        if (e.KeyCode == Keys.F1)
        {
            Help.ShowHelp(this, "http://form-wide-help.com");
        }
    }

    private void CustomForm_HelpRequested(object sender, HelpEventArgs hlpevent)
    {

    }

    protected override void OnClick(EventArgs e)
    {
        base.OnClick(e);
        MessageBox.Show(
            this,
            "Click Help. You will get 2 browsers opened (instead 1 as expected).",
            "MessageBox",
            MessageBoxButtons.OK,
            MessageBoxIcon.Information,
            MessageBoxDefaultButton.Button1,
            0,
            "http://message-box-help.com/");
    }
}