WPF 虚拟键盘
效果:
键盘,放大缩小,键位大小及字体自适应大小
源码下载

背景
Windows 提供了一个模拟键盘 API 函数 Keybd_event(),使用该函数可以相应的屏蔽键盘的动作。Keybd_event()函数能触发一个按键事件,也就是说会产生一个 WM_KEYDOWN 或 WM_KEYUP 消息。
public static partial class KeyHelper
{
[DllImport("user32.dll", EntryPoint = "keybd_event")]
public static extern void keybd_event(
byte bVk, //虚拟键值
byte bScan,// 一般为0
int dwFlags, //这里是整数类型 0 为按下,2为释放
int dwExtraInfo //这里是整数类型 一般情况下设成为0
);
[DllImport("user32.dll")]
public static extern int GetFocus();
}
/// <summary> 模拟键盘按键 </summary>
public static partial class KeyHelper
{
#region 模拟按键
public static void Play()
{
keybd_event(179, 0, 0, 0);
keybd_event(179, 0, 2, 0);
}
public static void Stop()
{
keybd_event(178, 0, 0, 0);
keybd_event(178, 0, 2, 0);
}
public static void Last()
{
keybd_event(177, 0, 0, 0);
keybd_event(177, 0, 2, 0);
}
public static void Next()
{
keybd_event(176, 0, 0, 0);
keybd_event(176, 0, 2, 0);
}
#endregion
/// <summary> 模拟按下指定建 Keys.ControlKey </summary>
public static void OnKeyPress(byte keyCode)
{
keybd_event(keyCode, 0, 0, 0);
keybd_event(keyCode, 0, 2, 0);
}
public static void OnKeyDown(byte keyCode)
{
keybd_event(keyCode, 0, 0, 0);
}
public static void OnKeyUp(byte keyCode)
{
keybd_event(keyCode, 0, 2, 0);
}
static Dictionary<string, string> _dic = new Dictionary<string, string>();
public static Dictionary<string, string> CodeToByte
{
get
{
if(_dic.Count==0)
{
ConvertASCII();
}
return _dic;
}
}
static void ConvertASCII()
{
string configer = @"0010 0000 32 20 (空格)(␠)
0010 0001 33 21 !
0010 0010 34 22 双引号
00100011 35 23 #
00100100 36 24 $
00100101 37 25 %
00100110 38 26 &
00100111 39 27 '
00101000 40 28 (
00101001 41 29 )
00101010 42 2A *
00101011 43 2B +
00101100 44 2C ,
00101101 45 2D -
00101110 46 2E .
00101111 47 2F /
00110000 48 30 0
00110001 49 31 1
00110010 50 32 2
00110011 51 33 3
00110100 52 34 4
00110101 53 35 5
00110110 54 36 6
00110111 55 37 7
00111000 56 38 8
00111001 57 39 9
00111010 58 3A :
00111011 59 3B ;
00111100 60 3C <
00111101 61 3D =
00111110 62 3E >
00111111 63 3F ?
01000000 64 40 @
01000001 65 41 A
01000010 66 42 B
01000011 67 43 C
01000100 68 44 D
01000101 69 45 E
01000110 70 46 F
01000111 71 47 G
01001000 72 48 H
01001001 73 49 I
01001010 74 4A J
01001011 75 4B K
01001100 76 4C L
01001101 77 4D M
01001110 78 4E N
01001111 79 4F O
01010000 80 50 P
01010001 81 51 Q
01010010 82 52 R
01010011 83 53 S
01010100 84 54 T
01010101 85 55 U
01010110 86 56 V
01010111 87 57 W
01011000 88 58 X
01011001 89 59 Y
01011010 90 5A Z
01011011 91 5B [
01011100 92 5C \
01011101 93 5D ]
01011110 94 5E ^
01011111 95 5F _
01100000 96 60 `
01100001 97 61 a
01100010 98 62 b
01100011 99 63 c
01100100 100 64 d
01100101 101 65 e
01100110 102 66 f
01100111 103 67 g
01101000 104 68 h
01101001 105 69 i
01101010 106 6A j
01101011 107 6B k
01101100 108 6C l
01101101 109 6D m
01101110 110 6E n
01101111 111 6F o
01110000 112 70 p
01110001 113 71 q
01110010 114 72 r
01110011 115 73 s
01110100 116 74 t
01110101 117 75 u
01110110 118 76 v
01110111 119 77 w
01111000 120 78 x
01111001 121 79 y
01111010 122 7A z
01111011 123 7B {
01111100 124 7C |
01111101 125 7D }
01111110 126 7E ~";
var collection = configer.Split('\r');
foreach (var item in collection)
{
var current = item.Split(' ', '\t').ToList();
current.RemoveAll(l => string.IsNullOrEmpty(l));
var result = current.ToList().Aggregate((l, k) => l + "-" + k);
if (current.Count != 4)
{
Debug.WriteLine("ERR:" + result);
}
else
{
_dic.Add(current[3], current[1]);
Debug.WriteLine(result);
}
}
}
KeyBoardControl :
/// <summary>
/// InputNumControl.xaml 的交互逻辑
/// </summary>
public partial class KeyBoardControl : UserControl
{
public KeyBoardControl()
{
InitializeComponent();
this.btn_caps.IsChecked = Console.CapsLock;
this.RefreshCaps();
}
void RefreshCapsText()
{
if (this.btn_caps.IsChecked)
{
this.btn_caps.Content = "大写";
}
else
{
this.btn_caps.Content = "小写";
}
}
private void Grid_Click(object sender, RoutedEventArgs e)
{
FuncButtonControl button = e.OriginalSource as FuncButtonControl;
if (button == null) return;
Debug.WriteLine(button.Content);
//if (button.Equals(this.btn_win))
// return;
string content = button.Content.ToString();
if (content == "确定")
{
this.OnSumitClick();
}
else if (content == "取消")
{
this.OnCancelClick();
}
else
{
Do :打开大写
if (this.btn_shift.IsChecked)
{
KeyHelper.OnKeyDown(Convert.ToByte(this.btn_shift.Tag.ToString()));
}
if (this.btn_shift.IsChecked)
{
KeyHelper.OnKeyDown(Convert.ToByte(this.btn_shift.Tag.ToString()));
}
if (this.btn_win.IsChecked)
{
KeyHelper.OnKeyDown(Convert.ToByte(this.btn_win.Tag.ToString()));
}
string tag = button.Tag.ToString();
byte b = Convert.ToByte(tag);
KeyHelper.OnKeyPress(b);
}
e.Handled = false;
}
//声明和注册路由事件
public static readonly RoutedEvent SumitClickRoutedEvent =
EventManager.RegisterRoutedEvent("SumitClick", RoutingStrategy.Bubble, typeof(EventHandler<RoutedEventArgs>), typeof(KeyBoardControl));
//CLR事件包装
public event RoutedEventHandler SumitClick
{
add { this.AddHandler(SumitClickRoutedEvent, value); }
remove { this.RemoveHandler(SumitClickRoutedEvent, value); }
}
//激发路由事件,借用Click事件的激发方法
protected void OnSumitClick()
{
RoutedEventArgs args = new RoutedEventArgs(SumitClickRoutedEvent, this);
this.RaiseEvent(args);
}
//声明和注册路由事件
public static readonly RoutedEvent CancelClickRoutedEvent =
EventManager.RegisterRoutedEvent("CancelClick", RoutingStrategy.Bubble, typeof(EventHandler<RoutedEventArgs>), typeof(KeyBoardControl));
//CLR事件包装
public event RoutedEventHandler CancelClick
{
add { this.AddHandler(CancelClickRoutedEvent, value); }
remove { this.RemoveHandler(CancelClickRoutedEvent, value); }
}
//激发路由事件,借用Click事件的激发方法
protected void OnCancelClick()
{
RoutedEventArgs args = new RoutedEventArgs(CancelClickRoutedEvent, this);
this.RaiseEvent(args);
}
public string InputText
{
get { return (string)GetValue(InputTextProperty); }
set { SetValue(InputTextProperty, value); }
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty InputTextProperty =
DependencyProperty.Register("InputText", typeof(string), typeof(KeyBoardControl), new PropertyMetadata(default(string), (d, e) =>
{
KeyBoardControl control = d as KeyBoardControl;
if (control == null) return;
string config = e.NewValue as string;
control.txt_input.Text = config;
}));
private void Btn_Click(object sender, RoutedEventArgs e)
{
FuncButtonControl btn = sender as FuncButtonControl;
string tag = btn.Tag.ToString();
byte b = Convert.ToByte(tag);
if (btn.IsChecked)
{
KeyHelper.OnKeyUp(b);
}
else
{
KeyHelper.OnKeyDown(b);
}
btn.IsChecked = !btn.IsChecked;
}
void RefreshCaps()
{
var btns = FindVisualChild<FuncButtonControl>(this.grid_center);
foreach (var btn in btns)
{
if (btn.Content == null) continue;
if (btn.Content.ToString().Length != 1) continue;
btn.Content = this.btn_caps.IsChecked ? btn.Content.ToString().ToUpper() : btn.Content.ToString().ToLower();
}
this.RefreshCapsText();
}
List<T> FindVisualChild<T>(DependencyObject obj) where T : DependencyObject
{
List<T> collecion = new List<T>();
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is T)
collecion.Add((T)child);
else
{
collecion.AddRange(FindVisualChild<T>(child));
}
}
return collecion;
}
private void btn_caps_Click(object sender, RoutedEventArgs e)
{
FuncButtonControl btn = sender as FuncButtonControl;
string tag = btn.Tag.ToString();
byte b = Convert.ToByte(tag);
KeyHelper.OnKeyDown(b);
btn.IsChecked = !btn.IsChecked;
this.RefreshCaps();
}
private void TextBlock_MouseDown(object sender, MouseButtonEventArgs e)
{
this.OnCancelClick();
}
}
<Border Focusable="False" Grid.Row="1" Background="LightGray" Opacity="1" CornerRadius="0">
<Grid Focusable="False">
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
<RowDefinition/>
</Grid.RowDefinitions>
<DockPanel LastChildFill="False" Focusable="False">
<!--<TextBlock DockPanel.Dock="Left" Text="SK.Lai虚拟键盘" Margin="20 0 0 0" VerticalAlignment="Center" Focusable="False"/>-->
<Grid DockPanel.Dock="Right" Height="20" Width="20" Margin="0 0 0 0" Focusable="False">
<Ellipse Focusable="False" Fill="#FFB47E7E"/>
<TextBlock Focusable="False" Text="×" HorizontalAlignment="Center" VerticalAlignment="Center" MouseDown="TextBlock_MouseDown" Foreground="White" FontSize="18" />
</Grid>
</DockPanel>
<Grid Focusable="False" Grid.Row="1" x:Name="grid_center" ButtonBase.Click="Grid_Click" Margin="0">
<Grid.RowDefinitions>
<RowDefinition Height="0"/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid Visibility="Collapsed">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5*"/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBox HorizontalContentAlignment="Center" x:Name="txt_input" Foreground="Black"
BorderBrush="#1fd800" BorderThickness="1.5" Height="80" FontWeight="Bold" FontSize="30"
CaretBrush="Black" VerticalAlignment="Center" VerticalContentAlignment="Center" HorizontalAlignment="Left"
Text="{Binding ElementName=control,Path=InputText}"
Width="800" />
<local:FuncButtonControl Grid.Row="0" Grid.Column="2" Grid.ColumnSpan="1" Width="200" Content="确定">
<Button.Background>
<ImageBrush ImageSource="Image/确认按钮.png" />
</Button.Background>
</local:FuncButtonControl>
<local:FuncButtonControl Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="1" Width="200" Content="取消"/>
</Grid>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<local:FuncButtonControl Grid.Row="1" Grid.Column="0" Content="Esc" Tag="23"/>
<local:FuncButtonControl IsEnabled="True" Grid.Row="1" Grid.Column="1" Content="`" Tag="192" ShiftValue="~"/>
<local:FuncButtonControl Grid.Row="1" Grid.Column="2" Content="1" Tag="49" ShiftValue="!"/>
<local:FuncButtonControl Grid.Row="1" Grid.Column="3" Content="2" Tag="50" ShiftValue="@"/>
<local:FuncButtonControl Grid.Row="2" Grid.Column="4" Content="3" Tag="51" ShiftValue="#"/>
<local:FuncButtonControl Grid.Row="2" Grid.Column="5" Content="4" Tag="52" ShiftValue="$"/>
<local:FuncButtonControl Grid.Row="2" Grid.Column="6" Content="5" Tag="53" ShiftValue="%"/>
<local:FuncButtonControl Grid.Row="3" Grid.Column="7" Content="6" Tag="54" ShiftValue="^"/>
<local:FuncButtonControl Grid.Row="3" Grid.Column="8" Content="7" Tag="55" ShiftValue="&"/>
<local:FuncButtonControl Grid.Row="3" Grid.Column="9" Content="8" Tag="56" ShiftValue="*"/>
<local:FuncButtonControl Grid.Row="3" Grid.Column="10" Content="9" Tag="57" ShiftValue="("/>
<local:FuncButtonControl Grid.Row="1" Grid.Column="11" Content="0" Tag="48" ShiftValue=")"/>
<local:FuncButtonControl Grid.Row="1" Grid.Column="12" Content="-" Tag="189" ShiftValue="_"/>
<local:FuncButtonControl Grid.Row="1" Grid.Column="13" Content="=" Tag="61" ShiftValue="+"/>
<local:FuncButtonControl Style="{StaticResource Resource.Style.FuncButton.Lenght.2}" Grid.Row="1" Grid.Column="14" Content=" " Tag="8"/>
<local:FuncButtonControl Style="{StaticResource Resource.Style.FuncButton.Lenght.2}" Grid.Row="1" Grid.Column="14" Content=" " Tag="8" IsHitTestVisible="False">
<Button.Background>
<ImageBrush ImageSource="Image/Backspace.png" Stretch="None" />
</Button.Background>
</local:FuncButtonControl>
</Grid>
<Grid Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<local:FuncButtonControl Grid.Row="1" Grid.Column="0" Style="{StaticResource Resource.Style.FuncButton.Lenght.2}" Content="Tab" Tag="9"/>
<local:FuncButtonControl Grid.Row="1" Grid.Column="1" Content="q" Tag="81"/>
<local:FuncButtonControl Grid.Row="1" Grid.Column="2" Content="w" Tag="87"/>
<local:FuncButtonControl Grid.Row="1" Grid.Column="3" Content="e" Tag="69" />
<local:FuncButtonControl Grid.Row="2" Grid.Column="4" Content="r" Tag="82"/>
<local:FuncButtonControl Grid.Row="2" Grid.Column="5" Content="t" Tag="84"/>
<local:FuncButtonControl Grid.Row="2" Grid.Column="6" Content="y" Tag="89"/>
<local:FuncButtonControl Grid.Row="3" Grid.Column="7" Content="u" Tag="85"/>
<local:FuncButtonControl Grid.Row="3" Grid.Column="8" Content="i" Tag="73" />
<local:FuncButtonControl Grid.Row="3" Grid.Column="9" Content="o" Tag="79"/>
<local:FuncButtonControl Grid.Row="3" Grid.Column="10" Content="p" Tag="80"/>
<local:FuncButtonControl Grid.Row="1" Grid.Column="11" Content="[" Tag="219" ShiftValue=" {"/>
<local:FuncButtonControl Grid.Row="1" Grid.Column="12" Content="]" Tag="221" ShiftValue="}"/>
<local:FuncButtonControl Grid.Row="1" Grid.Column="13" Content="\" Tag="226" ShiftValue="|"/>
<local:FuncButtonControl Grid.Row="1" Grid.Column="14" Content="Del" Tag="46"/>
</Grid>
<Grid Grid.Row="3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2.5*"/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition Width="2.5*"/>
</Grid.ColumnDefinitions>
<local:FuncButtonControl x:Name="btn_caps" Click="btn_caps_Click" Style="{StaticResource Resource.Style.FuncButton.Lenght.2.5}"
IsCheckedControl="True"
Grid.Column="0"
Content="Caps" Tag="20" />
<local:FuncButtonControl Grid.Row="1" Grid.Column="1" Content="a" Tag="65"/>
<local:FuncButtonControl Grid.Row="1" Grid.Column="2" Content="s" Tag="83" />
<local:FuncButtonControl Grid.Row="1" Grid.Column="3" Content="d" Tag="68"/>
<local:FuncButtonControl Grid.Row="2" Grid.Column="4" Content="f" Tag="70" />
<local:FuncButtonControl Grid.Row="2" Grid.Column="5" Content="g" Tag="71"/>
<local:FuncButtonControl Grid.Row="2" Grid.Column="6" Content="h" Tag="72"/>
<local:FuncButtonControl Grid.Row="3" Grid.Column="7" Content="j" Tag="74"/>
<local:FuncButtonControl Grid.Row="3" Grid.Column="8" Content="k" Tag="75"/>
<local:FuncButtonControl Grid.Row="3" Grid.Column="9" Content="l" Tag="76"/>
<local:FuncButtonControl Grid.Row="3" Grid.Column="10" Content=";" Tag="59" ShiftValue=":"/>
<local:FuncButtonControl Grid.Row="1" Grid.Column="11" Content="'" Tag="8" ShiftValue="""/>
<local:FuncButtonControl Style="{StaticResource Resource.Style.FuncButton.Lenght.2.5}" Grid.Row="1" Grid.Column="14" Content="Enter" Tag="13"/>
</Grid>
<Grid Grid.Row="4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2.5*"/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition Width="2.5*"/>
</Grid.ColumnDefinitions>
<local:FuncButtonControl Style="{StaticResource Resource.Style.FuncButton.Lenght.2.5}"
x:Name="btn_shift" Click="Btn_Click"
Grid.Row="1"
Grid.Column="0"
Content="Shift" Tag="160"/>
<local:FuncButtonControl Grid.Row="1" Grid.Column="1" Content="z" Tag="90"/>
<local:FuncButtonControl Grid.Row="1" Grid.Column="2" Content="x" Tag="88" />
<local:FuncButtonControl Grid.Row="1" Grid.Column="3" Content="c" Tag="67" />
<local:FuncButtonControl Grid.Row="2" Grid.Column="4" Content="v" Tag="86" />
<local:FuncButtonControl Grid.Row="2" Grid.Column="5" Content="b" Tag="66"/>
<local:FuncButtonControl Grid.Row="2" Grid.Column="6" Content="n" Tag="78" />
<local:FuncButtonControl Grid.Row="3" Grid.Column="7" Content="m" Tag="77"/>
<local:FuncButtonControl Grid.Row="3" Grid.Column="8" Content="," Tag="219" ShiftValue="<" />
<!--188-->
<local:FuncButtonControl Grid.Row="3" Grid.Column="9" Content="." Tag="119" ShiftValue=">" />
<local:FuncButtonControl Grid.Row="3" Grid.Column="10" Content="/" Tag="111" ShiftValue="?" />
<local:FuncButtonControl Grid.Row="1" Grid.Column="11" Content="∧" Tag="38"/>
<local:FuncButtonControl Style="{StaticResource Resource.Style.FuncButton.Lenght.2.5}" IsCheckedControl="True" Grid.Row="1" Grid.Column="14" Content="Shift" Tag="161"/>
</Grid>
<Grid Grid.Row="5 ">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition Width="5*"/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<local:FuncButtonControl IsEnabled="False" IsCheckedControl="True" Grid.Row="1" Grid.Column="0" Content="Fn" Tag="49"/>
<local:FuncButtonControl Click="Btn_Click" IsCheckedControl="True" Grid.Row="1" Grid.Column="1" Content="Ctrl" Tag="162"/>
<local:FuncButtonControl x:Name="btn_win" Click="Btn_Click" IsEnabled="True" IsCheckedControl="True" Grid.Row="1" Grid.Column="2" Content="Win" Tag="91"/>
<local:FuncButtonControl Click="Btn_Click" IsCheckedControl="True" Grid.Row="1" Grid.Column="3" Content="Alt" Tag="164" />
<local:FuncButtonControl Grid.Row="2" Style="{StaticResource Resource.Style.FuncButton.Lenght.5.5}" Grid.Column="4" Content="Space" Tag="32"/>
<local:FuncButtonControl Click="Btn_Click" IsCheckedControl="True" Grid.Row="2" Grid.Column="5" Content="Alt" Tag="165"/>
<local:FuncButtonControl Click="Btn_Click" IsCheckedControl="True" Grid.Row="2" Grid.Column="6" Content="Ctrl" Tag="163" />
<local:FuncButtonControl Grid.Row="3" Grid.Column="7" Content="<" Tag="37"/>
<local:FuncButtonControl Grid.Row="3" Grid.Column="8" Content="∨" Tag="40" />
<local:FuncButtonControl Grid.Row="3" Grid.Column="9" Content=">" Tag="39" />
<local:FuncButtonControl IsEnabled="False" Style="{StaticResource Resource.Style.FuncButton.Lenght.2}" Grid.Row="1" Grid.Column="10" Grid.ColumnSpan="2" Content="右键" Tag="2"/>
</Grid>
</Grid>
</Grid>
</Border>