实现类似WPF的KeyBoard,命名为Input类。

代码如下

using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.Interactivity;
using Avalonia.Threading;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Shares.Avalonia
{
    public class Input
    {
        public static event EventHandler<KeyEventArgs>? PreviewKeyDown;
        public static event EventHandler<KeyEventArgs>? KeyDown;
        public static event EventHandler<KeyEventArgs>? PreviewKeyUp;
        public static event EventHandler<KeyEventArgs>? KeyUp;
        public static event EventHandler<TextInputEventArgs>? PreTextInput;
        public static event EventHandler<TextInputEventArgs>? TextInput;

        private static readonly HashSet<Key> keysDown = new();
        private static readonly HashSet<Key> keysUp = new();
        private static FocusManager? focusManager = AvaloniaExtensions.GetService<FocusManager>();
        private static IDisposable? subsription;
        private static RoutingStrategies route;
        public static RoutingStrategies Route
        {
            get
            {
                return route;
            }
            set
            {
                if (subsription != null)
                    subsription.Dispose();
                string routing = "PreProcess";
                if (value.HasFlag(RoutingStrategies.Bubble))
                    routing = "PostProcess";
                else if (value.HasFlag(RoutingStrategies.Direct))
                    routing = "Process";
                var process = inputManager?.GetPropertyValue<IObservable<RawInputEventArgs>?>(routing);
                subsription = process?.Subscribe(OnRawEvent);
                route = value;
            }
        } 
        public static KeyModifiers KeyModifiers { get; private set; }
        public static bool IsRepeat { get; private set; }
        public static bool IsToggled { get; private set; }
        private static KeyboardDevice? device;
        private static IInputManager? inputManager;
        static Input()
        {
            device = AvaloniaExtensions.GetService<KeyboardDevice>();
            inputManager = device?.GetPropertyValue<IInputManager?>("InputManager");        
        }
        private static void OnRawEvent(RawInputEventArgs e)
        {
            switch (e)
            {
                case RawKeyEventArgs keyEvent:
                    HandleKeyEvent(keyEvent);
                    break;
                    
                case RawTextInputEventArgs textInputEvent:
                    HandleTextInputEvent(textInputEvent);
                    break;
            }
        }
        private static void HandleKeyEvent(RawKeyEventArgs e)
        {
            var type = e.GetPropertyValue<RawKeyEventType>("Type");
            var key = e.GetPropertyValue<Key>("Key");
            var physicalKey = e.GetPropertyValue<PhysicalKey>("PhysicalKey");
            var keySymbol = e.GetPropertyValue<string?>("KeySymbol");
            var keyDeviceType = e.GetPropertyValue<KeyDeviceType>("KeyDeviceType");
            var source = GetFocus();
            KeyModifiers = (KeyModifiers)e.GetPropertyValue<RawInputModifiers>("Modifiers");
            var previewArgs = new KeyEventArgs()
            {
                Key = key,
                KeyModifiers = KeyModifiers,
                PhysicalKey = physicalKey,
                KeySymbol = keySymbol,
                KeyDeviceType = keyDeviceType,
                Handled = false,
                Source = source,
                Route = Route
            };
            switch (type)
            {
                case RawKeyEventType.KeyDown:
                    {
                        keysUp.Remove(key);
                        previewArgs.RoutedEvent = InputElement.KeyDownEvent;
                        IsRepeat = !keysDown.Add(key);
                        if (key == Key.CapsLock || key == Key.NumLock || key == Key.Scroll)
                            IsToggled = !IsToggled;
                        PreviewKeyDown?.Invoke(source, previewArgs);
                        if (!previewArgs.Handled)
                        {
                            KeyDown?.Invoke(source, previewArgs);
                        }
                    }
                    break;
                    case RawKeyEventType.KeyUp:
                    {
                        keysDown.Remove(key);
                        previewArgs.RoutedEvent = InputElement.KeyUpEvent;
                        IsRepeat = !keysUp.Add(key);
                        PreviewKeyUp?.Invoke(source, previewArgs);
                        if (!previewArgs.Handled)
                        {
                            KeyUp?.Invoke(source, previewArgs);
                        }
                    }
                    break;
            }
            e.SetPropertyValue<bool>("Handled", previewArgs.Handled);
        }
        private static void HandleTextInputEvent(RawTextInputEventArgs e)
        {
            var text = e.GetPropertyValue<string>("Text");
            var source = GetFocus();
            var previewArgs = new TextInputEventArgs()
            {
                Text = text,
                RoutedEvent = InputElement.TextInputEvent,
                Handled = false,
                Source = source,
                Route = Route
            };
            PreTextInput?.Invoke(source, previewArgs);
            if (!previewArgs.Handled)
            {
                TextInput?.Invoke(source, previewArgs);
            }
            e.SetPropertyValue<bool>("Handled", previewArgs.Handled);
        }
        public static bool IsKeyDown(Key key)
        {
            return keysDown.Contains(key);
        }
        public static bool IsKeyUp(Key key)
        {
            return keysUp.Contains(key);
        }
        public static IInputElement? GetFocus()
        {
            return focusManager?.InvokeMethod<IInputElement>("GetFocusedElement");
        }
        public static bool SetFocus(IInputElement inputElement)
        {
            return (bool)(focusManager?.InvokeMethod("Focus",
                new object[] { inputElement, NavigationMethod.Unspecified, KeyModifiers.None })!);
        }
        public static void ClearFocus()
        {
            focusManager?.InvokeMethod("ClearFocus");
        }
    }
}

KeyModifiers.axaml代码

<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"
        Height="300" Width="300"
        x:Class="AvaloniaUI.KeyModifiers"
        Title="KeyModifiers">
    <StackPanel Margin="5">
        <TextBox KeyDown="KeyEvent"></TextBox>
        <TextBlock Name="lblInfo"></TextBlock>
        <Button Click="CheckShift">Check Current Shift State</Button>
    </StackPanel>
</Window>

KeyModifiers.axaml.cs代码

using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using Shares.Avalonia;
using System;
using System.Threading.Tasks;

namespace AvaloniaUI;

public partial class KeyModifiers : Window
{
    public KeyModifiers()
    {
        InitializeComponent();
        Input.Route = RoutingStrategies.Tunnel;
    }

    private void KeyEvent(object? sender, KeyEventArgs e)
    {
        lblInfo.Text = $"Modifiers: " + e.KeyModifiers.ToString();
        if (e.KeyModifiers.HasFlag(Avalonia.Input.KeyModifiers.Control))
        {
            lblInfo.Text += "\r\nYou held the Control key.";
        }
    }

    private void CheckShift(object? sender, RoutedEventArgs e)
    {
        if (Input.IsKeyDown(Key.LeftShift))
            lblInfo.Text = "The left Shift is held down.";
        else
            lblInfo.Text = "The left Shift is not held down.";
    }
}

运行效果

 

posted on 2025-07-19 12:43  dalgleish  阅读(48)  评论(0)    收藏  举报