How to switch active window contain WebView2 from modal window?

Docking/MDI for WPF Forum

Posted 8 months ago by Yuki
Version: 21.1.3
Avatar

Hello,

I'm using WebView2 in content of DocumentWindow/ToolWindow.

When I tried to switch active window from modal window, below exception occured.

Would you please tell me the workaround?

Is this problem of WebView2 side?

Exception

System.ArgumentException
  HResult=0x80070057
  Message=Value does not fall within the expected range.
  Source=Microsoft.Web.WebView2.Core
  Stack trace:
   at Microsoft.Web.WebView2.Core.Raw.ICoreWebView2Controller.MoveFocus(COREWEBVIEW2_MOVE_FOCUS_REASON reason)
   at Microsoft.Web.WebView2.Core.CoreWebView2Controller.MoveFocus(CoreWebView2MoveFocusReason reason)
   at Microsoft.Web.WebView2.Wpf.WebView2.TabIntoCore(TraversalRequest request)
   at ActiproSoftware.Internal.NH.LXA(UIElement  )
   at ActiproSoftware.Internal.NH.aXR(UIElement  )
   at ActiproSoftware.Internal.NH.<>c__DisplayClass7_0.K88()
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.DispatcherOperation.InvokeImpl()
   at MS.Internal.CulturePreservingExecutionContext.CallbackWrapper(Object obj)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at MS.Internal.CulturePreservingExecutionContext.Run(CulturePreservingExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Windows.Threading.DispatcherOperation.Invoke()
   at System.Windows.Threading.Dispatcher.ProcessQueue()
   at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
   at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
   at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
   at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
   at System.Windows.Window.ShowHelper(Object booleanBox)
   at System.Windows.Window.ShowDialog()
   at ActiproSoftware.ProductSamples.DockingSamples.Demo.SimpleIde.MainControl.OnSubWindowMenuClick(Object sender, RoutedEventArgs e) in C:\Users\[User Name]\Documents\Actipro Software\WPF-Controls\v21.1.3\SampleBrowser\ProductSamples\DockingSamples\Demo\SimpleIde\MainControl.xaml.cs:line 606
   at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
   at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
   at System.Windows.Controls.MenuItem.InvokeClickAfterRender(Object arg)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.DispatcherOperation.InvokeImpl()
   at MS.Internal.CulturePreservingExecutionContext.CallbackWrapper(Object obj)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at MS.Internal.CulturePreservingExecutionContext.Run(CulturePreservingExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Windows.Threading.DispatcherOperation.Invoke()
   at System.Windows.Threading.Dispatcher.ProcessQueue()
   at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
   at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
   at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
   at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
   at System.Windows.Application.RunDispatcher(Object ignore)
   at System.Windows.Application.RunInternal(Window window)
   at ActiproSoftware.SampleBrowser.App.Main()

My sample source code

(based on SampleBrowser\ProductSamples\DockingSamples\Demo\SimpleIde)

MainControl.xaml(1)
- Add ToolWindow contains WebView2 to ToolWindowContainer.

<sampleBrowser:ProductItemControl
~~
    namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"
~~~~
    >

                        <docking:ToolWindowContainer>
                            <docking:ToolWindow
                                x:Name="WebView2ToolWindow"
                                Title="WebView2">
                                <wv2:WebView2 docking:InteropFocusTracking.IsEnabled="True" Source="https://www.microsoft.com" />

                            </docking:ToolWindow>
~~~~

SubWindow.xaml (Create new)

<Window
    x:Class="ActiproSoftware.ProductSamples.DockingSamples.Demo.SimpleIde.SubWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Title="SubWindow"
    Width="800"
    Height="450"
    mc:Ignorable="d">
    <Grid>
        <Button Click="Button_Click" Content="Active" />
    </Grid>
</Window>

SubWindow.xaml.cs (Create new)

using System;
using System.Windows;

namespace ActiproSoftware.ProductSamples.DockingSamples.Demo.SimpleIde
{
    /// <summary>
    /// SubWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class SubWindow : Window
    {
        public Action ActiveAction { get; set; }

        public SubWindow()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            ActiveAction?.Invoke();
        }
    }
}

MainControl.xaml(2)

- Add menu item to show SubWindow.

<Menu>
    <MenuItem Header="_File">
        <MenuItem Click="OnSubWindowMenuClick" Header="Sub Window" />

MainControl.xaml.cs

- Add event handler

private void OnSubWindowMenuClick(object sender, RoutedEventArgs e)
{
    var w = new SubWindow();
    w.ActiveAction = () => WebView2ToolWindow.IsActive = true;
    w.ShowDialog();
}

Comments (8)

Posted 8 months ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hello,

While it does seem like it could a bug in the WebView2 code, what happens when you set IsActive = true is that our code attempts to move keyboard focus within the activated docking window's content.  In this case, the content is the WebView2.  However you have a modal dialog open at the time too, so focus in theory cannot move into the WebView2 until that dialog is closed.  The exception might be their way of telling you via Win32 that it's an impossible operation. 

We will add a try...catch around the focus calls for the next maintenance release, but that doesn't solve the core issue, which is you shouldn't be calling IsActive = true when a modal dialog is displayed. 

You could use IsOpen = true instead in that scenario.  Or even call WebView2ToolWindow.Activate(focus: false) to open it and ensure the tab is selected, without focusing within the content.

I hope that helps!


Actipro Software Support

Posted 8 months ago by Yuki
Avatar

Hello,

Thank you for replying.

We will add a try...catch around the focus calls for the next maintenance release

This is good news for us.

I understood that ative window will be switched but keyboard focus will not be moved to WebView2 in next maintenance release.

Is this correct?

You could use IsOpen = true instead in that scenario.  Or even call WebView2ToolWindow.Activate(focus: false) to open it and ensure the tab is selected, without focusing within the content.

Thank you for this information. 

However, at the following case, the exception still occur.

  • 2 ToolWindow is displayed as tab. (ToolWindow-A is active, and ToolWindow-B contains WebView2)
  • Close ToolWindow-A from modal window.
    • ToolWindow-B is active automatically and exception occur.

I'm going to wait for next maintenance release.

Posted 8 months ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hello,

Well, we can't watch specifically for WebView2 (since it's not something our product references) so the same logic will occur as before but we will at least catch the exception raised by WebView2 so that no crash occurs and the end user will not notice any problems.

I just verified that closing the other tool window from the modal window also catches the exception with yesterday's code change.


Actipro Software Support

Posted 8 months ago by Yuki
Avatar

Hello,

Thanks a lot.

I have question and request.

  1. When can we get next maintenance release? Do you have any next release schedule?
  2. Is it possible you to ask Microsoft that this modification is properly? (Is there any other way to fix the exception?)
Posted 8 months ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hello,

We expect the maintenance release in the next several weeks.  If you'd like to test a preview build of it now with our updates to this point, please write our support address and mention this thread.

It's hard to say, but they may feel that raising a Win32 error is justified in that scenario since keyboard focus is not allowed to be moved to the control while a modal is displayed.  It's just unfortunate that this ends up manifesting itself in a .NET Exception instead of returning false from the focus request like other WPF Controls do.  You could ask about it in the WebView2 GitHub repo, and perhaps even mock up the scenario in a simple sample project with native controls only, which should be pretty simple to do.  They are likely to look into it more if you provide a sample showing it happening with native WPF controls only (no third party ones).

The only ways to fix this are to not try and focus the control when your modal is open, or catch the exception.  Please write us if you'd like the preview build to test.


Actipro Software Support

Posted 8 months ago by Yuki
Avatar

Hello,

I recieved preview build and confirmed that the exception don't occur.

Thank you very much.

You could ask about it in the WebView2 GitHub repo, and perhaps even mock up the scenario in a simple sample project with native controls only, which should be pretty simple to do.  They are likely to look into it more if you provide a sample showing it happening with native WPF controls only (no third party ones).

Yes, I understand this.

I have tried create sample project with native WPF controls only(TabControl), but I cannot reproduce the exception.

Is it possible to let me know internal process(source code) of "move keyboard focus within the activated docking window's content"?

Posted 8 months ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hello,

The focus handling code is fairly complex because it has to handle a lot of tricky scenarios.  In one place, if the target element is a HwndHost (interop), then we see if it implements IKeyboardInputSink and call sink.TabInto(new TraversalRequest(FocusNavigationDirection.First)).  That was one place it triggered the exception before.

In your sample, do you have a modal dialog open when you call your focus logic?  And try adding the IKeyboardSink check / focus too.  If you can't reproduce it with that, please write back to the support ticket from before and send us your sample project and we can have a look.  Be sure to exclude the bin/obj folders of what you send so it doesn't get spam blocked.  Thanks!


Actipro Software Support

Posted 8 months ago by Yuki
Avatar

Hello,

Thank you for replying.

In one place, if the target element is a HwndHost (interop), then we see if it implements IKeyboardInputSink and call sink.TabInto(new TraversalRequest(FocusNavigationDirection.First)).  That was one place it triggered the exception before.

Thanks to this information, I could reproduce the same exception with native WPF controls only.

The latest build of this product (v22.1.3) was released 17 days ago, which was after the last post in this thread.

Add Comment

Please log in to a validated account to post comments.