WPF开发经验-实现Win10虚拟触摸键盘

2022-11-21,,,,

一 引入

项目有个需求,需要实现纯触控操作进行键盘输入。项目部署在Win10系统上,考虑有两种方案来实现。

    通过调用Win10自带的触摸键盘来实现;
    通过WPF实现一个触摸键盘来实现;

二 调用Win10自带的触摸键盘

简单附上一个调用Win10系统的TabTip.exe的操作类。

public class TabTipHelper
{
private const int WM_SYSCOMMAND = 274;
private const uint SC_CLOSE = 61536;
private const uint SC_RESTORE = 0xF120;
private const uint SC_MAXIMIZE = 0xF030;
private const uint SC_MINIMIZE = 0xF020; [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern bool PostMessage(IntPtr hWnd, int Msg, int wParam, int lParam); [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern bool PostMessage(IntPtr hWnd, int Msg, uint wParam, uint lParam); [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int RegisterWindowMessage(string lpString); public static int ShowTaptip()
{
try
{
dynamic file = "C:\\Program Files\\Common Files\\microsoft shared\\ink\\TabTip.exe";
if (!System.IO.File.Exists(file))
return -1;
Process.Start(file);
return 1;
}
catch (Exception)
{
return 255;
}
} public static async Task ShowTaptipAsync()
{
await Task.Run(() => ShowTaptip());
} public static void CloseTaptip()
{
var touchhWnd = new IntPtr(0);
touchhWnd = FindWindow("IPTip_Main_Window", null);
if (touchhWnd == IntPtr.Zero)
return;
PostMessage(touchhWnd, WM_SYSCOMMAND, SC_CLOSE, 0);
}
}

三 通过WPF实现一个触摸键盘

下图为demo效果:

当TextBox获取焦点时,弹出键盘。当TextBox失去焦点时,隐藏键盘。

点击键盘的右上角的关闭键或Esc键隐藏键盘。

因项目不需要全键位,所有针对性地对键位进行了删减和排列。



3.1 SoftKeyboardControl控件

使用WPF的UserControl实现虚拟键盘,下附SoftKeyboardControl的后台代码。

    /// <summary>
/// SoftKeyboardControl.xaml 的交互逻辑
/// </summary>
public partial class SoftKeyboardControl : UserControl
{
/// <summary>
/// Windows提供的一个模拟键盘API。
/// Keybd_event()函数能触发一个按键事件,生成一个 WM_KEYDOWN 或 WM_KEYUP 消息。
/// </summary>
/// <param name="bVk" >按键的虚拟键值</param>
/// <param name= "bScan" >扫描码,一般不用设置,用0代替就行</param>
/// <param name= "dwFlags" >选项标志:0:表示按下,2:表示松开</param>
/// <param name= "dwExtraInfo">一般设置为0</param>
[DllImport("User32.dll")]
public static extern void keybd_event(byte bVK, byte bScan, int dwFlags, int dwExtraInfo); public SoftKeyboardControl()
{
InitializeComponent();
CreateKeys();
KeyCommand = new RoutedCommand();
CommandBinding cmdBinding = new CommandBinding(KeyCommand, KeyCommand_Excuted, KeyCommand_CanExcute);
CommandBindings.Add(cmdBinding);
} #region 虚拟键值 /// <summary>
/// 键和虚拟键值的字典
/// </summary>
private Dictionary<string, byte> keys; /// <summary>
/// 初始化虚拟键值的字典
/// </summary>
private void CreateKeys()
{
keys = new Dictionary<string, byte>()
{
#region 数字键盘 { "0",0x60},
{ "1",0x61},
{ "2",0x62},
{ "3",0x63},
{ "4",0x64},
{ "5",0x65},
{ "6",0x66},
{ "7",0x67},
{ "8",0x68},
{ "9",0x69}, { "-",0x6d},
{ ".",0x6E}, #endregion #region Q字母行 { "q", 0x51},
{ "w", 0x57},
{ "e", 0x45},
{ "r", 0x52},
{ "t", 0x54},
{ "y", 0x59},
{ "u", 0x55},
{ "i", 0x49},
{ "o", 0x4f},
{ "p", 0x50}, { "Q", 0x51},
{ "W", 0x57},
{ "E", 0x45},
{ "R", 0x52},
{ "T", 0x54},
{ "Y", 0x59},
{ "U", 0x55},
{ "I", 0x49},
{ "O", 0x4f},
{ "P", 0x50}, #endregion #region A字母行 { "a", 0x41},
{ "s", 0x53},
{ "d", 0x44},
{ "f", 0x46},
{ "g", 0x47},
{ "h", 0x48},
{ "j", 0x4A},
{ "k", 0x4B},
{ "l", 0x4C}, { "A", 0x41},
{ "S", 0x53},
{ "D", 0x44},
{ "F", 0x46},
{ "G", 0x47},
{ "H", 0x48},
{ "J", 0x4A},
{ "K", 0x4B},
{ "L", 0x4C}, #endregion #region Z字母行 { "z", 0x5A},
{ "x", 0x58},
{ "c", 0x43},
{ "v", 0x56},
{ "b", 0x42},
{ "n", 0x4E},
{ "m", 0x4D}, { "Z", 0x5A},
{ "X", 0x58},
{ "C", 0x43},
{ "V", 0x56},
{ "B", 0x42},
{ "N", 0x4E},
{ "M", 0x4D}, #endregion #region other {"BackSpace",0x08 },
{"Tab",0x09 },
{"Enter",0x0d },
{"Shift",0x10 },
{"Ctrl",0x11 },
{"CapsLock",0x14 },
{"Space",0x20 }, #endregion
};
} #endregion #region DependencyProperty public static readonly DependencyProperty CapsProperty = DependencyProperty.Register(
"Caps", typeof(bool), typeof(SoftKeyboardControl), new PropertyMetadata(default(bool)));
public bool Caps
{
get { return (bool)GetValue(CapsProperty); }
set { SetValue(CapsProperty, value); }
} public static readonly DependencyProperty CtrlProperty = DependencyProperty.Register(
"Ctrl", typeof(bool), typeof(SoftKeyboardControl), new PropertyMetadata(default(bool))); public bool Ctrl
{
get { return (bool)GetValue(CtrlProperty); }
set { SetValue(CtrlProperty, value); }
} public static readonly DependencyProperty ShiftProperty = DependencyProperty.Register(
"Shift", typeof(bool), typeof(SoftKeyboardControl), new PropertyMetadata(default(bool))); public bool Shift
{
get { return (bool)GetValue(ShiftProperty); }
set { SetValue(ShiftProperty, value); }
} #endregion #region Command public ICommand KeyCommand { get; set; }
private void KeyCommand_CanExcute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
private void KeyCommand_Excuted(object sender, ExecutedRoutedEventArgs e)
{
if (e.Parameter != null)
{
var code = e.Parameter.ToString();
if (keys.ContainsKey(code))
{
byte b = keys[code];
try
{
keybd_event(b, 0, 0, 0);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
} public ICommand CloseCommand { get; set; } #endregion protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (e.Property == CapsProperty)
{
keybd_event(0x14, 0, 0, 0);//按下
keybd_event(0x14, 0, 2, 0); //抬起
}
else if (e.Property == ShiftProperty)
{
keybd_event(0x10, 0, 0, 0);
keybd_event(0x10, 0, 2, 0);
}
else if (e.Property == CtrlProperty)
{
keybd_event(0x11, 0, 0, 0);
keybd_event(0x11, 0, 2, 0);
}
}
} /// <summary>
/// 大小写转换
/// </summary>
public class UpperConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (parameter == null)
{
return "";
} if (value == null)
{
return parameter.ToString();
} if ((bool)value)
{
return parameter.ToString().ToUpper();
}
else
{
return parameter.ToString().ToLower();
}
} public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

下附SoftKeyboardControl的前台代码。

注:其中按钮的样式是自己的控件库中实现的,未贴出。

<UserControl x:Class="UI.View.SoftKeyboardControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:UI.View"
mc:Ignorable="d"
Background="#E6E6E6">
<UserControl.Resources>
<local:UpperConverter x:Key="UpperConverter"/>
</UserControl.Resources>
<StackPanel Margin="6 0 6 6">
<StackPanel Height="40" Orientation="Horizontal" >
<!--关闭按钮-->
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0 6 0 0" >
<StackPanel>
<WrapPanel>
<Button Content="Esc" Width="85" Height="Auto" Padding="0"
Command="{Binding CloseCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}" />
<Button Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}},
Converter={StaticResource UpperConverter},ConverterParameter=q}"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
<Button Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}},
Converter={StaticResource UpperConverter},ConverterParameter=w}"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
<Button Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}},
Converter={StaticResource UpperConverter},ConverterParameter=e}"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
<Button Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}},
Converter={StaticResource UpperConverter},ConverterParameter=r}"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
<Button Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}},
Converter={StaticResource UpperConverter},ConverterParameter=t}"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
<Button Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}},
Converter={StaticResource UpperConverter},ConverterParameter=y}"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
<Button Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}},
Converter={StaticResource UpperConverter},ConverterParameter=u}"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
<Button Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}},
Converter={StaticResource UpperConverter},ConverterParameter=i}"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
<Button Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}},
Converter={StaticResource UpperConverter},ConverterParameter=o}"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
<Button Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}},
Converter={StaticResource UpperConverter},ConverterParameter=p}"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
</WrapPanel>
<WrapPanel>
<ToggleButton Width="100" Content="Caps"
IsChecked="{Binding Caps,Mode=TwoWay,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"/>
<Button Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}},
Converter={StaticResource UpperConverter},ConverterParameter=a}"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
<Button Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}},
Converter={StaticResource UpperConverter},ConverterParameter=s}"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
<Button Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}},
Converter={StaticResource UpperConverter},ConverterParameter=d}"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
<Button Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}},
Converter={StaticResource UpperConverter},ConverterParameter=f}"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
<Button Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}},
Converter={StaticResource UpperConverter},ConverterParameter=g}"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
<Button Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}},
Converter={StaticResource UpperConverter},ConverterParameter=h}"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
<Button Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}},
Converter={StaticResource UpperConverter},ConverterParameter=j}"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
<Button Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}},
Converter={StaticResource UpperConverter},ConverterParameter=k}"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
<Button Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}},
Converter={StaticResource UpperConverter},ConverterParameter=l}"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
</WrapPanel>
<WrapPanel>
<ToggleButton Width="115" Padding="-18 0 0 0 " Content="Shift"
IsChecked="{Binding Shift,Mode=TwoWay,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"/>
<Button Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}},
Converter={StaticResource UpperConverter},ConverterParameter=z}"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
<Button Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}},
Converter={StaticResource UpperConverter},ConverterParameter=x}"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
<Button Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}},
Converter={StaticResource UpperConverter},ConverterParameter=c}"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
<Button Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}},
Converter={StaticResource UpperConverter},ConverterParameter=v}"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
<Button Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}},
Converter={StaticResource UpperConverter},ConverterParameter=b}"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
<Button Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}},
Converter={StaticResource UpperConverter},ConverterParameter=n}"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
<Button Content="{Binding Path=Caps,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}},
Converter={StaticResource UpperConverter},ConverterParameter=m}"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
<Button Width="125" Content="Enter"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
</WrapPanel>
<WrapPanel>
<ToggleButton Width="115" Padding="-28 0 0 0 " Content="Ctrl"
IsChecked="{Binding Ctrl,Mode=TwoWay,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"/>
<Button Content="Space" Width="185" Margin="50 0 0 0"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
<Button Content="BackSpace" Width="170" Margin="50 0 0 0"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
</WrapPanel>
</StackPanel>
<Grid Width="auto" Height="auto">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button Content="7"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
<Button Content="8" Grid.Column="1"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
<Button Content="9" Grid.Column="2"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
<Button Content="4" Grid.Row="1"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
<Button Content="5" Grid.Row="1" Grid.Column="1"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
<Button Content="6" Grid.Row="1" Grid.Column="2"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
<Button Content="1" Grid.Row="2"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
<Button Content="2" Grid.Row="2" Grid.Column="1"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
<Button Content="3" Grid.Row="2" Grid.Column="2"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
<Button Content="-" Grid.Row="3"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
<Button Content="0" Grid.Row="3" Grid.Column="1"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
<Button Content="." Grid.Row="3" Grid.Column="2"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/> <Button Content="Esc" Grid.Row="0" Grid.Column="3" Grid.RowSpan="2" Margin="0,0,0,5" Width="56" Height="Auto" Padding="0"
Command="{Binding CloseCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}" /> <Button Grid.Row="2" Grid.Column="3" Grid.RowSpan="2" Height="auto" Width="56" Margin="0 0 0 4" Padding="0"
Content="Enter"
Command="{Binding KeyCommand,RelativeSource={RelativeSource AncestorType={x:Type local:SoftKeyboardControl}}}"
CommandParameter="{Binding Path=Content,RelativeSource={RelativeSource Self}}"/>
</Grid>
</StackPanel>
</StackPanel>
</UserControl>

3.2 Demo界面的代码

下附Demo界面后台代码。

/// <summary>
/// TestWindow.xaml 的交互逻辑
/// </summary>
public partial class TestWindow : Window
{
public TestWindow()
{
InitializeComponent(); softKeyboard.CloseCommand = new RoutedCommand();
CommandBinding cmdBinding = new CommandBinding(softKeyboard.CloseCommand, CloseCommand_Excuted, CloseCommand_CanExcute);
CommandBindings.Add(cmdBinding);
} private void CloseCommand_CanExcute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = popup.IsOpen;
} private void CloseCommand_Excuted(object sender, ExecutedRoutedEventArgs e)
{
popup.IsOpen = false;
textBox.Focus();
} private void TextBox_GotFocus(object sender, RoutedEventArgs e)
{
popup.IsOpen = true;
} private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
popup.IsOpen = false;
}
}

下附Demo界面的前台代码。

<Window x:Class="UI.View.TestWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:view="clr-namespace:UI.View"
Title="TestWindow" Height="800" Width="1400">
<Grid x:Name="grid">
<TextBox Margin="415,233,447,0" FontSize="30" Height="62"
VerticalContentAlignment="Center" VerticalAlignment="Top"
GotFocus="TextBox_GotFocus" LostFocus="TextBox_LostFocus" />
<TextBox x:Name="textBox" Margin="470,343,503,364" />
<Popup x:Name="popup"
StaysOpen="True"
AllowsTransparency="True"
PopupAnimation="Fade"
PlacementTarget="{Binding ElementName = grid}"
Placement="Bottom"
HorizontalOffset="250"
VerticalOffset="-280">
<view:SoftKeyboardControl x:Name="softKeyboard"/>
</Popup>
</Grid>
</Window>

四 参考链接

虚拟键码 (Winuser.h) - Win32 apps | Microsoft Docs

keybd_event函数 (winuser.h) - Win32 应用|微软文档 (microsoft.com)

WPF实现软键盘 - 我是刹那、 - 博客园 (cnblogs.com)

WPF开发经验-实现Win10虚拟触摸键盘的相关教程结束。

《WPF开发经验-实现Win10虚拟触摸键盘.doc》

下载本文的Word格式文档,以方便收藏与打印。