Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Form not displayed in foreground #334

Closed
fvanheeswijk opened this issue Jul 14, 2020 · 24 comments
Closed

Form not displayed in foreground #334

fvanheeswijk opened this issue Jul 14, 2020 · 24 comments
Labels
bug Something isn't working

Comments

@fvanheeswijk
Copy link

fvanheeswijk commented Jul 14, 2020

We are seeing the following issue in our application: When we click on links to open objects within our application they are displayed underneath the current form (main application window). Strangely enough it does behave correctly if we select the link via tab and then enter it to open the link.

A reproducible example is available below:

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

using Microsoft.Web.WebView2.WinForms;

namespace WebView2BugMultipleForms
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(CreateTestForm());
        }

        public static Form CreateTestForm()
        {
            var form = new Form
            {
                Size = new Size(1280, 1024)
            };

            var webView2 = new WebView2
            {
                Dock = DockStyle.Fill,
                Source = new Uri(@"about:blank")
            };
            webView2.CoreWebView2Ready += (sender, e) =>
            {
                webView2.CoreWebView2.AddHostObjectToScript("handler", new Handler());
                webView2.CoreWebView2.NavigateToString(
@"<html>
    <head>
        <title>Test</title>
    </head>
    <body>
        <button onclick='window.chrome.webview.hostObjects.sync.handler.openForm();'><h1>Open new form</h1></button>
    </body>
</html>");
            };

            form.Controls.Add(webView2);

            return form;
        }
    }

    [ComVisible(true)]
    public class Handler
    {
        public void openForm()
        {
            new Thread(() =>
            {
                var newForm = Program.CreateTestForm();
                var nativeWindow = new NativeWindow();
                nativeWindow.AssignHandle(IntPtr.Zero);
                newForm.ShowDialog(nativeWindow);
            }).Start();
        }
    }
}

In here it basically reproduces in the same way:

  • Clicking the button results in the form being opened under the current form
  • Tabbing and entering the button results in the forming being opened on top of the current form

We expect the form to be opened on top of the current form in all cases. Also please be advised that the form is not supposed to be modal.

AB#27643346

@pagoe-msft pagoe-msft added .NET bug Something isn't working labels Jul 14, 2020
@pagoe-msft
Copy link

@fvanheeswijk,

Thanks! I've filed a bug and we will follow up.

@cgeier
Copy link

cgeier commented Aug 19, 2020

@fvanheeswijk, To get the form to display on top, when the button is clicked with the mouse, give the following a try. I've tested it and it seems to work.

Replace the Handler class code with the following:


    [ComVisible(true)]
    public class Handler
    {
        public void openForm()
        {

            CancellationToken cToken = new CancellationToken();

            Task.Factory.StartNew(() =>
            {
                var newForm = Program.CreateTestForm();
                newForm.Text = "Another Window";
                var nativeWindow = new NativeWindow();
                nativeWindow.AssignHandle(IntPtr.Zero);
                newForm.ShowDialog(nativeWindow);
            }, cToken, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
        }
    }   

*Note: You must include TaskScheduler.FromCurrentSynchronizationContext(), otherwise the new window displays underneath the current form (main application window).

@cgeier
Copy link

cgeier commented Aug 19, 2020

Here's another version of the Handler class that also works when the button is clicked with the mouse.

    [ComVisible(true)]
    public class Handler
    {
        public void openForm()
        {
            var sc = SynchronizationContext.Current;

            ThreadPool.QueueUserWorkItem(delegate
            {
                //do work on threadpool

                sc.Post(delegate 
                {
                    //do work on original context

                    var newForm = Program.CreateTestForm();
                    newForm.Text = "Another Window";
                    var nativeWindow = new NativeWindow();
                    nativeWindow.AssignHandle(IntPtr.Zero);
                    newForm.ShowDialog(nativeWindow);
                }, null);
            });
        }
    }

*Note: This code combines your code with the code from the article: Execution vs SynchronizationContext

@fvanheeswijk
Copy link
Author

Hello, I've just had time to test your code, but unfortunately it doesn't resolve our issue.
We want the opened form to be modeless, but with your code it becomes modal.

@fvanheeswijk
Copy link
Author

I also want to emphasize that this issue is urgent for us, it looks like the WebView2 webbrowser is heading towards a stable state yet we cannot use it yet due to at least this issue. And we are seeing an increased influx on issues on the old webbowser control, so we need to replace it.

@champnic
Copy link
Member

champnic commented Nov 2, 2020

@fvanheeswijk We'll try and take a deeper look shortly. It looks like it's probably related to window activation or focus due to the threading. If I don't try and open the window in a new thread it correctly shows up in the foreground for me. Until we have a fix, how important is the multi-threading for your scenario?

@fvanheeswijk
Copy link
Author

Okay, thank you for taking a deeper look into this!
It is very important for our scenario as we are working with multiple UI threads.

@fvanheeswijk
Copy link
Author

Has there been any news yet? We are still running into this issue unfortunately, while this issue persists and no workaround is available we cannot continue with the WebView2 adaptation.

@fvanheeswijk
Copy link
Author

I've got some extra information that may help with this problem. Currently we are running the EO (Essential Objects) webbrowser which is also Chromium-based just like the WebView2, when implementing it we ran into a similar issue with windows not appearing on the foreground but instead on the background.

Back then the developers have fixed it by calling AllowSetForegroundWindow from the webbrowser process such that it would allow the calling application (our executable) to put windows on top of windows from the webbrowser process. This may also be the case here and hopefully this will be of help to you. Because I don't think there's a reason why the program that includes the webbrowser may not be on top of said webbrowser.

I believe normally there is a link between threads and processes that would grant such permission but in the case of creating a new thread that link is lost so permission needs to be given on a global level.

Also, if it is possible to write our code in the browser process executable somewhere it could be of help as well, do you know any possibility regarding that?

@champnic
Copy link
Member

Thanks for the extra info, I've added it to the bug. We haven't been able to take a look at this particular bug yet, but I've just bumped the priority so hopefully we'll take a look soon.

@RussKie
Copy link

RussKie commented Apr 27, 2021

We want the opened form to be modeless, but with your code it becomes modal.

As per your original sample you're using newForm.ShowDialog(nativeWindow) which creates a modal dialog. If you want a modeless form then you need to use Show() method instead.

@RussKie
Copy link

RussKie commented Apr 27, 2021

windows not appearing on the foreground but instead on the background.

I think this is an expected behavior - you're launching a window on a background thread. Doing otherwise would result in a jarring user experience.
If you insist on launching new windows on non-main UI thread, then you'd likely need force the new window to the foreground with SetForegroundWindow

@maurawinstanley
Copy link
Contributor

There are a couple of workarounds/fixes:
You can override the OnShown method to call BringToFront():
image

If multiple forms are getting spawned and if needed, you can call the BringToFront() method when the new Thread is being created:
image

@champnic
Copy link
Member

champnic commented Apr 29, 2021

@fvanheeswijk Please give the above-mentioned fixes a try. I'm closing this issue for now as it sounds like it's By Design. Thanks!

@fvanheeswijk
Copy link
Author

@RussKie This behavior works normally, it is only when the WebView2 control is involved that it does not work.

@maurawinstanley This does seem to have some effect but not a consistent one. The first time you press the button to open a new form it does seem to work, but when you press it again it opens the forms behind the current window again. And even more peculiarly sometimes it does manage to open it in front again, so this does not seem a reliable workaround.

@champnic I'm still convinced the root of the issue can be resolved by calling AllowSetForegroundWindow from the browser process. I see no reason why you would not call it because the browser process is expected to be one with the application process, they run in the same window (visually) after all.
If there is a way to make the AllowSetForegroundWindow call myself I wouldn't mind either, but I don't know if it's possible to execute code in the browser process?

@champnic champnic reopened this Apr 30, 2021
@maurawinstanley
Copy link
Contributor

@fvanheeswijk Using the provided workaround triggered the new form to be created in the foreground for me every time (multiple times, I tried up to 20). Do you mind sharing how you are initializing the WebView?

@fvanheeswijk
Copy link
Author

This is the code (below) I am using @maurawinstanley, I had to add the KeyDown handler and press F8 because the NavigateToString doesn't work for me for some reason.

So I wait for the main form to open, press F8, then press on the 'Open new form' button then the new form always gets opened on top. After that I focus the original form again and repeat the same and then it opens under (most of the time).

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

using Microsoft.Web.WebView2.WinForms;

namespace WebView2BugMultipleForms
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
       [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(CreateTestForm());
        }

        public static Form CreateTestForm()
        {
            var form = new Form
            {
                Size = new Size(1280, 1024)
            };

            var webView2 = new WebView2
            {
                Dock = DockStyle.Fill,
                Source = new Uri(@"about:blank")
            };
            webView2.CoreWebView2InitializationCompleted += (sender, e) =>
            {
                webView2.CoreWebView2.AddHostObjectToScript("handler", new Handler(form));
                webView2.CoreWebView2.NavigateToString(
@"<html>
    <head>
        <title>Test</title>
    </head>
    <body>
        <button onclick='window.chrome.webview.hostObjects.sync.handler.openForm();'><h1>Open new form</h1></button>
    </body>
</html>");
            };
            webView2.KeyDown += (sender, e) =>
            {
                webView2.CoreWebView2.NavigateToString(
@"<html>
    <head>
        <title>Test</title>
    </head>
    <body>
        <button onclick='window.chrome.webview.hostObjects.sync.handler.openForm();'><h1>Open new form</h1></button>
    </body>
</html>");
            };

            form.Controls.Add(webView2);

            return form;
        }
    }

    [ComVisible(true)]
    public class Handler
    {
        private readonly Form owner;

        public Handler(Form owner)
        {
            this.owner = owner;
        }

        public void openForm()
        {
            var ownerHandle = owner.Handle;
            var thread = new Thread(() =>
            {
                var newForm = Program.CreateTestForm();
                newForm.Shown += (sender, e) => newForm.BringToFront();
                var nativeWindow = new NativeWindow();
                nativeWindow.AssignHandle(IntPtr.Zero);
                newForm.ShowDialog(nativeWindow);
            });
            thread.SetApartmentState(ApartmentState.STA);
            thread.Start();
        }
    }
}

I'm using the (newest) 1.0.865-prerelease version.

@maurawinstanley
Copy link
Contributor

@fvanheeswijk Can you try changing the newForm.Shown line to this:
newForm.Shown += (s, e) => newForm.Activate();

How are you setting up WebView2, do you call EnsureCoreWebView2Async?

@maurawinstanley
Copy link
Contributor

Hi @fvanheeswijk just following up one more time about this, if not, we will close out the issue!

@fvanheeswijk
Copy link
Author

@maurawinstanley We will try this workaround at a later point in time, however we still believe that calling AllowSetForegroundWindow is the correct course of action. So according to us this issue is not fixed, it should just work out of the box.

Currently we are running a different workaround that seemed to work but it fails if the newly opened window is maximized, hence why we do not want to rely on workarounds in a production application, we need a proper fix.

@fvanheeswijk
Copy link
Author

We have tried the suggested workaround in our actual application but cannot get it to work. We tried many things but none are successful.

We still kindly request Microsoft to ensure that the browser allows other windows to be stacked on top of it modelessly, because it visually is part of the process/application that hosts it.

@maurawinstanley
Copy link
Contributor

@fvanheeswijk We are looking into this, thanks for your patience!

@maurawinstanley
Copy link
Contributor

@fvanheeswijk Thanks for your patience, the fix should be in Canary later this week, closing this issue, feel free to re-open it if you find that the issue is not resolved.

@fvanheeswijk
Copy link
Author

@maurawinstanley Thank you! We can confirm that it works 😃

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

6 participants