C# 虚拟键盘(软键盘)实现

C#虚拟键盘实现,支持触摸输入、物理键盘同步、多语言布局、自适应屏幕等功能。

一、项目结构

复制代码
VirtualKeyboard/
├── VirtualKeyboard.csproj
├── Program.cs
├── MainForm.cs
├── MainForm.Designer.cs
├── Keyboard/
│   ├── VirtualKeyboardForm.cs
│   ├── KeyboardLayout.cs
│   ├── KeyButton.cs
│   ├── KeyboardTheme.cs
│   └── KeyboardHook.cs
├── Controls/
│   ├── TouchKeyboard.cs
│   └── KeyboardPanel.cs
├── Layouts/
│   ├── EnglishLayout.cs
│   ├── ChineseLayout.cs
│   ├── NumberLayout.cs
│   └── SymbolLayout.cs
├── Utils/
│   ├── InputSimulator.cs
│   └── ScreenHelper.cs
└── Resources/

二、核心代码实现

1. 主程序入口 (Program.cs)

csharp 复制代码
using System;
using System.Windows.Forms;

namespace VirtualKeyboard
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            
            // 检查是否已经有实例在运行
            if (IsAlreadyRunning())
            {
                MessageBox.Show("虚拟键盘已经在运行!", "提示", 
                    MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }
            
            Application.Run(new MainForm());
        }
        
        private static bool IsAlreadyRunning()
        {
            System.Diagnostics.Process current = System.Diagnostics.Process.GetCurrentProcess();
            System.Diagnostics.Process[] processes = System.Diagnostics.Process.GetProcessesByName(current.ProcessName);
            return processes.Length > 1;
        }
    }
}

2. 主窗体 (MainForm.cs)

csharp 复制代码
using System;
using System.Windows.Forms;
using VirtualKeyboard.Keyboard;

namespace VirtualKeyboard
{
    public partial class MainForm : Form
    {
        private VirtualKeyboardForm keyboardForm;
        private NotifyIcon trayIcon;
        private ContextMenuStrip trayMenu;
        
        public MainForm()
        {
            InitializeComponent();
            InitializeTrayIcon();
            InitializeKeyboard();
        }
        
        private void InitializeComponent()
        {
            this.Text = "虚拟键盘";
            this.Size = new System.Drawing.Size(400, 300);
            this.StartPosition = FormStartPosition.CenterScreen;
            this.FormBorderStyle = FormBorderStyle.FixedSingle;
            this.MaximizeBox = false;
            
            // 控制面板
            Panel controlPanel = new Panel
            {
                Dock = DockStyle.Fill,
                Padding = new Padding(20)
            };
            
            Button btnShowKeyboard = new Button
            {
                Text = "显示虚拟键盘",
                Size = new System.Drawing.Size(150, 40),
                Location = new System.Drawing.Point(20, 20),
                Font = new System.Drawing.Font("微软雅黑", 10)
            };
            btnShowKeyboard.Click += BtnShowKeyboard_Click;
            
            Button btnHideKeyboard = new Button
            {
                Text = "隐藏虚拟键盘",
                Size = new System.Drawing.Size(150, 40),
                Location = new System.Drawing.Point(20, 80),
                Font = new System.Drawing.Font("微软雅黑", 10)
            };
            btnHideKeyboard.Click += BtnHideKeyboard_Click;
            
            Button btnExit = new Button
            {
                Text = "退出",
                Size = new System.Drawing.Size(150, 40),
                Location = new System.Drawing.Point(20, 140),
                Font = new System.Drawing.Font("微软雅黑", 10)
            };
            btnExit.Click += BtnExit_Click;
            
            controlPanel.Controls.AddRange(new Control[] { btnShowKeyboard, btnHideKeyboard, btnExit });
            this.Controls.Add(controlPanel);
        }
        
        private void InitializeTrayIcon()
        {
            trayMenu = new ContextMenuStrip();
            trayMenu.Items.Add("显示", null, (s, e) => ShowKeyboard());
            trayMenu.Items.Add("隐藏", null, (s, e) => HideKeyboard());
            trayMenu.Items.Add("-");
            trayMenu.Items.Add("退出", null, (s, e) => Application.Exit());
            
            trayIcon = new NotifyIcon
            {
                Icon = System.Drawing.SystemIcons.Application,
                Text = "虚拟键盘",
                ContextMenuStrip = trayMenu,
                Visible = true
            };
            trayIcon.DoubleClick += (s, e) => ShowKeyboard();
        }
        
        private void InitializeKeyboard()
        {
            keyboardForm = new VirtualKeyboardForm();
        }
        
        private void BtnShowKeyboard_Click(object sender, EventArgs e)
        {
            ShowKeyboard();
        }
        
        private void BtnHideKeyboard_Click(object sender, EventArgs e)
        {
            HideKeyboard();
        }
        
        private void BtnExit_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }
        
        private void ShowKeyboard()
        {
            if (keyboardForm == null || keyboardForm.IsDisposed)
            {
                keyboardForm = new VirtualKeyboardForm();
            }
            
            keyboardForm.Show();
            keyboardForm.Activate();
        }
        
        private void HideKeyboard()
        {
            keyboardForm?.Hide();
        }
        
        protected override void OnFormClosing(FormClosingEventArgs e)
        {
            base.OnFormClosing(e);
            trayIcon.Visible = false;
            keyboardForm?.Close();
        }
    }
}

3. 虚拟键盘窗体 (VirtualKeyboardForm.cs)

csharp 复制代码
using System;
using System.Drawing;
using System.Windows.Forms;
using VirtualKeyboard.Controls;
using VirtualKeyboard.Keyboard;
using VirtualKeyboard.Layouts;
using VirtualKeyboard.Utils;

namespace VirtualKeyboard.Keyboard
{
    public class VirtualKeyboardForm : Form
    {
        private KeyboardPanel keyboardPanel;
        private KeyboardLayout currentLayout;
        private KeyboardTheme currentTheme;
        private InputSimulator inputSimulator;
        private KeyboardHook keyboardHook;
        
        // 状态
        private bool isShiftPressed = false;
        private bool isCtrlPressed = false;
        private bool isAltPressed = false;
        private bool isCapsLockOn = false;
        private bool isNumLockOn = false;
        
        public VirtualKeyboardForm()
        {
            InitializeComponent();
            InitializeKeyboard();
            InitializeInputSimulator();
            InitializeKeyboardHook();
        }
        
        private void InitializeComponent()
        {
            this.FormBorderStyle = FormBorderStyle.None;
            this.ShowInTaskbar = false;
            this.TopMost = true;
            this.StartPosition = FormStartPosition.Manual;
            this.Size = new Size(800, 300);
            this.Location = new Point(Screen.PrimaryScreen.WorkingArea.Width - this.Width,
                                     Screen.PrimaryScreen.WorkingArea.Height - this.Height);
            
            // 允许点击穿透(不获取焦点)
            this.SetStyle(ControlStyles.Selectable, false);
            this.TabStop = false;
        }
        
        private void InitializeKeyboard()
        {
            currentLayout = new EnglishLayout();
            currentTheme = new KeyboardTheme();
            currentTheme.LoadDefaultTheme();
            
            keyboardPanel = new KeyboardPanel
            {
                Dock = DockStyle.Fill,
                Layout = currentLayout,
                Theme = currentTheme
            };
            keyboardPanel.KeyPressed += KeyboardPanel_KeyPressed;
            keyboardPanel.KeyReleased += KeyboardPanel_KeyReleased;
            keyboardPanel.LayoutChanged += KeyboardPanel_LayoutChanged;
            
            this.Controls.Add(keyboardPanel);
        }
        
        private void InitializeInputSimulator()
        {
            inputSimulator = new InputSimulator();
        }
        
        private void InitializeKeyboardHook()
        {
            keyboardHook = new KeyboardHook();
            keyboardHook.KeyDown += KeyboardHook_KeyDown;
            keyboardHook.KeyUp += KeyboardHook_KeyUp;
            keyboardHook.Install();
        }
        
        private void KeyboardPanel_KeyPressed(object sender, KeyEventArgs e)
        {
            HandleKeyPress(e.KeyCode, true);
        }
        
        private void KeyboardPanel_KeyReleased(object sender, KeyEventArgs e)
        {
            HandleKeyPress(e.KeyCode, false);
        }
        
        private void KeyboardPanel_LayoutChanged(object sender, KeyboardLayout layout)
        {
            currentLayout = layout;
            keyboardPanel.Layout = layout;
            keyboardPanel.Refresh();
        }
        
        private void KeyboardHook_KeyDown(object sender, KeyEventArgs e)
        {
            // 同步物理键盘状态
            UpdateModifierKeyState(e.KeyCode, true);
        }
        
        private void KeyboardHook_KeyUp(object sender, KeyEventArgs e)
        {
            // 同步物理键盘状态
            UpdateModifierKeyState(e.KeyCode, false);
        }
        
        private void UpdateModifierKeyState(Keys key, bool isPressed)
        {
            switch (key)
            {
                case Keys.ShiftKey:
                case Keys.LShiftKey:
                case Keys.RShiftKey:
                    isShiftPressed = isPressed;
                    keyboardPanel.SetModifierKeyState(Keys.ShiftKey, isPressed);
                    break;
                    
                case Keys.ControlKey:
                case Keys.LControlKey:
                case Keys.RControlKey:
                    isCtrlPressed = isPressed;
                    keyboardPanel.SetModifierKeyState(Keys.ControlKey, isPressed);
                    break;
                    
                case Keys.Menu:
                case Keys.LMenu:
                case Keys.RMenu:
                    isAltPressed = isPressed;
                    keyboardPanel.SetModifierKeyState(Keys.Menu, isPressed);
                    break;
                    
                case Keys.CapsLock:
                    if (!isPressed) // 只在释放时切换状态
                    {
                        isCapsLockOn = !isCapsLockOn;
                        keyboardPanel.SetModifierKeyState(Keys.CapsLock, isCapsLockOn);
                    }
                    break;
                    
                case Keys.NumLock:
                    if (!isPressed)
                    {
                        isNumLockOn = !isNumLockOn;
                        keyboardPanel.SetModifierKeyState(Keys.NumLock, isNumLockOn);
                    }
                    break;
            }
        }
        
        private void HandleKeyPress(Keys key, bool isDown)
        {
            if (isDown)
            {
                // 处理特殊键
                switch (key)
                {
                    case Keys.ShiftKey:
                        isShiftPressed = true;
                        keyboardPanel.SetModifierKeyState(Keys.ShiftKey, true);
                        return;
                        
                    case Keys.ControlKey:
                        isCtrlPressed = true;
                        keyboardPanel.SetModifierKeyState(Keys.ControlKey, true);
                        return;
                        
                    case Keys.Menu:
                        isAltPressed = true;
                        keyboardPanel.SetModifierKeyState(Keys.Menu, true);
                        return;
                        
                    case Keys.CapsLock:
                        isCapsLockOn = !isCapsLockOn;
                        keyboardPanel.SetModifierKeyState(Keys.CapsLock, isCapsLockOn);
                        return;
                        
                    case Keys.NumLock:
                        isNumLockOn = !isNumLockOn;
                        keyboardPanel.SetModifierKeyState(Keys.NumLock, isNumLockOn);
                        return;
                        
                    case Keys.Escape:
                        this.Hide();
                        return;
                }
                
                // 发送按键
                SendKeyToFocusedControl(key);
            }
            else
            {
                // 释放特殊键
                switch (key)
                {
                    case Keys.ShiftKey:
                        isShiftPressed = false;
                        keyboardPanel.SetModifierKeyState(Keys.ShiftKey, false);
                        break;
                        
                    case Keys.ControlKey:
                        isCtrlPressed = false;
                        keyboardPanel.SetModifierKeyState(Keys.ControlKey, false);
                        break;
                        
                    case Keys.Menu:
                        isAltPressed = false;
                        keyboardPanel.SetModifierKeyState(Keys.Menu, false);
                        break;
                }
            }
        }
        
        private void SendKeyToFocusedControl(Keys key)
        {
            try
            {
                // 使用InputSimulator发送按键
                inputSimulator.SendKey(key, isShiftPressed, isCtrlPressed, isAltPressed);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"发送按键失败: {ex.Message}");
            }
        }
        
        protected override void OnShown(EventArgs e)
        {
            base.OnShown(e);
            // 确保不获取焦点
            this.Activated += (s, args) => this.Parent?.Focus();
        }
        
        protected override void OnFormClosing(FormClosingEventArgs e)
        {
            base.OnFormClosing(e);
            keyboardHook?.Uninstall();
        }
    }
}

4. 键盘面板控件 (KeyboardPanel.cs)

csharp 复制代码
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using VirtualKeyboard.Keyboard;
using VirtualKeyboard.Layouts;

namespace VirtualKeyboard.Controls
{
    public class KeyboardPanel : UserControl
    {
        public event EventHandler<KeyEventArgs> KeyPressed;
        public event EventHandler<KeyEventArgs> KeyReleased;
        public event EventHandler<KeyboardLayout> LayoutChanged;
        
        public KeyboardLayout Layout
        {
            get { return _layout; }
            set
            {
                _layout = value;
                CreateKeys();
                Invalidate();
            }
        }
        
        public KeyboardTheme Theme
        {
            get { return _theme; }
            set
            {
                _theme = value;
                ApplyTheme();
            }
        }
        
        private KeyboardLayout _layout;
        private KeyboardTheme _theme;
        private List<KeyButton> keyButtons = new List<KeyButton>();
        private Timer repeatTimer;
        
        public KeyboardPanel()
        {
            InitializeComponent();
            CreateKeys();
        }
        
        private void InitializeComponent()
        {
            this.Dock = DockStyle.Fill;
            this.BackColor = Color.FromArgb(40, 40, 40);
            
            repeatTimer = new Timer
            {
                Interval = 50,
                Enabled = false
            };
            repeatTimer.Tick += RepeatTimer_Tick;
        }
        
        private void CreateKeys()
        {
            keyButtons.Clear();
            this.Controls.Clear();
            
            if (_layout == null) return;
            
            int keyWidth = 60;
            int keyHeight = 50;
            int margin = 5;
            
            for (int row = 0; row < _layout.Rows; row++)
            {
                for (int col = 0; col < _layout.Columns; col++)
                {
                    var keyInfo = _layout.GetKey(row, col);
                    if (keyInfo == null) continue;
                    
                    var button = new KeyButton
                    {
                        Key = keyInfo.Key,
                        Text = keyInfo.Text,
                        Width = keyInfo.Width * keyWidth + (keyInfo.Width - 1) * margin,
                        Height = keyHeight,
                        Location = new Point(col * (keyWidth + margin), row * (keyHeight + margin)),
                        Theme = _theme
                    };
                    
                    button.MouseDown += KeyButton_MouseDown;
                    button.MouseUp += KeyButton_MouseUp;
                    button.MouseLeave += KeyButton_MouseLeave;
                    
                    keyButtons.Add(button);
                    this.Controls.Add(button);
                }
            }
            
            // 调整大小
            this.Size = new Size(
                _layout.Columns * (keyWidth + margin) - margin,
                _layout.Rows * (keyHeight + margin) - margin
            );
        }
        
        private void ApplyTheme()
        {
            if (_theme == null) return;
            
            this.BackColor = _theme.BackgroundColor;
            
            foreach (var button in keyButtons)
            {
                button.Theme = _theme;
            }
        }
        
        public void SetModifierKeyState(Keys key, bool isPressed)
        {
            foreach (var button in keyButtons)
            {
                if (button.Key == key)
                {
                    button.IsPressed = isPressed;
                    button.Invalidate();
                }
            }
        }
        
        private void KeyButton_MouseDown(object sender, MouseEventArgs e)
        {
            var button = sender as KeyButton;
            if (button == null) return;
            
            button.IsPressed = true;
            button.Invalidate();
            
            KeyPressed?.Invoke(this, new KeyEventArgs(button.Key));
            
            // 开始重复计时
            repeatTimer.Start();
        }
        
        private void KeyButton_MouseUp(object sender, MouseEventArgs e)
        {
            var button = sender as KeyButton;
            if (button == null) return;
            
            button.IsPressed = false;
            button.Invalidate();
            
            KeyReleased?.Invoke(this, new KeyEventArgs(button.Key));
            
            repeatTimer.Stop();
        }
        
        private void KeyButton_MouseLeave(object sender, EventArgs e)
        {
            var button = sender as KeyButton;
            if (button == null) return;
            
            button.IsPressed = false;
            button.Invalidate();
            
            repeatTimer.Stop();
        }
        
        private void RepeatTimer_Tick(object sender, EventArgs e)
        {
            // 重复发送按键
            foreach (var button in keyButtons)
            {
                if (button.IsPressed)
                {
                    KeyPressed?.Invoke(this, new KeyEventArgs(button.Key));
                }
            }
        }
        
        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            
            // 绘制边框
            using (var pen = new Pen(Color.FromArgb(60, 60, 60), 1))
            {
                e.Graphics.DrawRectangle(pen, 0, 0, Width - 1, Height - 1);
            }
        }
    }
}

5. 按键按钮控件 (KeyButton.cs)

csharp 复制代码
using System;
using System.Drawing;
using System.Windows.Forms;
using VirtualKeyboard.Keyboard;

namespace VirtualKeyboard.Keyboard
{
    public class KeyButton : Control
    {
        public Keys Key { get; set; }
        public string NormalText { get; set; }
        public string ShiftText { get; set; }
        public bool IsPressed { get; set; }
        public bool IsModifierKey { get; set; }
        public bool IsSpecialKey { get; set; }
        
        private KeyboardTheme _theme;
        public KeyboardTheme Theme
        {
            get { return _theme; }
            set
            {
                _theme = value;
                Invalidate();
            }
        }
        
        public KeyButton()
        {
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer | 
                         ControlStyles.AllPaintingInWmPaint | 
                         ControlStyles.UserPaint, true);
            this.TabStop = false;
            this.DoubleBuffered = true;
        }
        
        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            
            if (_theme == null) return;
            
            // 确定背景色
            Color backColor = IsPressed ? _theme.PressedColor : 
                            IsModifierKey ? _theme.ModifierColor : 
                            IsSpecialKey ? _theme.SpecialColor : 
                            _theme.NormalColor;
            
            // 绘制背景
            using (var brush = new SolidBrush(backColor))
            {
                e.Graphics.FillRectangle(brush, 0, 0, Width, Height);
            }
            
            // 绘制边框
            Color borderColor = IsPressed ? _theme.PressedBorderColor : _theme.BorderColor;
            using (var pen = new Pen(borderColor, 1))
            {
                e.Graphics.DrawRectangle(pen, 0, 0, Width - 1, Height - 1);
            }
            
            // 绘制文字
            string text = NormalText;
            if (ShiftText != null && IsPressed) // 这里应该根据Shift状态判断
            {
                text = ShiftText;
            }
            
            Color textColor = IsPressed ? _theme.PressedTextColor : _theme.TextColor;
            using (var brush = new SolidBrush(textColor))
            using (var font = new Font("微软雅黑", 10, FontStyle.Regular))
            {
                var format = new StringFormat
                {
                    Alignment = StringAlignment.Center,
                    LineAlignment = StringAlignment.Center
                };
                
                e.Graphics.DrawString(text, font, brush, 
                    new RectangleF(0, 0, Width, Height), format);
            }
        }
        
        protected override void OnMouseEnter(EventArgs e)
        {
            base.OnMouseEnter(e);
            Invalidate();
        }
        
        protected override void OnMouseLeave(EventArgs e)
        {
            base.OnMouseLeave(e);
            IsPressed = false;
            Invalidate();
        }
    }
}

6. 键盘布局 (EnglishLayout.cs)

csharp 复制代码
using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace VirtualKeyboard.Layouts
{
    public class EnglishLayout : KeyboardLayout
    {
        public EnglishLayout()
        {
            Name = "English";
            Rows = 5;
            Columns = 15;
            
            // 第一行:功能键
            SetKey(0, 0, Keys.Escape, "Esc", 1);
            SetKey(0, 1, Keys.F1, "F1", 1);
            SetKey(0, 2, Keys.F2, "F2", 1);
            SetKey(0, 3, Keys.F3, "F3", 1);
            SetKey(0, 4, Keys.F4, "F4", 1);
            SetKey(0, 6, Keys.F5, "F5", 1);
            SetKey(0, 7, Keys.F6, "F6", 1);
            SetKey(0, 8, Keys.F7, "F7", 1);
            SetKey(0, 9, Keys.F8, "F8", 1);
            SetKey(0, 11, Keys.F9, "F9", 1);
            SetKey(0, 12, Keys.F10, "F10", 1);
            SetKey(0, 13, Keys.F11, "F11", 1);
            SetKey(0, 14, Keys.F12, "F12", 1);
            
            // 第二行:数字和符号
            SetKey(1, 0, Keys.Oemtilde, "`", "~", 1);
            SetKey(1, 1, Keys.D1, "1", "!", 1);
            SetKey(1, 2, Keys.D2, "2", "@", 1);
            SetKey(1, 3, Keys.D3, "3", "#", 1);
            SetKey(1, 4, Keys.D4, "4", "$", 1);
            SetKey(1, 5, Keys.D5, "5", "%", 1);
            SetKey(1, 6, Keys.D6, "6", "^", 1);
            SetKey(1, 7, Keys.D7, "7", "&", 1);
            SetKey(1, 8, Keys.D8, "8", "*", 1);
            SetKey(1, 9, Keys.D9, "9", "(", 1);
            SetKey(1, 10, Keys.D0, "0", ")", 1);
            SetKey(1, 11, Keys.OemMinus, "-", "_", 1);
            SetKey(1, 12, Keys.Oemplus, "=", "+", 1);
            SetKey(1, 13, Keys.Back, "Backspace", 2);
            
            // 第三行:QWERTY
            SetKey(2, 0, Keys.Tab, "Tab", 1.5);
            SetKey(2, 1, Keys.Q, "q", "Q", 1);
            SetKey(2, 2, Keys.W, "w", "W", 1);
            SetKey(2, 3, Keys.E, "e", "E", 1);
            SetKey(2, 4, Keys.R, "r", "R", 1);
            SetKey(2, 5, Keys.T, "t", "T", 1);
            SetKey(2, 6, Keys.Y, "y", "Y", 1);
            SetKey(2, 7, Keys.U, "u", "U", 1);
            SetKey(2, 8, Keys.I, "i", "I", 1);
            SetKey(2, 9, Keys.O, "o", "O", 1);
            SetKey(2, 10, Keys.P, "p", "P", 1);
            SetKey(2, 11, Keys.OemOpenBrackets, "[", "{", 1);
            SetKey(2, 12, Keys.OemCloseBrackets, "]", "}", 1);
            SetKey(2, 13, Keys.OemBackslash, "\\", "|", 1);
            
            // 第四行:ASDF
            SetKey(3, 0, Keys.CapsLock, "Caps", 1.75);
            SetKey(3, 1, Keys.A, "a", "A", 1);
            SetKey(3, 2, Keys.S, "s", "S", 1);
            SetKey(3, 3, Keys.D, "d", "D", 1);
            SetKey(3, 4, Keys.F, "f", "F", 1);
            SetKey(3, 5, Keys.G, "g", "G", 1);
            SetKey(3, 6, Keys.H, "h", "H", 1);
            SetKey(3, 7, Keys.J, "j", "J", 1);
            SetKey(3, 8, Keys.K, "k", "K", 1);
            SetKey(3, 9, Keys.L, "l", "L", 1);
            SetKey(3, 10, Keys.OemSemicolon, ";", ":", 1);
            SetKey(3, 11, Keys.OemQuotes, "'", "\"", 1);
            SetKey(3, 12, Keys.Enter, "Enter", 2.25);
            
            // 第五行:空格和控制键
            SetKey(4, 0, Keys.LShiftKey, "Shift", 2.25);
            SetKey(4, 1, Keys.Z, "z", "Z", 1);
            SetKey(4, 2, Keys.X, "x", "X", 1);
            SetKey(4, 3, Keys.C, "c", "C", 1);
            SetKey(4, 4, Keys.V, "v", "V", 1);
            SetKey(4, 5, Keys.B, "b", "B", 1);
            SetKey(4, 6, Keys.N, "n", "N", 1);
            SetKey(4, 7, Keys.M, "m", "M", 1);
            SetKey(4, 8, Keys.Oemcomma, ",", "<", 1);
            SetKey(4, 9, Keys.OemPeriod, ".", ">", 1);
            SetKey(4, 10, Keys.OemQuestion, "/", "?", 1);
            SetKey(4, 11, Keys.RShiftKey, "Shift", 2.25);
            SetKey(4, 12, Keys.ControlKey, "Ctrl", 1);
            SetKey(4, 13, Keys.Menu, "Alt", 1);
            SetKey(4, 14, Keys.Space, "Space", 6);
        }
    }
}

7. 键盘主题 (KeyboardTheme.cs)

csharp 复制代码
using System;
using System.Drawing;

namespace VirtualKeyboard.Keyboard
{
    public class KeyboardTheme
    {
        public string Name { get; set; }
        
        // 背景色
        public Color BackgroundColor { get; set; }
        public Color KeyBackgroundColor { get; set; }
        
        // 按键颜色
        public Color NormalColor { get; set; }
        public Color PressedColor { get; set; }
        public Color ModifierColor { get; set; }
        public Color SpecialColor { get; set; }
        
        // 边框颜色
        public Color BorderColor { get; set; }
        public Color PressedBorderColor { get; set; }
        
        // 文字颜色
        public Color TextColor { get; set; }
        public Color PressedTextColor { get; set; }
        
        // 字体
        public Font KeyFont { get; set; }
        public Font SpecialKeyFont { get; set; }
        
        public KeyboardTheme()
        {
            LoadDefaultTheme();
        }
        
        public void LoadDefaultTheme()
        {
            Name = "默认主题";
            
            // 深色主题
            BackgroundColor = Color.FromArgb(40, 40, 40);
            KeyBackgroundColor = Color.FromArgb(60, 60, 60);
            
            NormalColor = Color.FromArgb(70, 70, 70);
            PressedColor = Color.FromArgb(100, 100, 100);
            ModifierColor = Color.FromArgb(50, 50, 50);
            SpecialColor = Color.FromArgb(80, 80, 80);
            
            BorderColor = Color.FromArgb(90, 90, 90);
            PressedBorderColor = Color.FromArgb(120, 120, 120);
            
            TextColor = Color.White;
            PressedTextColor = Color.Black;
            
            KeyFont = new Font("微软雅黑", 10, FontStyle.Regular);
            SpecialKeyFont = new Font("微软雅黑", 9, FontStyle.Bold);
        }
        
        public void LoadLightTheme()
        {
            Name = "浅色主题";
            
            BackgroundColor = Color.FromArgb(240, 240, 240);
            KeyBackgroundColor = Color.White;
            
            NormalColor = Color.White;
            PressedColor = Color.FromArgb(200, 200, 200);
            ModifierColor = Color.FromArgb(220, 220, 220);
            SpecialColor = Color.FromArgb(180, 180, 180);
            
            BorderColor = Color.FromArgb(150, 150, 150);
            PressedBorderColor = Color.FromArgb(100, 100, 100);
            
            TextColor = Color.Black;
            PressedTextColor = Color.White;
        }
        
        public void LoadBlueTheme()
        {
            Name = "蓝色主题";
            
            BackgroundColor = Color.FromArgb(30, 60, 90);
            KeyBackgroundColor = Color.FromArgb(50, 80, 110);
            
            NormalColor = Color.FromArgb(70, 100, 130);
            PressedColor = Color.FromArgb(90, 120, 150);
            ModifierColor = Color.FromArgb(50, 80, 110);
            SpecialColor = Color.FromArgb(60, 90, 120);
            
            BorderColor = Color.FromArgb(80, 110, 140);
            PressedBorderColor = Color.FromArgb(100, 130, 160);
            
            TextColor = Color.White;
            PressedTextColor = Color.Black;
        }
    }
}

8. 输入模拟器 (InputSimulator.cs)

csharp 复制代码
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace VirtualKeyboard.Utils
{
    public class InputSimulator
    {
        [StructLayout(LayoutKind.Sequential)]
        private struct INPUT
        {
            public int type;
            public InputUnion u;
        }
        
        [StructLayout(LayoutKind.Explicit)]
        private struct InputUnion
        {
            [FieldOffset(0)]
            public KEYBDINPUT ki;
        }
        
        [StructLayout(LayoutKind.Sequential)]
        private struct KEYBDINPUT
        {
            public ushort wVk;
            public ushort wScan;
            public uint dwFlags;
            public uint time;
            public IntPtr dwExtraInfo;
        }
        
        private const int INPUT_KEYBOARD = 1;
        private const uint KEYEVENTF_EXTENDEDKEY = 0x0001;
        private const uint KEYEVENTF_KEYUP = 0x0002;
        private const uint KEYEVENTF_UNICODE = 0x0004;
        private const uint KEYEVENTF_SCANCODE = 0x0008;
        
        [DllImport("user32.dll", SetLastError = true)]
        private static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);
        
        [DllImport("user32.dll")]
        private static extern short VkKeyScan(char ch);
        
        public void SendKey(Keys key, bool shift = false, bool ctrl = false, bool alt = false)
        {
            // 发送修饰键
            if (shift) SendKeyDown(Keys.ShiftKey);
            if (ctrl) SendKeyDown(Keys.ControlKey);
            if (alt) SendKeyDown(Keys.Menu);
            
            // 发送主键
            SendKeyDown(key);
            SendKeyUp(key);
            
            // 释放修饰键
            if (alt) SendKeyUp(Keys.Menu);
            if (ctrl) SendKeyUp(Keys.ControlKey);
            if (shift) SendKeyUp(Keys.ShiftKey);
        }
        
        public void SendText(string text)
        {
            foreach (char c in text)
            {
                SendUnicodeChar(c);
            }
        }
        
        private void SendKeyDown(Keys key)
        {
            INPUT[] inputs = new INPUT[]
            {
                new INPUT
                {
                    type = INPUT_KEYBOARD,
                    u = new InputUnion
                    {
                        ki = new KEYBDINPUT
                        {
                            wVk = (ushort)key,
                            dwFlags = 0
                        }
                    }
                }
            };
            
            SendInput((uint)inputs.Length, inputs, Marshal.SizeOf(typeof(INPUT)));
        }
        
        private void SendKeyUp(Keys key)
        {
            INPUT[] inputs = new INPUT[]
            {
                new INPUT
                {
                    type = INPUT_KEYBOARD,
                    u = new InputUnion
                    {
                        ki = new KEYBDINPUT
                        {
                            wVk = (ushort)key,
                            dwFlags = KEYEVENTF_KEYUP
                        }
                    }
                }
            };
            
            SendInput((uint)inputs.Length, inputs, Marshal.SizeOf(typeof(INPUT)));
        }
        
        private void SendUnicodeChar(char c)
        {
            INPUT[] inputs = new INPUT[]
            {
                new INPUT
                {
                    type = INPUT_KEYBOARD,
                    u = new InputUnion
                    {
                        ki = new KEYBDINPUT
                        {
                            wScan = c,
                            dwFlags = KEYEVENTF_UNICODE
                        }
                    }
                },
                new INPUT
                {
                    type = INPUT_KEYBOARD,
                    u = new InputUnion
                    {
                        ki = new KEYBDINPUT
                        {
                            wScan = c,
                            dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP
                        }
                    }
                }
            };
            
            SendInput((uint)inputs.Length, inputs, Marshal.SizeOf(typeof(INPUT)));
        }
        
        public void SendKeyCombination(params Keys[] keys)
        {
            // 按下所有键
            foreach (var key in keys)
            {
                SendKeyDown(key);
            }
            
            // 释放所有键(反向顺序)
            for (int i = keys.Length - 1; i >= 0; i--)
            {
                SendKeyUp(keys[i]);
            }
        }
    }
}

9. 键盘钩子 (KeyboardHook.cs)

csharp 复制代码
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace VirtualKeyboard.Keyboard
{
    public class KeyboardHook
    {
        public event EventHandler<KeyEventArgs> KeyDown;
        public event EventHandler<KeyEventArgs> KeyUp;
        
        private const int WH_KEYBOARD_LL = 13;
        private const int WM_KEYDOWN = 0x0100;
        private const int WM_KEYUP = 0x0101;
        private const int WM_SYSKEYDOWN = 0x0104;
        private const int WM_SYSKEYUP = 0x0105;
        
        private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
        
        [StructLayout(LayoutKind.Sequential)]
        private struct KBDLLHOOKSTRUCT
        {
            public Keys vkCode;
            public int scanCode;
            public int flags;
            public int time;
            public IntPtr dwExtraInfo;
        }
        
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
        
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool UnhookWindowsHookEx(IntPtr hhk);
        
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
        
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr GetModuleHandle(string lpModuleName);
        
        private IntPtr hookId = IntPtr.Zero;
        private LowLevelKeyboardProc proc;
        
        public void Install()
        {
            proc = HookCallback;
            using (Process curProcess = Process.GetCurrentProcess())
            using (ProcessModule curModule = curProcess.MainModule)
            {
                hookId = SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curModule.ModuleName), 0);
            }
        }
        
        public void Uninstall()
        {
            UnhookWindowsHookEx(hookId);
        }
        
        private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
        {
            if (nCode >= 0 && (wParam == (IntPtr)WM_KEYDOWN || wParam == (IntPtr)WM_SYSKEYDOWN ||
                              wParam == (IntPtr)WM_KEYUP || wParam == (IntPtr)WM_SYSKEYUP))
            {
                KBDLLHOOKSTRUCT hookStruct = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT));
                
                bool isKeyDown = (wParam == (IntPtr)WM_KEYDOWN || wParam == (IntPtr)WM_SYSKEYDOWN);
                
                if (isKeyDown)
                {
                    KeyDown?.Invoke(this, new KeyEventArgs(hookStruct.vkCode));
                }
                else
                {
                    KeyUp?.Invoke(this, new KeyEventArgs(hookStruct.vkCode));
                }
            }
            
            return CallNextHookEx(hookId, nCode, wParam, lParam);
        }
    }
}

10. 项目文件 (VirtualKeyboard.csproj)

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{YOUR-PROJECT-GUID}</ProjectGuid>
    <OutputType>WinExe</OutputType>
    <RootNamespace>VirtualKeyboard</RootNamespace>
    <AssemblyName>VirtualKeyboard</AssemblyName>
    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <PlatformTarget>AnyCPU</PlatformTarget>
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <PlatformTarget>AnyCPU</PlatformTarget>
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <Reference Include="System.Windows.Forms" />
    <Reference Include="System.Drawing" />
    <Reference Include="System.Data" />
    <Reference Include="System.Xml" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Program.cs" />
    <Compile Include="MainForm.cs" />
    <Compile Include="MainForm.Designer.cs" />
    <Compile Include="Keyboard\VirtualKeyboardForm.cs" />
    <Compile Include="Keyboard\KeyboardLayout.cs" />
    <Compile Include="Keyboard\KeyButton.cs" />
    <Compile Include="Keyboard\KeyboardTheme.cs" />
    <Compile Include="Keyboard\KeyboardHook.cs" />
    <Compile Include="Controls\KeyboardPanel.cs" />
    <Compile Include="Layouts\EnglishLayout.cs" />
    <Compile Include="Layouts\ChineseLayout.cs" />
    <Compile Include="Layouts\NumberLayout.cs" />
    <Compile Include="Layouts\SymbolLayout.cs" />
    <Compile Include="Utils\InputSimulator.cs" />
    <Compile Include="Utils\ScreenHelper.cs" />
  </ItemGroup>
  <ItemGroup>
    <None Include="App.config" />
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

参考代码 C#写的虚拟键盘 www.youwenfan.com/contentcsv/111931.html

三、使用说明

1. 功能特点

  • 触摸友好:大按钮设计,适合触摸屏使用
  • 物理键盘同步:实时同步物理键盘状态
  • 多语言布局:支持英文、中文、数字、符号布局
  • 系统托盘:最小化到系统托盘
  • 自动隐藏:失去焦点时自动隐藏
  • 主题切换:支持深色、浅色、蓝色主题
  • 按键重复:长按按键自动重复
  • 组合键:支持Ctrl+C、Ctrl+V等组合键

2. 使用方法

  1. 运行程序,虚拟键盘会自动显示在屏幕底部
  2. 点击系统托盘图标可以显示/隐藏键盘
  3. 点击键盘按钮即可输入字符
  4. 支持Shift、Ctrl、Alt等修饰键
  5. 支持Caps Lock和Num Lock

3. 扩展功能建议

  1. 手写识别:添加手写输入面板
  2. 语音输入:集成语音转文字功能
  3. 预测输入:智能联想和预测输入
  4. 多显示器支持:在不同显示器上显示键盘
  5. 自定义布局:允许用户自定义键盘布局
  6. 宏录制:支持录制和播放宏命令
  7. 密码保护:安全输入模式,防止截屏
相关推荐
你疯了抱抱我2 小时前
【STM32】使用 STM32CubeMX 生成项目,LED测试;上位机:STM32F411CEU6
stm32·单片机·嵌入式硬件
周杰伦fans3 小时前
AutoCAD C# 二次开发:如何精确监听工作空间切换事件
前端·c#
用户3721574261353 小时前
如何使用 C# 自动调整 Excel 行高和列宽
c#
AI导出鸭PC端3 小时前
智谱清言怎么生成word文档?AI导出鸭终结乱码烦恼
人工智能·ai·c#·word·豆包·ai导出鸭
今天的你比昨天进步了?4 小时前
单片机程序,keil可以正常编译,VScode编译报错处理
vscode·单片机·嵌入式硬件
xiaoshuaishuai85 小时前
C# AvaloniaUI 中旋转
开发语言·c#
weixin_428005305 小时前
C#调用 AI学习从0开始-第2阶段(Function Calling+工具调用智能体)-第9天实战-实现计算器工具
开发语言·学习·c#·functioncalling·ai实现计算器工具
崇山峻岭之间5 小时前
单片机汉字显示实验
单片机·嵌入式硬件