WebView2Feedback icon indicating copy to clipboard operation
WebView2Feedback copied to clipboard

WebView2 html select dropdown positioning not updated

Open ispysoftware opened this issue 3 years ago • 43 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

ispysoftware avatar Mar 21 '22 01:03 ispysoftware

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 avatar Mar 21 '22 21:03 champnic

@champnic WinUI3

ispysoftware avatar Mar 21 '22 23:03 ispysoftware

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

ispysoftware avatar Jun 03 '22 03:06 ispysoftware

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

champnic avatar Jun 03 '22 18:06 champnic

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

ispysoftware avatar Aug 12 '22 05:08 ispysoftware

@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 avatar Aug 23 '22 14:08 TomOeser

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

ispysoftware avatar Aug 24 '22 00:08 ispysoftware

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!

Eilon avatar Sep 21 '22 22:09 Eilon

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 avatar Sep 22 '22 05:09 DrNoLife

@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

Brosinski avatar Sep 22 '22 07:09 Brosinski

Seems to work ok in WinUI3

ispysoftware avatar Sep 22 '22 07:09 ispysoftware

@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

Brosinski avatar Sep 26 '22 07:09 Brosinski

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. :)

TomOeser avatar Nov 01 '22 07:11 TomOeser

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.

Eilon avatar Nov 01 '22 16:11 Eilon

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
}

acrankyturtle avatar Nov 30 '22 02:11 acrankyturtle

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
}

Brosinski avatar Nov 30 '22 05:11 Brosinski

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.

Mathyn avatar Dec 07 '22 13:12 Mathyn

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.

acrankyturtle avatar Dec 24 '22 03:12 acrankyturtle

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?

ispysoftware avatar Dec 24 '22 03:12 ispysoftware

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?

champnic avatar Jan 06 '23 21:01 champnic

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.

ispysoftware avatar Jan 07 '23 01:01 ispysoftware

@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).

Mathyn avatar Jan 08 '23 10:01 Mathyn

@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.

TomOeser avatar Jan 08 '23 10:01 TomOeser

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...).

dady8889 avatar Jan 08 '23 21:01 dady8889

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?

champnic avatar Jan 09 '23 19:01 champnic

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

mikeduglas avatar Jan 09 '23 20:01 mikeduglas

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.

danroth27 avatar Jan 09 '23 21:01 danroth27

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

TomOeser avatar Jan 09 '23 23:01 TomOeser

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

Mathyn avatar Jan 24 '23 10:01 Mathyn

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.

johna-ms avatar Mar 02 '23 20:03 johna-ms