question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

How to inject a VirtualKeyboard

See original GitHub issue

I have made simple VirtualKeyboard.

In my case I would like to as soon as the TextBox receives the InputFocus, transfer the Text of the TextBox to the VirtualKeyboard and open it in a separate Window (RaspberryPie currently only supports a single window). This is finally closed with Return and transferred to the TextBox.

I would like to avoid having to create my own CustomTextbox that the VirtualKeyboard opens automatically.

keyboard

Now I would like to combine it with avaloniaui. As you already mentioned here https://github.com/AvaloniaUI/Avalonia/issues/3415 it should be possible. But actually I’m a little bit stuck and don’t know how to go on.

The events TextInputOptionsQuery and TextInputMethodClientRequested are not called.

var boxes = this.GetLogicalDescendants().OfType<TextBox>().ToList();
foreach (var box in boxes)
{
	box.TextInputOptionsQuery += (o, eventArgs) =>
	{
		// is not called
	};
	box.TextInputMethodClientRequested += (o, eventArgs) =>
	{
		// is not called                        
	};
}

As soon as the VirtualKeyboard works, I will share my code so that it may even be included in the repo or to improve it.

Issue Analytics

  • State:open
  • Created 2 years ago
  • Comments:13 (2 by maintainers)

github_iconTop GitHub Comments

9reactions
dojo90commented, Aug 5, 2022

keyboard2

For those who also want to implement a virtual keyboard, here is the source code.

Improvements are always welcome


grafik

Layout/KeyboardLayout.cs
public abstract class KeyboardLayout : UserControl
{
    string LayoutName { get; }
}
Layout/VirtualKeyboardLayoutDE.axaml
public partial class VirtualKeyboardLayoutDE : KeyboardLayout
{
    public VirtualKeyboardLayoutDE()
    {
        InitializeComponent();
    }

    private void InitializeComponent()
    {
        AvaloniaXamlLoader.Load(this);
    }

    public string LayoutName => "de-DE";
}
<UserControl 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:keyboard="clr-namespace:HMI.Infrastructure.Keyboard"
             xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
             mc:Ignorable="d" d:DesignWidth="800"
             x:Class="HMI.Infrastructure.Keyboard.Layout.VirtualKeyboardLayoutDE">
  <UserControl.Resources>
    <keyboard:VirtualKeyWidthMultiplayer x:Key="VirtualKeyWidthMultiplayer"/>
  </UserControl.Resources>
  <UserControl.Styles>
    <Style Selector="keyboard|VirtualKey">
      <Setter Property="Width" Value="45"/>
      <Setter Property="Height" Value="45"/>
    </Style>
  </UserControl.Styles>
  <StackPanel>
    <StackPanel Orientation="Horizontal">
      <keyboard:VirtualKey NormalKey="^" ShiftKey="°" AltCtrlKey="" Name="VirtualKey1"/>
      <keyboard:VirtualKey NormalKey="1" ShiftKey="!" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="2" ShiftKey="&quot;" AltCtrlKey="²" />
      <keyboard:VirtualKey NormalKey="3" ShiftKey="§" AltCtrlKey="³" />
      <keyboard:VirtualKey NormalKey="4" ShiftKey="$" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="5" ShiftKey="%" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="6" ShiftKey="&amp;" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="7" ShiftKey="/" AltCtrlKey="{}{" />
      <keyboard:VirtualKey NormalKey="8" ShiftKey="(" AltCtrlKey="[" />
      <keyboard:VirtualKey NormalKey="9" ShiftKey=")" AltCtrlKey="]" />
      <keyboard:VirtualKey NormalKey="0" ShiftKey="=" AltCtrlKey="}" />
      <keyboard:VirtualKey NormalKey="ß" ShiftKey="?" AltCtrlKey="\" />
      <keyboard:VirtualKey NormalKey="´" ShiftKey="`" AltCtrlKey=""/>
      <keyboard:VirtualKey SpecialKey="Back" SpecialIcon="KeyboardBackspace" Width="{Binding #VirtualKey1.Bounds.Width, Converter={StaticResource VirtualKeyWidthMultiplayer}, ConverterParameter=20}"/>
    </StackPanel>
    <StackPanel Orientation="Horizontal">
      <keyboard:VirtualKey SpecialKey="Tab" SpecialIcon="KeyboardTab" Width="{Binding #VirtualKey1.Bounds.Width, Converter={StaticResource VirtualKeyWidthMultiplayer}, ConverterParameter=15}"/>
      <keyboard:VirtualKey NormalKey="q" ShiftKey="Q" AltCtrlKey="@" />
      <keyboard:VirtualKey NormalKey="w" ShiftKey="W" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="e" ShiftKey="E" AltCtrlKey="€" />
      <keyboard:VirtualKey NormalKey="r" ShiftKey="R" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="t" ShiftKey="T" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="z" ShiftKey="Z" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="u" ShiftKey="U" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="i" ShiftKey="I" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="o" ShiftKey="O" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="p" ShiftKey="P" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="ü" ShiftKey="Ü" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="+" ShiftKey="*" AltCtrlKey="~"/>
      <keyboard:VirtualKey SpecialKey="Enter" SpecialIcon="KeyboardReturn" Margin="0,0,0,-45" Height="90" Width="{Binding #VirtualKey1.Bounds.Width, Converter={StaticResource VirtualKeyWidthMultiplayer}, ConverterParameter=15}">
      </keyboard:VirtualKey>
    </StackPanel>
    <StackPanel Orientation="Horizontal">
      <keyboard:VirtualKey SpecialKey="CapsLock" SpecialIcon="KeyboardCapslock" Width="{Binding #VirtualKey1.Bounds.Width, Converter={StaticResource VirtualKeyWidthMultiplayer}, ConverterParameter=18}"/>
      <keyboard:VirtualKey NormalKey="a" ShiftKey="A" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="s" ShiftKey="S" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="d" ShiftKey="D" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="f" ShiftKey="F" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="g" ShiftKey="G" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="h" ShiftKey="H" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="j" ShiftKey="J" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="k" ShiftKey="K" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="l" ShiftKey="L" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="ö" ShiftKey="Ö" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="ä" ShiftKey="Ä" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="#" ShiftKey="'" AltCtrlKey="" />
    </StackPanel>
    <StackPanel Orientation="Horizontal">
      <keyboard:VirtualKey SpecialKey="LeftShift" SpecialIcon="AppleKeyboardShift" Width="{Binding #VirtualKey1.Bounds.Width, Converter={StaticResource VirtualKeyWidthMultiplayer}, ConverterParameter=15}"/>
      <keyboard:VirtualKey NormalKey="&lt;" ShiftKey="&gt;" AltCtrlKey="|" />
      <keyboard:VirtualKey NormalKey="y" ShiftKey="Y" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="x" ShiftKey="X" AltCtrlKey="³" />
      <keyboard:VirtualKey NormalKey="c" ShiftKey="C" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="v" ShiftKey="V" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="b" ShiftKey="B" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="n" ShiftKey="N" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="m" ShiftKey="M" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="," ShiftKey=";" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="." ShiftKey=":" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="-" ShiftKey="_" AltCtrlKey="" />
      <keyboard:VirtualKey SpecialKey="RightShift" SpecialIcon="AppleKeyboardShift" Width="{Binding #VirtualKey1.Bounds.Width, Converter={StaticResource VirtualKeyWidthMultiplayer}, ConverterParameter=25}">
      </keyboard:VirtualKey>
    </StackPanel>
    <StackPanel Orientation="Horizontal">
      <keyboard:VirtualKey SpecialKey="Home" SpecialIcon="KeyboardTabReverse" Width="{Binding #VirtualKey1.Bounds.Width, Converter={StaticResource VirtualKeyWidthMultiplayer}, ConverterParameter=12}"/>
      <keyboard:VirtualKey SpecialKey="Left" SpecialIcon="ChevronLeft" Width="{Binding #VirtualKey1.Bounds.Width, Converter={StaticResource VirtualKeyWidthMultiplayer}, ConverterParameter=12}"/>
      <keyboard:VirtualKey SpecialKey="Right" SpecialIcon="ChevronRight" Width="{Binding #VirtualKey1.Bounds.Width, Converter={StaticResource VirtualKeyWidthMultiplayer}, ConverterParameter=12}"/>
      <keyboard:VirtualKey SpecialKey="End" SpecialIcon="KeyboardTab" Width="{Binding #VirtualKey1.Bounds.Width, Converter={StaticResource VirtualKeyWidthMultiplayer}, ConverterParameter=12}"/>
      <keyboard:VirtualKey NormalKey=" " Width="{Binding #VirtualKey1.Bounds.Width, Converter={StaticResource VirtualKeyWidthMultiplayer}, ConverterParameter=57}"/>
      <keyboard:VirtualKey SpecialKey="RightAlt" SpecialIcon="AppleKeyboardControl" Width="{Binding #VirtualKey1.Bounds.Width, Converter={StaticResource VirtualKeyWidthMultiplayer}, ConverterParameter=15}"/>
      <keyboard:VirtualKey SpecialKey="Clear" SpecialIcon="DeleteForever" Width="{Binding #VirtualKey1.Bounds.Width, Converter={StaticResource VirtualKeyWidthMultiplayer}, ConverterParameter=15}"/>
      <keyboard:VirtualKey SpecialKey="Help" SpecialIcon="Translate" Width="{Binding #VirtualKey1.Bounds.Width, Converter={StaticResource VirtualKeyWidthMultiplayer}, ConverterParameter=15}"/>
    </StackPanel>
  </StackPanel>
</UserControl>

Layout/VirtualKeyboardLayoutUS.axaml
public partial class VirtualKeyboardLayoutUS : KeyboardLayout
{
    public VirtualKeyboardLayoutUS()
    {
        InitializeComponent();
    }

    private void InitializeComponent()
    {
        AvaloniaXamlLoader.Load(this);
    }

    public string LayoutName => "en-US";
}
<UserControl 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:keyboard="clr-namespace:HMI.Infrastructure.Keyboard"
             xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
             mc:Ignorable="d" d:DesignWidth="800"
             x:Class="HMI.Infrastructure.Keyboard.Layout.VirtualKeyboardLayoutUS">
  <UserControl.Resources>
    <keyboard:VirtualKeyWidthMultiplayer x:Key="VirtualKeyWidthMultiplayer" />
  </UserControl.Resources>
  <UserControl.Styles>
    <Style Selector="keyboard|VirtualKey">
      <Setter Property="Width" Value="45" />
      <Setter Property="Height" Value="45" />
    </Style>
  </UserControl.Styles>
  <StackPanel>
    <StackPanel Orientation="Horizontal">
      <keyboard:VirtualKey NormalKey="`" ShiftKey="~" AltCtrlKey="" Name="VirtualKey1" />
      <keyboard:VirtualKey NormalKey="1" ShiftKey="!" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="2" ShiftKey="@" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="3" ShiftKey="#" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="4" ShiftKey="$" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="5" ShiftKey="%" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="6" ShiftKey="^" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="7" ShiftKey="&amp;" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="8" ShiftKey="*" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="9" ShiftKey="(" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="0" ShiftKey=")" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="-" ShiftKey="_" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="=" ShiftKey="+" AltCtrlKey="" />
      <keyboard:VirtualKey SpecialKey="Back" SpecialIcon="KeyboardBackspace" Width="{Binding #VirtualKey1.Bounds.Width, Converter={StaticResource VirtualKeyWidthMultiplayer}, ConverterParameter=20}" />
    </StackPanel>
    <StackPanel Orientation="Horizontal">
      <keyboard:VirtualKey SpecialKey="Tab" SpecialIcon="KeyboardTab"
                           Width="{Binding #VirtualKey1.Bounds.Width, Converter={StaticResource VirtualKeyWidthMultiplayer}, ConverterParameter=15}" />
      <keyboard:VirtualKey NormalKey="q" ShiftKey="Q" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="w" ShiftKey="W" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="e" ShiftKey="E" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="r" ShiftKey="R" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="t" ShiftKey="T" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="y" ShiftKey="Y" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="u" ShiftKey="U" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="i" ShiftKey="I" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="o" ShiftKey="O" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="p" ShiftKey="P" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="[" ShiftKey="{}{" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="]" ShiftKey="}" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="\" ShiftKey="|" AltCtrlKey=""
                           Width="{Binding #VirtualKey1.Bounds.Width, Converter={StaticResource VirtualKeyWidthMultiplayer}, ConverterParameter=15}" />
    </StackPanel>
    <StackPanel Orientation="Horizontal">
      <keyboard:VirtualKey SpecialKey="CapsLock" SpecialIcon="KeyboardCapslock"
                           Width="{Binding #VirtualKey1.Bounds.Width, Converter={StaticResource VirtualKeyWidthMultiplayer}, ConverterParameter=18}" />
      <keyboard:VirtualKey NormalKey="a" ShiftKey="A" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="s" ShiftKey="S" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="d" ShiftKey="D" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="f" ShiftKey="F" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="g" ShiftKey="G" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="h" ShiftKey="H" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="j" ShiftKey="J" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="k" ShiftKey="K" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="l" ShiftKey="L" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey=";" ShiftKey=":" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="'" ShiftKey="&quot;" AltCtrlKey="" />
      <keyboard:VirtualKey SpecialKey="Enter" SpecialIcon="KeyboardReturn" Width="{Binding #VirtualKey1.Bounds.Width, Converter={StaticResource VirtualKeyWidthMultiplayer}, ConverterParameter=22}"/>
    </StackPanel>
    <StackPanel Orientation="Horizontal">
      <keyboard:VirtualKey SpecialKey="LeftShift" SpecialIcon="AppleKeyboardShift"
                           Width="{Binding #VirtualKey1.Bounds.Width, Converter={StaticResource VirtualKeyWidthMultiplayer}, ConverterParameter=25}" />
      <keyboard:VirtualKey NormalKey="z" ShiftKey="Z" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="x" ShiftKey="X" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="c" ShiftKey="C" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="v" ShiftKey="V" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="b" ShiftKey="B" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="n" ShiftKey="N" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="m" ShiftKey="M" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="," ShiftKey="&lt;" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="." ShiftKey=">" AltCtrlKey="" />
      <keyboard:VirtualKey NormalKey="/" ShiftKey="?" AltCtrlKey="" />
      <keyboard:VirtualKey SpecialKey="RightShift" SpecialIcon="AppleKeyboardShift"
                           Width="{Binding #VirtualKey1.Bounds.Width, Converter={StaticResource VirtualKeyWidthMultiplayer}, ConverterParameter=25}" />
    </StackPanel>
    <StackPanel Orientation="Horizontal">
      <keyboard:VirtualKey SpecialKey="Home" SpecialIcon="KeyboardTabReverse"
                           Width="{Binding #VirtualKey1.Bounds.Width, Converter={StaticResource VirtualKeyWidthMultiplayer}, ConverterParameter=12}" />
      <keyboard:VirtualKey SpecialKey="Left" SpecialIcon="ChevronLeft"
                           Width="{Binding #VirtualKey1.Bounds.Width, Converter={StaticResource VirtualKeyWidthMultiplayer}, ConverterParameter=12}" />
      <keyboard:VirtualKey SpecialKey="Right" SpecialIcon="ChevronRight"
                           Width="{Binding #VirtualKey1.Bounds.Width, Converter={StaticResource VirtualKeyWidthMultiplayer}, ConverterParameter=12}" />
      <keyboard:VirtualKey SpecialKey="End" SpecialIcon="KeyboardTab"
                           Width="{Binding #VirtualKey1.Bounds.Width, Converter={StaticResource VirtualKeyWidthMultiplayer}, ConverterParameter=12}" />
      <keyboard:VirtualKey NormalKey=" "
                           Width="{Binding #VirtualKey1.Bounds.Width, Converter={StaticResource VirtualKeyWidthMultiplayer}, ConverterParameter=71}" />
      <keyboard:VirtualKey SpecialKey="Clear" SpecialIcon="DeleteForever"
                           Width="{Binding #VirtualKey1.Bounds.Width, Converter={StaticResource VirtualKeyWidthMultiplayer}, ConverterParameter=15}" />
      <keyboard:VirtualKey SpecialKey="Help" SpecialIcon="Translate"
                           Width="{Binding #VirtualKey1.Bounds.Width, Converter={StaticResource VirtualKeyWidthMultiplayer}, ConverterParameter=15}" />
    </StackPanel>
  </StackPanel>
</UserControl>
VirtualKey.axaml
public class VirtualKey : TemplatedControl
    {
        public static readonly StyledProperty<ICommand> ButtonCommandProperty = AvaloniaProperty.Register<VirtualKey, ICommand>(nameof(ButtonCommand));

        public ICommand ButtonCommand
        {
            get { return GetValue(ButtonCommandProperty); }
            set { SetValue(ButtonCommandProperty, value); }
        }

        public static readonly StyledProperty<string> NormalKeyProperty =  AvaloniaProperty.Register<VirtualKey, string>(nameof(NormalKey));

        public string NormalKey
        {
            get { return GetValue(NormalKeyProperty); }
            set { SetValue(NormalKeyProperty, value); }
        }

        public static readonly StyledProperty<string> ShiftKeyProperty =  AvaloniaProperty.Register<VirtualKey, string>(nameof(ShiftKey));

        public string ShiftKey
        {
            get { return GetValue(ShiftKeyProperty); }
            set { SetValue(ShiftKeyProperty, value); }
        }
        public static readonly StyledProperty<string> AltCtrlKeyProperty =  AvaloniaProperty.Register<VirtualKey, string>(nameof(AltCtrlKey));

        public string AltCtrlKey
        {
            get { return GetValue(AltCtrlKeyProperty); }
            set { SetValue(AltCtrlKeyProperty, value); }
        }

        public static readonly StyledProperty<object> CaptionProperty = AvaloniaProperty.Register<VirtualKey, object>(nameof(Caption));

        public object Caption
        {
            get { return GetValue(CaptionProperty); }
            set { SetValue(CaptionProperty, value); }
        }

        public static readonly StyledProperty<Key> SpecialKeyProperty =  AvaloniaProperty.Register<VirtualKey, Key>(nameof(SpecialKey));

        public Key SpecialKey
        {
            get { return GetValue(SpecialKeyProperty); }
            set { SetValue(SpecialKeyProperty, value); }
        }

        public static readonly StyledProperty<MaterialIconKind> SpecialIconProperty =  AvaloniaProperty.Register<VirtualKey, MaterialIconKind>(nameof(SpecialIcon));

        public MaterialIconKind SpecialIcon
        {
            get { return GetValue(SpecialIconProperty); }
            set { SetValue(SpecialIconProperty, value); }
        }

        public VirtualKeyboardLayoutDE VirtualKeyboardLayout { get; set; }

        private ToggleButton _toggleButton;

        public VirtualKey()
        {
            DataContext = this;

            Initialized += (sender, args) =>
            {
                VirtualKeyboard keyboard = null;
                if (!Design.IsDesignMode)
                {
                    keyboard = this.GetVisualAncestors().OfType<VirtualKeyboard>().First();

                    keyboard.KeyboardStateStream.Subscribe(state =>
                    {
                        if (!string.IsNullOrEmpty(NormalKey))
                        {
                            switch (state)
                            {
                                case VirtualKeyboardState.Default:
                                    Caption = NormalKey;
                                    break;
                                case VirtualKeyboardState.Shift:
                                case VirtualKeyboardState.Capslock:
                                    Caption = ShiftKey;
                                    break;
                                case VirtualKeyboardState.AltCtrl:
                                    Caption = AltCtrlKey;
                                    break;
                                default:
                                    throw new ArgumentOutOfRangeException(nameof(state), state, null);
                            }
                        }
                    });

                    ButtonCommand = new RelayCommand(() =>
                    {
                        if (string.IsNullOrEmpty(NormalKey))
                        {
                            keyboard.ProcessKey(SpecialKey);
                        }
                        else
                        {
                            if(Caption is string s && !string.IsNullOrEmpty(s))
                                keyboard.ProcessText(s);
                        }
                    });
                }

                if (SpecialKey == Key.LeftShift || SpecialKey == Key.RightShift || SpecialKey == Key.CapsLock || SpecialKey == Key.RightAlt)
                {
                    _toggleButton = new ToggleButton
                    {
                        BorderThickness = new Thickness(1),
                        BorderBrush = new SolidColorBrush(Color.Parse("Black")),
                        [!ToggleButton.WidthProperty] = new Binding("Width"),
                        [!ToggleButton.HeightProperty] = new Binding("Height"),
                        [!ToggleButton.ContentProperty] = new Binding("Caption"),
                        [!ToggleButton.CommandProperty] = new Binding("ButtonCommand"),
                    };
                    Template = new FuncControlTemplate((control, scope) => _toggleButton);

                    if (keyboard != null)
                    {
                        keyboard.KeyboardStateStream.Subscribe(state =>
                        {
                            switch (state)
                            {
                                case VirtualKeyboardState.Default:
                                    _toggleButton.IsChecked = false;
                                    break;
                                case VirtualKeyboardState.Shift:
                                    if (SpecialKey == Key.LeftShift || SpecialKey == Key.RightShift)
                                        _toggleButton.IsChecked = true;
                                    else
                                    {
                                        _toggleButton.IsChecked = false;
                                    }
                                    break;
                                case VirtualKeyboardState.Capslock:
                                    _toggleButton.IsChecked = SpecialKey == Key.CapsLock;
                                    break;
                                case VirtualKeyboardState.AltCtrl:
                                    _toggleButton.IsChecked = SpecialKey == Key.RightAlt;
                                    break;
                                default:
                                    throw new ArgumentOutOfRangeException(nameof(state), state, null);
                            }
                        });
                    }
                }
                else
                {
                    Template = new FuncControlTemplate((control, scope) =>
                    {
                        return new Button
                        {
                            BorderThickness = new Thickness(1),
                            BorderBrush = new SolidColorBrush(Color.Parse("Black")),
                            [!Button.WidthProperty] = new Binding("Width"),
                            [!Button.HeightProperty] = new Binding("Height"),
                            [!Button.ContentProperty] = new Binding("Caption"),
                            [!Button.CommandProperty] = new Binding("ButtonCommand"),
                        };
                    });
                }

                if (string.IsNullOrEmpty(NormalKey))
                {
                    // special cases
                    switch (SpecialKey)
                    {
                        case Key.Tab:
                        {
                            var stackPanel = new StackPanel();
                            stackPanel.Orientation = Orientation.Vertical;
                            var first = new MaterialIcon();
                            first.Kind = SpecialIcon;
                            var second = new MaterialIcon();
                            second.Kind = SpecialIcon;
                            second.RenderTransform = new RotateTransform(180.0);
                            stackPanel.Children.Add(first);
                            stackPanel.Children.Add(second);
                            Caption = stackPanel;
                            IsEnabled = false;
                        }
                            break;
                        case Key.Space:
                        {
                            Caption = null;
                        }
                            break;
                        default:
                            Caption = new MaterialIcon
                            {
                                Kind = SpecialIcon
                            };
                            break;
                    }
                }
                else
                {
                    Caption = NormalKey;
                }
            };
        }
    }
<Styles xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:controls="using:HMI.Infrastructure.Keyboard">
  <Design.PreviewWith>
    <controls:VirtualKey />
  </Design.PreviewWith>
</Styles>
VirtualKeyboard.axaml
public enum VirtualKeyboardState
    {
        Default,
        Shift,
        Capslock,
        AltCtrl
    }
    public class VirtualKeyboard : UserControl
    {
        private static List<Type> Layouts { get; } = new();
        private static Func<Type> DefaultLayout { get; set; }

        public static void AddLayout<TLayout>() where TLayout : KeyboardLayout => Layouts.Add(typeof(TLayout));

        public static void SetDefaultLayout(Func<Type> getDefaultLayout) => DefaultLayout = getDefaultLayout;

        public static async Task<string?> ShowDialog(TextInputOptionsQueryEventArgs options, Window? owner = null)
        {
            var keyboard = new VirtualKeyboard();

            if (options.Source is TextBox textBox)
            {
                keyboard.TextBox.Text = textBox.Text;
                keyboard.TextBox.PasswordChar = textBox.PasswordChar;
            }

            var window = new CoporateWindow();
            window.CoporateContent = keyboard;
            window.Title = "MyFancyKeyboard";
            await window.ShowDialog(owner ?? App.MainWindow);
            if (window.Tag is string s)
            {
                if (options.Source is TextBox tb)
                    tb.Text = s;
                return s;
            }
            return null;
        }

        public TextBox TextBox { get; }
        public TransitioningContentControl TransitioningContentControl { get; }

        public IObservable<VirtualKeyboardState> KeyboardStateStream => _keyboardStateStream;
        private readonly BehaviorSubject<VirtualKeyboardState> _keyboardStateStream;

        private Window _parentWindow;

        public VirtualKeyboard()
        {
            InitializeComponent();
            TextBox = this.Get<TextBox>(nameof(TextBox));
            TransitioningContentControl = this.Get<TransitioningContentControl>(nameof(TransitioningContentControl));

            Initialized += async (sender, args) =>
            {
                TransitioningContentControl.Content = Activator.CreateInstance(DefaultLayout.Invoke());
                _parentWindow = this.GetVisualAncestors().OfType<Window>().First();
                await Task.Delay(TimeSpan.FromMilliseconds(100));
                Dispatcher.UIThread.Post(() =>
                {
                    TextBox.Focus();
                    if(!string.IsNullOrEmpty(TextBox.Text))
                        TextBox.CaretIndex = TextBox.Text.Length;
                });
            };
            KeyDown += (sender, args) =>
            {
                TextBox.Focus();
                if (args.Key == Key.Escape)
                {
                    TextBox.Text = "";
                }
                else if(args.Key == Key.Enter)
                {
                    _parentWindow.Tag = TextBox.Text;
                    _parentWindow.Close();
                }
            };
            _keyboardStateStream = new BehaviorSubject<VirtualKeyboardState>(VirtualKeyboardState.Default);
        }

        public void ProcessText(string text)
        {
            TextBox.Focus();
            InputManager.Instance.ProcessInput(new RawTextInputEventArgs(KeyboardDevice.Instance, (ulong)DateTime.Now.Ticks, (Window)TextBox.GetVisualRoot(), text ));
            if (_keyboardStateStream.Value == VirtualKeyboardState.Shift)
            {
                _keyboardStateStream.OnNext(VirtualKeyboardState.Default);
            }
        }

        public void ProcessKey(Key key)
        {
            if (key == Key.LeftShift || key == Key.RightShift)
            {
                if (_keyboardStateStream.Value == VirtualKeyboardState.Shift)
                {
                    _keyboardStateStream.OnNext(VirtualKeyboardState.Default);
                }
                else
                {
                    _keyboardStateStream.OnNext(VirtualKeyboardState.Shift);
                }
            }
            else if (key == Key.RightAlt)
            {
                if (_keyboardStateStream.Value == VirtualKeyboardState.AltCtrl)
                {
                    _keyboardStateStream.OnNext(VirtualKeyboardState.Default);
                }
                else
                {
                    _keyboardStateStream.OnNext(VirtualKeyboardState.AltCtrl);
                }
            }
            else if (key == Key.CapsLock)
            {
                if (_keyboardStateStream.Value == VirtualKeyboardState.Capslock)
                {
                    _keyboardStateStream.OnNext(VirtualKeyboardState.Default);
                }
                else
                {
                    _keyboardStateStream.OnNext(VirtualKeyboardState.Capslock);
                }
            }
            else
            {
                if (key == Key.Clear)
                {
                    TextBox.Text = "";
                    TextBox.Focus();
                }
                else if (key == Key.Enter)
                {
                    _parentWindow.Tag = TextBox.Text;
                    _parentWindow.Close();
                }
                else if (key == Key.Help)
                {
                    _keyboardStateStream.OnNext(VirtualKeyboardState.Default);
                    if (TransitioningContentControl.Content is KeyboardLayout layout)
                    {
                        var index = Layouts.IndexOf(layout.GetType());
                        if (Layouts.Count - 1 > index)
                        {
                            TransitioningContentControl.Content = Activator.CreateInstance(Layouts[index + 1]);
                        }
                        else
                        {
                            TransitioningContentControl.Content = Activator.CreateInstance(Layouts[0]);
                        }
                    }
                }
                else
                {
                    TextBox.Focus();
                    InputManager.Instance.ProcessInput(new RawKeyEventArgs(KeyboardDevice.Instance, (ulong)DateTime.Now.Ticks, (Window)TextBox.GetVisualRoot(), RawKeyEventType.KeyDown, key, RawInputModifiers.None ));
                    InputManager.Instance.ProcessInput(new RawKeyEventArgs(KeyboardDevice.Instance, (ulong)DateTime.Now.Ticks, (Window)TextBox.GetVisualRoot(), RawKeyEventType.KeyUp, key, RawInputModifiers.None ));
                }
            }
        }

        private void InitializeComponent()
        {
            AvaloniaXamlLoader.Load(this);
        }
    }
<UserControl 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:reactiveUi="http://reactiveui.net"
             xmlns:layout="clr-namespace:HMI.Infrastructure.Keyboard.Layout"
             mc:Ignorable="d"
             x:Class="HMI.Infrastructure.Keyboard.VirtualKeyboard">
    <DockPanel Margin="10">
      <TextBox Name="TextBox" DockPanel.Dock="Top"></TextBox>
      <Grid DockPanel.Dock="Bottom">
        <!-- QuickAndDirty resize bugfix -->
        <layout:VirtualKeyboardLayoutDE Opacity="0" IsHitTestVisible="False"/>
        <reactiveUi:TransitioningContentControl Name="TransitioningContentControl" />
      </Grid>
    </DockPanel>
</UserControl>
VirtualKeyboardTextInputMethod.cs
    public class VirtualKeyboardTextInputMethod : ITextInputMethodImpl
    {
        private bool _isOpen;
        private TextInputOptionsQueryEventArgs? _textInputOptions;
        public async void SetActive(bool active)
        {
            if (active && !_isOpen && _textInputOptions != null)
            {
                _isOpen = true;
                await VirtualKeyboard.ShowDialog(_textInputOptions);
                _isOpen = false;
                _textInputOptions = null;
                App.MainWindow.Focus(); // remove focus from the last control (TextBox)
            }
        }

        public void SetCursorRect(Rect rect){}

        public void SetOptions(TextInputOptionsQueryEventArgs? options)
        {
            _textInputOptions = options;
        }

        public void Reset(){}
    }
VirtualKeyWidthMultiplayer.cs
    public class VirtualKeyWidthMultiplayer : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var v = double.Parse(value.ToString());
            var p = double.Parse(parameter.ToString());
            return v * (p / 10.0);
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotImplementedException();
    }
CoporateWindow

The CoporateWindow is just a normal Window with a default styling. It can easily be replaced by the normal Window.

<Window 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:theme="clr-namespace:HMI.Theme"
      mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
      x:Class="HMI.Views.CoporateWindow"
      Title="CoporateWindow"
      Icon="/Assets/logo.ico"
      TransparencyLevelHint="AcrylicBlur"
      ExtendClientAreaToDecorationsHint="True"
      Background="Red"
      SizeToContent="WidthAndHeight"
      WindowStartupLocation="CenterOwner">
<Panel>
  <ExperimentalAcrylicBorder IsHitTestVisible="False">
    <ExperimentalAcrylicBorder.Material>
      <ExperimentalAcrylicMaterial
        BackgroundSource="Digger"
        TintColor="{x:Static theme:HaprotecCorporateDesignTheme.Secondary}"
        TintOpacity="1"
        MaterialOpacity="0.65" />
    </ExperimentalAcrylicBorder.Material>
  </ExperimentalAcrylicBorder>
  <Panel Margin="20,40,20,20">
    <ContentPresenter Content="{Binding Path=CoporateContent}"/>
  </Panel>
</Panel>
</Window>
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;

namespace HMI.Views
{
    public class CoporateWindow : Window
    {
        public static readonly StyledProperty<object> CoporateContentProperty = AvaloniaProperty.Register<CoporateWindow, object>(nameof(CoporateContent));

        public object CoporateContent
        {
            get { return GetValue(CoporateContentProperty); }
            set { SetValue(CoporateContentProperty, value); }
        }

        public CoporateWindow()
        {
            DataContext = this;
            InitializeComponent();
#if DEBUG
            this.AttachDevTools();
#endif
        }

        private void InitializeComponent()
        {
            AvaloniaXamlLoader.Load(this);
        }
    }
}

Usage

define available layouts and set func which one should be used when opening (based on the language of the current logged in user for example).

VirtualKeyboard.AddLayout<VirtualKeyboardLayoutDE>();
VirtualKeyboard.AddLayout<VirtualKeyboardLayoutUS>();
VirtualKeyboard.SetDefaultLayout(() => typeof(VirtualKeyboardLayoutDE));

Add the VirtualKeyboardTextInputMethod to your MainWindow. Here you can optional check your local settings (of your app) if you need the VirtualKeyboard or not.

   public partial class MainWindow : ReactiveWindow<MainWindowViewModel>, ITextInputMethodRoot
    {
        public MainWindow()
        {
            InitializeComponent();
#if DEBUG
            this.AttachDevTools();

#endif
        }

        private void InitializeComponent() => AvaloniaXamlLoader.Load(this);

        private VirtualKeyboardTextInputMethod virtualKeyboardTextInput = new VirtualKeyboardTextInputMethod();
        ITextInputMethodImpl ITextInputMethodRoot.InputMethod
        {
            get
            {
                return virtualKeyboardTextInput;
            }
        }
    }
1reaction
011010101001commented, Aug 11, 2023

Hello @nullx1337 thanks so much, i was searching for this for long, could you please share a working code that can compile with avalonia 11 ? thanks

Read more comments on GitHub >

github_iconTop Results From Across the Web

VirtualKeyboard API
The VirtualKeyboard API provides authors with greater control over the visibility of the virtual keyboard (VK), and greater ability to adapt ...
Read more >
VirtualKeyboard API - MDN Web Docs
The VirtualKeyboard API provides developers control over the layout of their applications when the on-screen virtual keyboard appears and ...
Read more >
How to build a virtual keyboard in an HTML page
To create a virtual keyboard, you need to add several buttons to a page. When users click a certain button, the JavaScript function...
Read more >
chrome.virtualKeyboard - Chrome Developers
The chrome.virtualKeyboard API is a kiosk only API used to configure virtual keyboard layout and behavior in kiosk sessions. Permissions. virtualKeyboard.
Read more >
how do i create a virtual keyboard that can be used to ...
So I am trying to create a virtual keyboard that can insert values in a Jtextfield of another Jframe. The problem is that...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found