MicrosoftEdge / WebView2Feedback

Feedback and discussions about Microsoft Edge WebView2

Home Page:https://aka.ms/webview2

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

WebView2 html select dropdown positioning not updated

ispysoftware opened this issue · comments

Description
Drop-downs don't update their location when the application window is moved. Their location is updated when the application window is resized.

Version
SDK: 1.0.1
Framework: .Net 5.0, WinUI
OS: Win 11

Repro Steps
Start an application containing a WebView2 instance, trigger a drop-down. Move the application window, trigger dropdown again - dropdown shows in original location.

winuibug

Additional context

AB#38615382

Thanks for the bug report @ispysoftware, and sorry you're running into this. I've opened it on our backlog. Are you using WinUI 2 or WinUI 3 (through WindowsAppSDK)?

@champnic WinUI3

@champnic any update on this.. been over 2 months..

This is a bug on the WinUI control, and it looks like that team has begun work on a fix.

@champnic any chance you could chase this up - it's been holding us back since last year

@ispysoftware any news on that so you closed the issue?

I ran into the same issue with my WPF-App. Showing the dropdown right while in debug/release running from Visual Studio. As soon as i try to use it "from the outside" by starting the program in the output folders directly the dropdowns go off!

@TomOeser no, sorry, i think i just clicked the wrong button by accident

Thanks to Mike from the Windows App SDK team, we came up with a workaround for anyone running into this issue. The same type of workaround should work in a Windows App SDK (WinUI3) app, and also in a .NET MAUI app running on Windows.

The workaround is to cause the WebView2 to "update" itself when the app's window moves, which will cause any open dropdowns to close themselves (which is normal behavior for dropdowns in web browsers). This avoids the "floating dropdown" issue (because there's nothing to float).

In a Windows App SDK app you should be able to do something like this:

  1. In MainPage.xaml.cs change the constructor to hook up the AppWindow.Changed event:
        public MainWindow()
        {
            this.InitializeComponent();
    
            var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
            var windowId = Microsoft.UI.Win32Interop.GetWindowIdFromWindow(hWnd);
            var appWindow = Microsoft.UI.Windowing.AppWindow.GetFromWindowId(windowId);
            if (appWindow != null)
            {
                appWindow.Changed += AppWindow_Changed;
            }
        }
  2. Add a Changed event handler to force the WebView2 to update itself, which will cause the dropdown to close:
        private void AppWindow_Changed(Microsoft.UI.Windowing.AppWindow sender, Microsoft.UI.Windowing.AppWindowChangedEventArgs args)
        {
            if (args.DidPositionChange)
            {
                // Whenever the app's window moves, force the WebView2 to update, which will cause any open HTML select's to close (which is normal behavior), and thus not "float around"
                var origMargin = myWebView.Margin;
                myWebView.Margin = new Thickness(myWebView.Margin.Left + 1);
                myWebView.UpdateLayout(); // force the WebView to update its layout, and then immediately set it back
                myWebView.Margin = origMargin;
            }
        }

In a .NET MAUI app this similar should work:

  1. In MainPage.xaml give your BlazorWebView a name (if it doesn't already have one):
    <BlazorWebView HostPage="wwwroot/index.html" x:Name="bwv">
  2. In MainPage.xaml.cs add some code for the Windows platform to detect when the app's window is available, and hook up some events to force the WebView2 (used by BlazorWebView) to update itself, which causes any open dropdown list to close:
        public MainPage()
        {
            InitializeComponent();
        }
    
    #if WINDOWS
        bool _foundWindow;
    
        protected override void OnHandlerChanged()
        {
            base.OnHandlerChanged();
    
            if (!_foundWindow)
            {
                var window = GetParentWindow();
    
                if ((window?.Handler?.PlatformView as MauiWinUIWindow)?.GetAppWindow() is var appWindow)
                {
                    appWindow.Changed += AppWindow_Changed;
                    _foundWindow = true;
                }
            }
        }
    
        private void AppWindow_Changed(Microsoft.UI.Windowing.AppWindow sender, Microsoft.UI.Windowing.AppWindowChangedEventArgs args)
        {
            if (args.DidPositionChange)
            {
                // Whenever the app's window moves, force the WebView2 to update, which will cause any open HTML select's to close (which is normal behavior), and thus not "float around"
                var platformWebView = bwv.Handler?.PlatformView as Microsoft.UI.Xaml.Controls.WebView2;
                platformWebView.Width = platformWebView.Width;
            }
        }
    #endif

Please let us know if this helps you in the meantime, until a real fix can be provided. Thanks!

Just tried the proposed workaround for the .NET MAUI app, Eilon. But it didn't work.

Gave the webview a name, and copy pasted the rest of the code over into MainPage.xaml.cs, however it changed nothing. The bug still persists, at least for MAUI apps.

Gif of the bug still persisting

Edit.

Using the code from the Windows App SDK version (for the .cs file) seems to work. It's a bit funky however, as each movement causes the application to blink which is accompanied with a loading indicator.

Gif of the behaviour described above

@DrNoLife I also tried the code for .NET MAUI BlazorWebView .and it didn't work.

I did find a semi-reliable way to correct the behaviour in .NET MAUI Blazor by adapting the code provided by @Eilon a little.

EDIT : When I say semi-reliable, I'm talking like 30-40% of the time. Moving the window too fast seems to cause some issues as well. Oh well, better than broken

int reloadLimiter =0;
private void AppWindow_Changed(Microsoft.UI.Windowing.AppWindow sender, Microsoft.UI.Windowing.AppWindowChangedEventArgs args)
    {

      
        if (args.DidPositionChange)
        {
            // Whenever the app's window moves, force the WebView2 to update, which will cause any open HTML select's to close (which is normal behavior), and thus not "float around"
            var platformWebView = bwv.Handler?.PlatformView as Microsoft.UI.Xaml.Controls.WebView2;
            platformWebView.Width = (platformWebView.Width);
            if (reloadLimiter == 10)
            {
                platformWebView.UpdateLayout();

                reloadLimiter=0;
            }
            else{
                reloadLimiter++;
            }
            
            }
       
    }
}

I'm not too sure if UpdateLayout() will cause any unknown issues.

I couldn't find an event for after the window position has been changed . There is a mention in the docs of UpdateLayout() causing performance issues if overused, which in this case it probably is.

EDIT: I added the reloadLimiter count to just reduce the amount UpdateLayout() is called as it is dragged.

https://learn.microsoft.com/en-us/windows/windows-app-sdk/api/winrt/microsoft.ui.xaml.uielement.updatelayout?view=windows-app-sdk-1.1#microsoft-ui-xaml-uielement-updatelayout

Seems to work ok in WinUI3

@DrNoLife So I found a workaround that works 100% of the time for me with .NET MAUI Blazor

It is the same as @Eilon with the exception that it is targetting the actual window needed to trigger a change in the selects position

MainPage.xaml.cs

public MainPage()
	{
		InitializeComponent();
	}
	#if WINDOWS
    bool _foundWindow;

    protected override void OnHandlerChanged()
    {
        base.OnHandlerChanged();

        if (!_foundWindow)
        {
            var window = GetParentWindow();

            if ((window?.Handler?.PlatformView as MauiWinUIWindow)?.GetAppWindow() is var appWindow)
            {
                appWindow.Changed += AppWindow_Changed;
                _foundWindow = true;
            }
        }
    }

    private void AppWindow_Changed(Microsoft.UI.Windowing.AppWindow sender, Microsoft.UI.Windowing.AppWindowChangedEventArgs args)
    {

      
        if (args.DidPositionChange)
        {
            // Whenever the app's window moves, force the WebView2 to update, which will cause any open HTML select's to close (which is normal behavior), and thus not "float around"
            var platformWebView = bwv.Handler?.PlatformView as Microsoft.UI.Xaml.Controls.WebView2;
     
            
                this.WidthRequest = this.Width + 1;
				this.HeightRequest= this.Height + 1;
				this.WidthRequest = sender.Size.Width;
				this.HeightRequest = sender.Size.Height;
				
			
           
            
            }
        else if (args.DidSizeChange)
        {
			this.WidthRequest = sender.Size.Width;
			this.HeightRequest = sender.Size.Height;
		}
       
    }
    
#endif

The change is not registered if you don't make an actual change to WidthRequest or HeightRequest, hence the increment.

Thank you @Eilon, saved my sanity

Thanks for the workaround. However, I have now ended up with a self-developed dropdown web component, as the design and functional possibilities of the HTML select element are very limited.
:)

Thanks for the workaround. However, I have now ended up with a self-developed dropdown web component, as the design and functional possibilities of the HTML select element are very limited. :)

Indeed, there are many scenarios that are not supported by an HTML <select>, and using an HTML-based solution would allow you to create a more suitable custom UI.

I have this problem using .NET 7 MAUI Blazor and the above workarounds did not resolve my issue. This, however, works for me. This is MainPage.xaml.cs.

using Microsoft.Maui.Platform;

namespace Controller.Application;

public partial class MainPage : ContentPage
{
	public MainPage()
	{
		InitializeComponent();
	}

#if WINDOWS
	bool _foundWindow;

	protected override void OnHandlerChanged()
	{
		base.OnHandlerChanged();

		if (!_foundWindow)
		{
			var window = GetParentWindow();

			if ((window?.Handler?.PlatformView as MauiWinUIWindow)?.GetAppWindow() is var appWindow)
			{
				appWindow.Changed += AppWindow_Changed;
				_foundWindow = true;
			}
		}
	}

	private void AppWindow_Changed(Microsoft.UI.Windowing.AppWindow sender, Microsoft.UI.Windowing.AppWindowChangedEventArgs args)
	{
		if (args.DidPositionChange)
		{
			var width = this.Window.Width;

			this.Window.Width = width + 1;
			this.Window.Width = width;
		}

	}
#endif
}

I have this problem using .NET 7 MAUI Blazor and the above workarounds did not resolve my issue. This, however, works for me. This is MainPage.xaml.cs.

That would only update the select position when moving the screen, resizing the screen would still cause the error. Please refer to my solution above or below
MainPage.xaml.cs

using Microsoft.Maui.Platform;

namespace Controller.Application;

public partial class MainPage : ContentPage
{
	public MainPage()
	{
		InitializeComponent();
	}

#if WINDOWS
	bool _foundWindow;

	protected override void OnHandlerChanged()
	{
		base.OnHandlerChanged();

		if (!_foundWindow)
		{
			var window = GetParentWindow();

			if ((window?.Handler?.PlatformView as MauiWinUIWindow)?.GetAppWindow() is var appWindow)
			{
				appWindow.Changed += AppWindow_Changed;
				_foundWindow = true;
			}
		}
	}

	private void AppWindow_Changed(Microsoft.UI.Windowing.AppWindow sender, Microsoft.UI.Windowing.AppWindowChangedEventArgs args)
	{
		if (args.DidPositionChange)
		{
			var width = this.Window.Width;

			this.Window.Width = width + 1;
			this.Window.Width = width;
		} 
           else if (args.DidSizeChange)
                {
			this.WidthRequest = sender.Size.Width;
			this.HeightRequest = sender.Size.Height;
		}

	}
#endif
}

Is the work around shared in this issue still working properly? When I use it I find that if I already have the dropdown opened and I move the window the dropdown does not move with the window.

After closing and reopening the dropdown it does show up in the right location.

One of the comments in the work around says

// Whenever the app's window moves, force the WebView2 to update, which will cause any open HTML select's to close (which is normal behavior), and thus not "float around"

However this does not appear to be true (at least on my system), the dropdowns remain open. This is for a Windows 10 system.

Is the work around shared in this issue still working properly? When I use it I find that if I already have the dropdown opened and I move the window the dropdown does not move with the window.

After closing and reopening the dropdown it does show up in the right location.

One of the comments in the work around says

// Whenever the app's window moves, force the WebView2 to update, which will cause any open HTML select's to close (which is normal behavior), and thus not "float around"

However this does not appear to be true (at least on my system), the dropdowns remain open. This is for a Windows 10 system.

That workaround did not work for me either. I am also on Windows 10. I posted a workaround above that did work for me.

I'm finding it hard to believe that this issue is still open and not resolved yet. It's a major bug and it's been there for almost a year. It's not very reassuring to develop on these platforms when bugs like this are just left unresolved for extended periods of time.

@champnic - could we please get a proper resolution to this major issue?

This is high on our list and we hope to investigate and get a fix out soon. Is this a common scenario for folks, to open a select and then resize or move a window without dismissing or interacting with the select dropdown? ie. did you find this in manual testing, or are you hearing it from lots of end users?

You don't need to not dismiss it, you just need to have opened it once, then you can dismiss it, move the window, open it again and the drop-down overlay is locked in it's original position.

I've got no feedback about user experience as this issue has prevented us from ever deploying the WinUI version of our application - it makes it look like a broken mess. Also we're stuck on "preview" nuget packages which is preventing us from even uploading the application to the store.

We also have an ongoing issue with WinUI somehow interfering with our native threading code (some incompatibility with pthread and windows threads we suspect) which intermittently hangs the webrtc code side of our application.

@champnic We are hearing about this issue from our end users. On top of that like @ispysoftware mentioned this makes the application look messy and unprofessional.

On our side we have decided to switch to a HTML replacement for the dropdown which solved the issue for us. None of the work arounds found online really worked in all cases for us (those which fixed it causes other issues).

@champnic
Simply put, what do users who are used to the correct behaviour of a select from everyday browsing expect? Correct - it should appear in the right position. It should be displayed correctly. It should be moved with the window and then again displayed or opened at the correct position. It must comprehensively cover the expected functionality. Such elementary errors leave the end customer with the stale aftertaste: "My goodness, these software developers can't get it right, but they also demand money from us in return." The rest of the software can be as good as it wants to be, customers quickly get upset by something like this - and that's all that gets noticed from then on. "Your software works, we save time and money, but your dropdowns - no way!

As a developer, you assume that a dropdown behaves as you expect it to.

Please address this problem. I'm using my own solution now, but I'm thinking of the novice developers who are just starting out.

I had a similar problem with WebView in MAUI Hybrid Blazor app, when opening and using a color picker.
Thankfully, I stumbled upon this thread and was able to combine my solution with the code available here.

The code already presented to fix this issue has awful performance, as the redraw happens on every move message. I was able to optimize it as follows:

            builder.ConfigureLifecycleEvents(events =>
            {
#if WINDOWS
                events.AddWindows(windows => windows
                       .OnPlatformMessage((window, args) =>
                       {
                           // force redraw of webview => causes all popups to close
                           if (args.MessageId == 561) // WM_ENTERSIZEMOVE
                           {
                               var mauiWindow = window.GetWindow();
                               if (mauiWindow != null)
                               {
                                   var blazorWebView = (mauiWindow.Content as MainPage)?.Content as BlazorWebView;
                                   if (blazorWebView != null)
                                   {
                                       var platformWebView = blazorWebView.Handler?.PlatformView as WebView2;
                                       if (platformWebView != null)
                                       {
                                           var margin = platformWebView.Margin;
                                           platformWebView.Margin = new Microsoft.UI.Xaml.Thickness(platformWebView.Margin.Left + 1);
                                           platformWebView.UpdateLayout();
                                           platformWebView.Margin = margin;
                                       }
                                   }
                               }

                               // System.Diagnostics.Debug.WriteLine($"WM_ENTERSIZEMOVE");
                           }

                           // force resize => causes popups to show at the correct location
                           else if (args.MessageId == 562) // WM_EXITSIZEMOVE
                           {
                               var windowsWindow = window.GetAppWindow();
                               if (windowsWindow != null)
                               {
                                   var size = windowsWindow.Size;
                                   windowsWindow.Resize(new Windows.Graphics.SizeInt32(size.Width + 1, size.Height));
                                   windowsWindow.Resize(new Windows.Graphics.SizeInt32(size.Width, size.Height));
                               }
                               
                               // System.Diagnostics.Debug.WriteLine($"WM_EXITSIZEMOVE");
                           }
                       })
                );
#endif
});

Add this code into your CreateMauiApp builder in the MauiProgram class to use it.
You can uncomment the Debug.WriteLine, and see that it will be called only at move start/end.
This code works correctly for the color picker on MAUI, I believe it will also work for any other popups.

I would just add, that the issue is not that users open and then move the window (even though it still should close the popup or move the popup along with the window). The popup will never move from the initial position unless the window is resized, even after opening/closing of the popup, which is a major bug (but this may be for the MAUI team and not WebView...).

Thanks for the info all! I was under the impression this only happened when the dropdown was kept open. For example, @Mathyn reports:

After closing and reopening the dropdown it does show up in the right location.

Whereas it sounds like this also happens with incorrect positioning even when the dropdown has been dismissed and reopened. For those seeing this behavior, just want to confirm whether you are seeing this on WinUI 2 or WinUI 3 (WindowsAppSDK) or other?

commented

@champnic I see this behavior on WinForms (SDK 1.0.1466.0) and 111.0.1605.0 canary runtime.

just want to confirm whether you are seeing this on WinUI 2 or WinUI 3 (WindowsAppSDK) or other?

@champnic The .NET MAUI uses WinUI 3 under the covers.

@champnic I see this behavior on WPF (SDK current version, current canary)

@champnic I see this behavior using .NET Maui (so WinUI3).

Hi all. I'm the dev that's most recently looked at this issue. The problem of the select dropdown reopening in a stale location looks to be an issue with the control not calling the NotifyParentWindowPositionUpdated API. This is unfortunately not a publicly exposed API for Maui. I believe this would be an issue on WinUI3 so please file one with them.

The problem of the select dropdown not dismissing on click+drag remains legit and on us.

I didn't notice that a workaround was already provided by WinApp folks above about a year ago. I'm seeing that the remaining commenters are finding the workaround insufficient. Could commenters get specific about issues with the workaround are so we can work with WinUI to see if they can be addressed?

@ispysoftware @Mathyn

commented

Work around seems OK here. Seems some people above are having performance issues with it - I guess if hacking at the browser margin to force a complete redraw and thereby collapse a select dropdown on movement is good enough for Microsoft then it's good enough for us. Doesn't sit well with my OCD.

We are also experiencing it in WPF. For those running with the workaround, has anyone felt an impact on performance from rerendering when dragging / min-maxing?

This issue does not seem to be platform-specific, because we are using CoreWebView2 directly through C++/WinRT (previously Win32) without any framework wrappers (WPF/WinUI/etc.) in our MFC application and still run into the dropdown-position not updating when moving the window while the dropdown is open.

SDK Version: 1.0.1901.177 (latest)
Runtime Version: 115.0.1901.203 (latest stable evergreen)

@Optimierungswerfer this can happen if you are not calling NotifyParentWindowPositionChanged when the parent window position changes. Are you calling this API?

Yes, we are calling NotifyParentWindowPositionChanged whenever the parent window moves. We do that to explicitly fix the issue with stuck/floating elements like the search bar (Ctrl+F) or the download pop-up. However, open HTML drop-down menus being stuck are surprisingly not fixed by this.

Actually, the place where we call NotifyParentWindowPositionChanged was exactly where I also tried the resize-workaround that is suggested here. That causes noticeable jittering when moving the window though, which we do not want, so the workaround is no option for us.

commented

@dady8889 Thanks for this. Only solution that works properly and to the top of it solves the lagging/performance issue.
Solutions above unfortunately never closed the dropdown, and changing WidthRequest/HeightRequest also somehow messed up the styling.

For those who do not like nested ifs, here is a compacted version:

if (args.MessageId == WM_ENTERSIZEMOVE)
   if (window.GetWindow()?.Content is MainPage { Content: BlazorWebView { Handler.PlatformView: WebView2 platformWebView  } })
   ...

@champnic Is this ever going to be fixed?
The workarounds work but have some serious side effects - we're playing video and now when the window is moved the video flickers badly because of the forced redraw. We're having to tell our users that it's because Microsoft don't fully support Select elements in their web browser control which is embarrassing all round (and also they find hard to believe).

Almost half way through 2024 and this issue still persists.

@ispysoftware @CorbynBMG it's been mentioned earlier, but the fix for the issue of select dropdown positioning that would resolve the performance issues is actually work on the WinUI team microsoft-ui-xaml. I don't see an open github issue on their side tracking it. This one was opened recently though and that appears to be the same issue. Please let them know and tag this issue so they can take your feedback into consideration regarding priority.

@Optimierungswerfer I've tried to repro the select dropdown positioning in a local C++/WinRT app but I cannot. Please let me know what you are doing or link a min-repro sample app so I can help diagnose what's going on

@johna-ms This issue has been open for over 2 years - it's a generic select element not working properly in your web browser control. If that's not a critical bug i'm not sure what is. This isn't some rare edge case thing.

Someone at microsoft needs to take ownership of it and get it fixed instead of telling people to log cases elsewhere.

@johna-ms @ispysoftware @CorbynBMG

I agree that it's insane that this is not consider a showstopper type bug. I am using Photino and was baffled when I ran into this.

I just gave up and moved on. I switched to select2. It is an upgrade and works without any issues at all. select2 is not perfect and there are other options (Tom Select, Selectize.js, Chosen (deprecated), Choices.js), but I have history with select2.

I am just following this issue because of the trainwreck it is. I agree with ispysoftware, the finger pointing and buck passing it crazy.

But this is the world we live in now. If you build something using a component, and that component has a bug, no matter how big and bad it is for you, it might not even be a use case that's considered by owner.