AvaloniaUI / Avalonia

Develop Desktop, Embedded, Mobile and WebAssembly apps with C# and XAML. The most popular .NET UI client technology
https://avaloniaui.net
MIT License
26.09k stars 2.26k forks source link

Using Winforms interop can't tab between Avalonia controls #12025

Open redrabbit007 opened 1 year ago

redrabbit007 commented 1 year ago

Describe the bug I'm using the WinFormsAvaloniaControlHost in a .net 7 WinForm app. The Avalonia view I'm hosting has 3 textboxes. I'm unable to use tab to move between them. Pressing tab moves between the WinFormsAvaloniaControlHost and the other WinForm controls. It doesn't tab between the 3 textboxes inside the Avalonia UserControl.

To Reproduce Steps to reproduce the behavior:

Create a new Avalonia Project. Add a couple TextBoxes to MainView.axaml:

<UserControl
  x:Class="AvaloniaProject.Views.MainView"
  xmlns="https://github.com/avaloniaui"
  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"
  xmlns:v="clr-namespace:AvaloniaProject.Views"
  xmlns:vm="clr-namespace:AvaloniaProject.ViewModels"
  d:DesignHeight="450"
  d:DesignWidth="800"
  x:DataType="vm:MainViewModel"
  mc:Ignorable="d">
  <Design.DataContext>
    <!--
      This only sets the DataContext for the previewer in an IDE,
      to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs)
    -->
    <vm:MainViewModel />
  </Design.DataContext>

  <Grid RowDefinitions="Auto,Auto,Auto,*">
    <TextBox Grid.Row="0" Width="150" />
    <TextBox Grid.Row="1" Width="150" />
    <TextBox Grid.Row="2" Width="150" />
  </Grid>
</UserControl>

Create a new WinForm project. Add a reference to the Avalonia project. Add the Avalonia.Win32.Interoperability package (11.0.0-rc1.1) Change Program.cs to:

using Avalonia;
using AvaloniaProject;

namespace AvaloniaTabTest
{
  internal static class Program
  {
    /// <summary>
    ///  The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
      // To customize application configuration such as set high DPI settings or default font,
      // see https://aka.ms/applicationconfiguration.
      ApplicationConfiguration.Initialize();

      AppBuilder.Configure<App>()
            .UsePlatformDetect()
            .UseWin32()
            .With(new Win32PlatformOptions
            {
              UseWindowsUIComposition = false
            })
            .LogToTrace()
            .SetupWithoutStarting();

      System.Windows.Forms.Application.Run(new Form1());
    }
  }
}

Add a WinFormsAvaloniaControlHost to Form1. In the Form1 code behind set the winFormsAvaloniaControlHost Content to the MainView.

using AvaloniaProject.Views;

namespace AvaloniaTabTest
{
  public partial class Form1 : Form
  {
    public Form1()
    {
      InitializeComponent();

      winFormsAvaloniaControlHost1.Content = new MainView();
    }
  }
}

Run the WinForm app and try to tab between the text boxes.

Expected behavior I expect to be able to tab between the controls in the Avalonia view as usual.

Desktop (please complete the following information):

Also, if I instead create a new MainView and call Show() I'm unable to enter text into the 3 textboxes.

redrabbit007 commented 1 year ago

I found a workaround. If I add an eventhandler to the PreviewKeyDown event on the WinFormsAvaloniaControlHost, check if the key is a tab and set IsInputKey to true then I can successfully tab between the TextBoxes inside the Avalonia Control.

private void winFormsAvaloniaControlHost1_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
  if (e.KeyData == Keys.Tab || e.KeyData == Keys.Left ||
    e.KeyData == Keys.Right || e.KeyData == Keys.Up ||
    e.KeyData == Keys.Down)
  {
    e.IsInputKey = true;
  }
}

I guess by default the WinForm app is taking the tab and handling it, instead of passing it on to the Avalonia Control.

A limitation of this workaround is that focus will become stuck inside the Avalonia Control and won't go back to the WinForm controls. If there is a way to check if the last control inside the Avalonia Control is focused then you could leave IsInputKey set to false to go back to the WinForm Contorls.