├── WpfKb.TestClient ├── Resources │ ├── AbstractBlue.png │ ├── Images.xaml │ └── Styles.xaml ├── App.xaml.cs ├── MainWindow.xaml.cs ├── AssemblyInfo.cs ├── App.xaml ├── WpfKb.TestClient.csproj └── MainWindow.xaml ├── README.md ├── WpfKb ├── LogicalKeys │ ├── EmptyKey.cs │ ├── ILogicalKey.cs │ ├── CaseSensitiveKey.cs │ ├── ShiftSensitiveKey.cs │ ├── NumLockSensitiveKey.cs │ ├── ModifierKeyBase.cs │ ├── TogglingModifierKey.cs │ ├── StringKey.cs │ ├── VirtualKey.cs │ ├── InstantaneousModifierKey.cs │ ├── MultiCharacterKey.cs │ ├── ChordKey.cs │ └── LogicalKeyBase.cs ├── Input │ ├── Native │ │ ├── HARDWAREINPUT.cs │ │ ├── MOUSEKEYBDHARDWAREINPUT.cs │ │ ├── INPUT.cs │ │ ├── KEYBDINPUT.cs │ │ └── MOUSEINPUT.cs │ ├── VirtualKeyCode.cs │ └── InputSimulator.cs ├── Controls │ ├── OnScreenKeyEventArgs.cs │ ├── OnScreenKeypad.cs │ ├── FloatingTouchScreenKeyboard.xaml │ ├── OnScreenKeyboardSection.cs │ ├── UniformOnScreenKeyboard.cs │ ├── FloatingTouchScreenKeyboard.xaml.cs │ ├── OnScreenKeyboard.cs │ ├── OnScreenKey.cs │ └── OnScreenWebKeyboard.cs ├── WpfKb.csproj ├── Styles │ └── OnScreenKeyoard.xaml └── Behaviors │ └── AutoHideBehavior.cs ├── LICENSE ├── WpfKb.sln └── .gitignore /WpfKb.TestClient/Resources/AbstractBlue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imasm/wpfkb/HEAD/WpfKb.TestClient/Resources/AbstractBlue.png -------------------------------------------------------------------------------- /WpfKb.TestClient/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | namespace WpfKb.TestClient 4 | { 5 | public partial class App : Application 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WPF Touch Screen Keyboard 2 | 3 | Fork of https://wpfkb.codeplex.com 4 | 5 | ## Project Description 6 | WPF Touch Screen Keyboard is a re-usable control and toolkit for anyone developing a touch screen application in WPF. 7 | 8 | 9 | -------------------------------------------------------------------------------- /WpfKb.TestClient/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | namespace WpfKb.TestClient 4 | { 5 | public partial class MainWindow : Window 6 | { 7 | public MainWindow() 8 | { 9 | InitializeComponent(); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /WpfKb/LogicalKeys/EmptyKey.cs: -------------------------------------------------------------------------------- 1 | namespace WpfKb.LogicalKeys 2 | { 3 | public class EmptyKey : LogicalKeyBase 4 | { 5 | 6 | public EmptyKey(string displayName) 7 | { 8 | DisplayName = displayName; 9 | } 10 | 11 | public EmptyKey() 12 | { 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /WpfKb/LogicalKeys/ILogicalKey.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace WpfKb.LogicalKeys 4 | { 5 | public interface ILogicalKey : INotifyPropertyChanged 6 | { 7 | string DisplayName { get; } 8 | void Press(); 9 | event LogicalKeyPressedEventHandler LogicalKeyPressed; 10 | } 11 | } -------------------------------------------------------------------------------- /WpfKb.TestClient/Resources/Images.xaml: -------------------------------------------------------------------------------- 1 | 3 | pack://application:,,,/WpfKb.TestClient;component/Resources/AbstractBlue.png 4 | -------------------------------------------------------------------------------- /WpfKb/Input/Native/HARDWAREINPUT.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | // ReSharper disable InconsistentNaming 3 | 4 | namespace WpfKb.Input.Native 5 | { 6 | [StructLayout(LayoutKind.Sequential)] 7 | internal struct HARDWAREINPUT 8 | { 9 | internal int Msg; 10 | internal short ParamL; 11 | internal short ParamH; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /WpfKb/LogicalKeys/CaseSensitiveKey.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using WpfKb.Input; 3 | 4 | namespace WpfKb.LogicalKeys 5 | { 6 | public class CaseSensitiveKey : MultiCharacterKey 7 | { 8 | public CaseSensitiveKey(VirtualKeyCode keyCode, IList keyDisplays) 9 | : base(keyCode, keyDisplays) 10 | { 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /WpfKb/LogicalKeys/ShiftSensitiveKey.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using WpfKb.Input; 3 | 4 | namespace WpfKb.LogicalKeys 5 | { 6 | public class ShiftSensitiveKey : MultiCharacterKey 7 | { 8 | public ShiftSensitiveKey(VirtualKeyCode keyCode, IList keyDisplays) 9 | : base(keyCode, keyDisplays) 10 | { 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /WpfKb/LogicalKeys/NumLockSensitiveKey.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using WpfKb.Input; 3 | 4 | namespace WpfKb.LogicalKeys 5 | { 6 | public class NumLockSensitiveKey : MultiCharacterKey 7 | { 8 | public NumLockSensitiveKey(VirtualKeyCode keyCode, IList keyDisplays) 9 | : base(keyCode, keyDisplays) 10 | { 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /WpfKb/Input/Native/MOUSEKEYBDHARDWAREINPUT.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | // ReSharper disable InconsistentNaming 4 | namespace WpfKb.Input.Native 5 | { 6 | [StructLayout(LayoutKind.Explicit)] 7 | internal struct MOUSEKEYBDHARDWAREINPUT 8 | { 9 | [FieldOffset(0)] 10 | internal MOUSEINPUT Mouse; 11 | 12 | [FieldOffset(0)] 13 | internal KEYBDINPUT Keyboard; 14 | 15 | [FieldOffset(0)] 16 | internal HARDWAREINPUT Hardware; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /WpfKb/Input/Native/INPUT.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | // ReSharper disable InconsistentNaming 3 | 4 | namespace WpfKb.Input.Native 5 | { 6 | [StructLayout(LayoutKind.Sequential)] 7 | internal struct INPUT 8 | { 9 | internal InputType Type; 10 | internal MOUSEKEYBDHARDWAREINPUT Data; 11 | internal static int Size => Marshal.SizeOf(typeof(INPUT)); 12 | } 13 | 14 | internal enum InputType : uint 15 | { 16 | MOUSE = 0, 17 | KEYBOARD = 1, 18 | HARDWARE = 2 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /WpfKb/Controls/OnScreenKeyEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | namespace WpfKb.Controls 4 | { 5 | public class OnScreenKeyEventArgs : RoutedEventArgs 6 | { 7 | public OnScreenKey OnScreenKey { get; private set; } 8 | 9 | public OnScreenKeyEventArgs(RoutedEvent routedEvent, OnScreenKey onScreenKey) 10 | : base(routedEvent) 11 | { 12 | OnScreenKey = onScreenKey; 13 | } 14 | } 15 | 16 | public delegate void OnScreenKeyEventHandler(DependencyObject sender, OnScreenKeyEventArgs e); 17 | } -------------------------------------------------------------------------------- /WpfKb.TestClient/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | [assembly: ThemeInfo( 4 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 5 | //(used if a resource is not found in the page, 6 | // or application resource dictionaries) 7 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 8 | //(used if a resource is not found in the page, 9 | // app, or any theme specific resource dictionaries) 10 | )] 11 | -------------------------------------------------------------------------------- /WpfKb/Input/Native/KEYBDINPUT.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | // ReSharper disable InconsistentNaming 5 | namespace WpfKb.Input.Native 6 | { 7 | 8 | [StructLayout(LayoutKind.Sequential)] 9 | internal struct KEYBDINPUT 10 | { 11 | internal VirtualKeyCode Vk; // ushort 12 | internal ushort Scan; //ScanCodeShort 13 | internal KEYEVENTF Flags; 14 | internal uint Time; 15 | internal UIntPtr ExtraInfo; 16 | } 17 | 18 | [Flags] 19 | internal enum KEYEVENTF : uint 20 | { 21 | EXTENDEDKEY = 0x0001, 22 | KEYUP = 0x0002, 23 | SCANCODE = 0x0008, 24 | UNICODE = 0x0004 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /WpfKb/LogicalKeys/ModifierKeyBase.cs: -------------------------------------------------------------------------------- 1 | using WpfKb.Input; 2 | 3 | namespace WpfKb.LogicalKeys 4 | { 5 | public abstract class ModifierKeyBase : VirtualKey 6 | { 7 | private bool _isInEffect; 8 | 9 | public bool IsInEffect 10 | { 11 | get { return _isInEffect; } 12 | set 13 | { 14 | if (value != _isInEffect) 15 | { 16 | _isInEffect = value; 17 | OnPropertyChanged("IsInEffect"); 18 | } 19 | } 20 | } 21 | 22 | protected ModifierKeyBase(VirtualKeyCode keyCode) : 23 | base(keyCode) 24 | { 25 | } 26 | 27 | public abstract void SynchroniseKeyState(); 28 | } 29 | } -------------------------------------------------------------------------------- /WpfKb.TestClient/App.xaml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /WpfKb/Input/Native/MOUSEINPUT.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | // ReSharper disable InconsistentNaming 4 | 5 | namespace WpfKb.Input.Native 6 | { 7 | [StructLayout(LayoutKind.Sequential)] 8 | internal struct MOUSEINPUT 9 | { 10 | internal int X; 11 | internal int Y; 12 | internal int MouseData; 13 | internal MOUSEEVENTF Flags; 14 | internal uint Time; 15 | internal UIntPtr ExtraInfo; 16 | } 17 | 18 | [Flags] 19 | internal enum MOUSEEVENTF : uint 20 | { 21 | ABSOLUTE = 0x8000, 22 | HWHEEL = 0x01000, 23 | MOVE = 0x0001, 24 | MOVE_NOCOALESCE = 0x2000, 25 | LEFTDOWN = 0x0002, 26 | LEFTUP = 0x0004, 27 | RIGHTDOWN = 0x0008, 28 | RIGHTUP = 0x0010, 29 | MIDDLEDOWN = 0x0020, 30 | MIDDLEUP = 0x0040, 31 | VIRTUALDESK = 0x4000, 32 | WHEEL = 0x0800, 33 | XDOWN = 0x0080, 34 | XUP = 0x0100 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /WpfKb/LogicalKeys/TogglingModifierKey.cs: -------------------------------------------------------------------------------- 1 | using WpfKb.Input; 2 | 3 | namespace WpfKb.LogicalKeys 4 | { 5 | public class TogglingModifierKey : ModifierKeyBase 6 | { 7 | public TogglingModifierKey(string displayName, VirtualKeyCode keyCode) : 8 | base(keyCode) 9 | { 10 | DisplayName = displayName; 11 | } 12 | 13 | public override void Press() 14 | { 15 | // This is a bit tricky because we can only get the state of a toggling key after the input has been 16 | // read off the MessagePump. Ergo if we make that assumption that in the time it takes to run this method 17 | // we will be toggling the state of the key, set IsInEffect to the new state and then press the key. 18 | IsInEffect = !InputSimulator.IsTogglingKeyInEffect(KeyCode); 19 | base.Press(); 20 | } 21 | 22 | public override void SynchroniseKeyState() 23 | { 24 | IsInEffect = InputSimulator.IsTogglingKeyInEffect(KeyCode); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /WpfKb/LogicalKeys/StringKey.cs: -------------------------------------------------------------------------------- 1 | 2 | using WpfKb.Input; 3 | 4 | namespace WpfKb.LogicalKeys 5 | { 6 | public class StringKey : LogicalKeyBase 7 | { 8 | private string _stringToSimulate; 9 | 10 | public virtual string StringToSimulate 11 | { 12 | get { return _stringToSimulate; } 13 | set 14 | { 15 | if (value != _stringToSimulate) 16 | { 17 | _stringToSimulate = value; 18 | OnPropertyChanged("StringToSimulate"); 19 | } 20 | } 21 | } 22 | 23 | public StringKey(string displayName, string stringToSimulate) 24 | { 25 | DisplayName = displayName; 26 | _stringToSimulate = stringToSimulate; 27 | } 28 | 29 | public StringKey() 30 | { 31 | } 32 | 33 | public override void Press() 34 | { 35 | InputSimulator.SimulateTextEntry(_stringToSimulate); 36 | base.Press(); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /WpfKb/LogicalKeys/VirtualKey.cs: -------------------------------------------------------------------------------- 1 | using WpfKb.Input; 2 | 3 | namespace WpfKb.LogicalKeys 4 | { 5 | public class VirtualKey : LogicalKeyBase 6 | { 7 | private VirtualKeyCode _keyCode; 8 | 9 | public virtual VirtualKeyCode KeyCode 10 | { 11 | get { return _keyCode; } 12 | set 13 | { 14 | if (value != _keyCode) 15 | { 16 | _keyCode = value; 17 | OnPropertyChanged("KeyCode"); 18 | } 19 | } 20 | } 21 | 22 | public VirtualKey(VirtualKeyCode keyCode, string displayName) 23 | { 24 | DisplayName = displayName; 25 | KeyCode = keyCode; 26 | } 27 | 28 | public VirtualKey(VirtualKeyCode keyCode) 29 | { 30 | KeyCode = keyCode; 31 | } 32 | 33 | public VirtualKey() 34 | { 35 | } 36 | 37 | public override void Press() 38 | { 39 | InputSimulator.SimulateKeyPress(_keyCode); 40 | base.Press(); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Ivan Masmitjà 4 | Copyright (c) 2009 - 2010 michaelnoonan 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /WpfKb/LogicalKeys/InstantaneousModifierKey.cs: -------------------------------------------------------------------------------- 1 | using WpfKb.Input; 2 | 3 | namespace WpfKb.LogicalKeys 4 | { 5 | public class InstantaneousModifierKey : ModifierKeyBase 6 | { 7 | public InstantaneousModifierKey(string displayName, VirtualKeyCode keyCode) : 8 | base(keyCode) 9 | { 10 | DisplayName = displayName; 11 | } 12 | 13 | public override void Press() 14 | { 15 | if (IsInEffect) InputSimulator.SimulateKeyUp(KeyCode); 16 | else InputSimulator.SimulateKeyDown(KeyCode); 17 | 18 | // We need to use IsKeyDownAsync here so we will know exactly what state the key will be in 19 | // once the active windows read the input from the MessagePump. IsKeyDown will only report 20 | // the correct value after the input has been read from the MessagePump and will not be correct 21 | // by the time we set IsInEffect. 22 | IsInEffect = InputSimulator.IsKeyDownAsync(KeyCode); 23 | OnKeyPressed(); 24 | } 25 | 26 | public override void SynchroniseKeyState() 27 | { 28 | IsInEffect = InputSimulator.IsKeyDownAsync(KeyCode); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /WpfKb/WpfKb.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0-windows 5 | true 6 | michaelnoonan, imasmitja 7 | WpfKb 8 | 2.0.0.0 9 | $(AssemblyVersion) 10 | $(AssemblyVersion) 11 | An on-screen or touch screen keyboard control and toolkit for WPF. 12 | https://github.com/imasm/wpfkb 13 | Copyright © 2017 Ivan Masmitjà; Copyright © 2009 - 2010 michaelnoonan 14 | IMasm.$(AssemblyName) 15 | README.md 16 | https://github.com/imasm/wpfkb 17 | WPF;keyboard 18 | 19 | 20 | 21 | 22 | True 23 | \ 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /WpfKb/LogicalKeys/MultiCharacterKey.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using WpfKb.Input; 4 | 5 | namespace WpfKb.LogicalKeys 6 | { 7 | public class MultiCharacterKey : VirtualKey 8 | { 9 | private int _selectedIndex; 10 | public IList KeyDisplays { get; protected set; } 11 | public string SelectedKeyDisplay { get; protected set; } 12 | 13 | public int SelectedIndex 14 | { 15 | get { return _selectedIndex; } 16 | set 17 | { 18 | if (value != _selectedIndex) 19 | { 20 | _selectedIndex = value; 21 | SelectedKeyDisplay = KeyDisplays[value]; 22 | DisplayName = SelectedKeyDisplay; 23 | OnPropertyChanged("SelectedIndex"); 24 | OnPropertyChanged("SelectedKeyDisplay"); 25 | } 26 | } 27 | } 28 | 29 | public MultiCharacterKey(VirtualKeyCode keyCode, IList keyDisplays) : 30 | base(keyCode) 31 | { 32 | if (keyDisplays == null) throw new ArgumentNullException("keyDisplays"); 33 | if (keyDisplays.Count <= 0) 34 | throw new ArgumentException("Please provide a list of one or more keyDisplays", "keyDisplays"); 35 | KeyDisplays = keyDisplays; 36 | DisplayName = keyDisplays[0]; 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /WpfKb/LogicalKeys/ChordKey.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using WpfKb.Input; 3 | 4 | namespace WpfKb.LogicalKeys 5 | { 6 | public class ChordKey : LogicalKeyBase 7 | { 8 | public IList ModifierKeys { get; private set; } 9 | public IList Keys { get; private set; } 10 | 11 | public ChordKey(string displayName, VirtualKeyCode modifierKey, VirtualKeyCode key) 12 | : this(displayName, new List { modifierKey }, new List { key }) 13 | { 14 | } 15 | 16 | public ChordKey(string displayName, IList modifierKeys, VirtualKeyCode key) 17 | : this(displayName, modifierKeys, new List { key }) 18 | { 19 | } 20 | 21 | public ChordKey(string displayName, VirtualKeyCode modifierKey, IList keys) 22 | : this(displayName, new List { modifierKey }, keys) 23 | { 24 | } 25 | 26 | public ChordKey(string displayName, IList modifierKeys, IList keys) 27 | { 28 | DisplayName = displayName; 29 | ModifierKeys = modifierKeys; 30 | Keys = keys; 31 | } 32 | 33 | public override void Press() 34 | { 35 | InputSimulator.SimulateModifiedKeyStroke(ModifierKeys, Keys); 36 | base.Press(); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /WpfKb.TestClient/WpfKb.TestClient.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | WinExe 5 | net6.0-windows 6 | true 7 | WpfKb.TestClient 8 | 2.0.0.0 9 | $(AssemblyVersion) 10 | $(AssemblyVersion) 11 | WpfKb demo. 12 | https://github.com/imasm/wpfkb 13 | Copyright © 2017 Ivan Masmitjà; Copyright © 2009 - 2010 michaelnoonan 14 | IMasm.$(AssemblyName) 15 | https://github.com/imasm/wpfkb 16 | WPF;keyboard;demo 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | Never 30 | 31 | 32 | 33 | 34 | 35 | XamlIntelliSenseFileGenerator 36 | Never 37 | 38 | 39 | XamlIntelliSenseFileGenerator 40 | Never 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /WpfKb.TestClient/MainWindow.xaml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 22 | 25 | 26 | 27 | 28 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /WpfKb.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.3.32929.385 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WpfKb", "WpfKb\WpfKb.csproj", "{1CFEE3EC-5983-4D6F-8549-D529312BDC0B}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WpfKb.TestClient", "WpfKb.TestClient\WpfKb.TestClient.csproj", "{3E6262D8-676E-41D0-A919-80087E4C230E}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {3E6262D8-676E-41D0-A919-80087E4C230E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {3E6262D8-676E-41D0-A919-80087E4C230E}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {3E6262D8-676E-41D0-A919-80087E4C230E}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {3E6262D8-676E-41D0-A919-80087E4C230E}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {1CFEE3EC-5983-4D6F-8549-D529312BDC0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {1CFEE3EC-5983-4D6F-8549-D529312BDC0B}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {1CFEE3EC-5983-4D6F-8549-D529312BDC0B}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {1CFEE3EC-5983-4D6F-8549-D529312BDC0B}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {97C7737C-9245-47BF-8C1C-70197F61736F} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /WpfKb/LogicalKeys/LogicalKeyBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | 4 | namespace WpfKb.LogicalKeys 5 | { 6 | public class LogicalKeyEventArgs : EventArgs 7 | { 8 | public ILogicalKey Key { get; private set; } 9 | 10 | public LogicalKeyEventArgs(ILogicalKey key) 11 | { 12 | Key = key; 13 | } 14 | } 15 | 16 | public delegate void LogicalKeyPressedEventHandler(object sender, LogicalKeyEventArgs e); 17 | 18 | public abstract class LogicalKeyBase : ILogicalKey 19 | { 20 | public event LogicalKeyPressedEventHandler LogicalKeyPressed; 21 | public event PropertyChangedEventHandler PropertyChanged; 22 | 23 | private string _displayName; 24 | 25 | public virtual string DisplayName 26 | { 27 | get { return _displayName; } 28 | set 29 | { 30 | if (value != _displayName) 31 | { 32 | _displayName = value; 33 | OnPropertyChanged("DisplayName"); 34 | } 35 | } 36 | } 37 | 38 | public virtual void Press() 39 | { 40 | OnKeyPressed(); 41 | } 42 | 43 | protected void OnKeyPressed() 44 | { 45 | if (LogicalKeyPressed != null) LogicalKeyPressed(this, new LogicalKeyEventArgs(this)); 46 | } 47 | 48 | protected virtual void OnPropertyChanged(string propertyName) 49 | { 50 | var handler = PropertyChanged; 51 | if (handler != null) 52 | { 53 | var args = new PropertyChangedEventArgs(propertyName); 54 | handler(this, args); 55 | } 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /WpfKb/Controls/OnScreenKeypad.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | using WpfKb.Input; 3 | using WpfKb.LogicalKeys; 4 | 5 | 6 | namespace WpfKb.Controls 7 | { 8 | public class OnScreenKeypad : UniformOnScreenKeyboard 9 | { 10 | public OnScreenKeypad() 11 | { 12 | Keys = new ObservableCollection 13 | { 14 | new OnScreenKey { GridRow = 0, GridColumn = 0, Key = new VirtualKey(VirtualKeyCode.VK_7, "7") }, 15 | new OnScreenKey { GridRow = 0, GridColumn = 1, Key = new VirtualKey(VirtualKeyCode.VK_8, "8") }, 16 | new OnScreenKey { GridRow = 0, GridColumn = 2, Key = new VirtualKey(VirtualKeyCode.VK_9, "9") }, 17 | new OnScreenKey { GridRow = 1, GridColumn = 0, Key = new VirtualKey(VirtualKeyCode.VK_4, "4") }, 18 | new OnScreenKey { GridRow = 1, GridColumn = 1, Key = new VirtualKey(VirtualKeyCode.VK_5, "5") }, 19 | new OnScreenKey { GridRow = 1, GridColumn = 2, Key = new VirtualKey(VirtualKeyCode.VK_6, "6") }, 20 | new OnScreenKey { GridRow = 2, GridColumn = 0, Key = new VirtualKey(VirtualKeyCode.VK_1, "1") }, 21 | new OnScreenKey { GridRow = 2, GridColumn = 1, Key = new VirtualKey(VirtualKeyCode.VK_2, "2") }, 22 | new OnScreenKey { GridRow = 2, GridColumn = 2, Key = new VirtualKey(VirtualKeyCode.VK_3, "3") }, 23 | new OnScreenKey { GridRow = 3, GridColumn = 0, Key = new VirtualKey(VirtualKeyCode.DELETE, "Clear") }, 24 | new OnScreenKey { GridRow = 3, GridColumn = 1, Key = new VirtualKey(VirtualKeyCode.VK_0, "0") }, 25 | new OnScreenKey { GridRow = 3, GridColumn = 2, Key = new VirtualKey(VirtualKeyCode.BACK, "Del") }, 26 | }; 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build Folders (you can keep bin if you'd like, to store dlls and pdbs) 2 | [Bb]in/ 3 | [Oo]bj/ 4 | 5 | # mstest test results 6 | TestResults 7 | 8 | ## Ignore Visual Studio temporary files, build results, and 9 | ## files generated by popular Visual Studio add-ons. 10 | 11 | # User-specific files 12 | *.suo 13 | *.user 14 | *.sln.docstates 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Rr]elease/ 19 | x64/ 20 | *_i.c 21 | *_p.c 22 | *.ilk 23 | *.meta 24 | *.obj 25 | *.pch 26 | *.pdb 27 | *.pgc 28 | *.pgd 29 | *.rsp 30 | *.sbr 31 | *.tlb 32 | *.tli 33 | *.tlh 34 | *.tmp 35 | *.log 36 | *.bak 37 | *.vspscc 38 | *.vssscc 39 | .builds 40 | 41 | # Visual C++ cache files 42 | ipch/ 43 | *.aps 44 | *.ncb 45 | *.opensdf 46 | *.sdf 47 | 48 | # Visual Studio profiler 49 | *.psess 50 | *.vsp 51 | *.vspx 52 | 53 | # Guidance Automation Toolkit 54 | *.gpState 55 | 56 | # ReSharper is a .NET coding add-in 57 | _ReSharper* 58 | 59 | # NCrunch 60 | *.ncrunch* 61 | .*crunch*.local.xml 62 | 63 | # Installshield output folder 64 | [Ee]xpress 65 | 66 | # DocProject is a documentation generator add-in 67 | DocProject/buildhelp/ 68 | DocProject/Help/*.HxT 69 | DocProject/Help/*.HxC 70 | DocProject/Help/*.hhc 71 | DocProject/Help/*.hhk 72 | DocProject/Help/*.hhp 73 | DocProject/Help/Html2 74 | DocProject/Help/html 75 | 76 | # Click-Once directory 77 | publish 78 | 79 | # Publish Web Output 80 | *.Publish.xml 81 | 82 | # NuGet Packages Directory 83 | packages 84 | 85 | # Windows Azure Build Output 86 | csx 87 | *.build.csdef 88 | 89 | # Windows Store app package directory 90 | AppPackages/ 91 | 92 | # Others 93 | [Bb]in 94 | [Oo]bj 95 | sql 96 | TestResults 97 | [Tt]est[Rr]esult* 98 | *.Cache 99 | ClientBin 100 | [Ss]tyle[Cc]op.* 101 | ~$* 102 | *.dbmdl 103 | Generated_Code #added for RIA/Silverlight projects 104 | 105 | # Backup & report files from converting an old project file to a newer 106 | # Visual Studio version. Backup files are not needed, because we have git ;-) 107 | _UpgradeReport_Files/ 108 | Backup*/ 109 | Visual Studio*/ 110 | UpgradeLog*.XML 111 | tools/ 112 | output/* 113 | /.vs 114 | -------------------------------------------------------------------------------- /WpfKb/Input/VirtualKeyCode.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable InconsistentNaming 2 | namespace WpfKb.Input 3 | { 4 | public enum VirtualKeyCode : ushort 5 | { 6 | LBUTTON = 1, 7 | RBUTTON, 8 | CANCEL, 9 | MBUTTON, 10 | XBUTTON1, 11 | XBUTTON2, 12 | BACK = 8, 13 | TAB, 14 | CLEAR = 12, 15 | RETURN, 16 | SHIFT = 16, 17 | CONTROL, 18 | MENU, 19 | PAUSE, 20 | CAPITAL, 21 | KANA, 22 | HANGEUL = 21, 23 | HANGUL = 21, 24 | JUNJA = 23, 25 | FINAL, 26 | HANJA, 27 | KANJI = 25, 28 | ESCAPE = 27, 29 | CONVERT, 30 | NONCONVERT, 31 | ACCEPT, 32 | MODECHANGE, 33 | SPACE, 34 | PRIOR, 35 | NEXT, 36 | END, 37 | HOME, 38 | LEFT, 39 | UP, 40 | RIGHT, 41 | DOWN, 42 | SELECT, 43 | PRINT, 44 | EXECUTE, 45 | SNAPSHOT, 46 | INSERT, 47 | DELETE, 48 | HELP, 49 | VK_0, 50 | VK_1, 51 | VK_2, 52 | VK_3, 53 | VK_4, 54 | VK_5, 55 | VK_6, 56 | VK_7, 57 | VK_8, 58 | VK_9, 59 | VK_A = 65, 60 | VK_B, 61 | VK_C, 62 | VK_D, 63 | VK_E, 64 | VK_F, 65 | VK_G, 66 | VK_H, 67 | VK_I, 68 | VK_J, 69 | VK_K, 70 | VK_L, 71 | VK_M, 72 | VK_N, 73 | VK_O, 74 | VK_P, 75 | VK_Q, 76 | VK_R, 77 | VK_S, 78 | VK_T, 79 | VK_U, 80 | VK_V, 81 | VK_W, 82 | VK_X, 83 | VK_Y, 84 | VK_Z, 85 | LWIN, 86 | RWIN, 87 | APPS, 88 | SLEEP = 95, 89 | NUMPAD0, 90 | NUMPAD1, 91 | NUMPAD2, 92 | NUMPAD3, 93 | NUMPAD4, 94 | NUMPAD5, 95 | NUMPAD6, 96 | NUMPAD7, 97 | NUMPAD8, 98 | NUMPAD9, 99 | MULTIPLY, 100 | ADD, 101 | SEPARATOR, 102 | SUBTRACT, 103 | DECIMAL, 104 | DIVIDE, 105 | F1, 106 | F2, 107 | F3, 108 | F4, 109 | F5, 110 | F6, 111 | F7, 112 | F8, 113 | F9, 114 | F10, 115 | F11, 116 | F12, 117 | F13, 118 | F14, 119 | F15, 120 | F16, 121 | F17, 122 | F18, 123 | F19, 124 | F20, 125 | F21, 126 | F22, 127 | F23, 128 | F24, 129 | NUMLOCK = 144, 130 | SCROLL, 131 | LSHIFT = 160, 132 | RSHIFT, 133 | LCONTROL, 134 | RCONTROL, 135 | LMENU, 136 | RMENU, 137 | BROWSER_BACK, 138 | BROWSER_FORWARD, 139 | BROWSER_REFRESH, 140 | BROWSER_STOP, 141 | BROWSER_SEARCH, 142 | BROWSER_FAVORITES, 143 | BROWSER_HOME, 144 | VOLUME_MUTE, 145 | VOLUME_DOWN, 146 | VOLUME_UP, 147 | MEDIA_NEXT_TRACK, 148 | MEDIA_PREV_TRACK, 149 | MEDIA_STOP, 150 | MEDIA_PLAY_PAUSE, 151 | LAUNCH_MAIL, 152 | LAUNCH_MEDIA_SELECT, 153 | LAUNCH_APP1, 154 | LAUNCH_APP2, 155 | OEM_1 = 186, 156 | OEM_PLUS, 157 | OEM_COMMA, 158 | OEM_MINUS, 159 | OEM_PERIOD, 160 | OEM_2, 161 | OEM_3, 162 | OEM_4 = 219, 163 | OEM_5, 164 | OEM_6, 165 | OEM_7, 166 | OEM_8, 167 | OEM_102 = 226, 168 | PROCESSKEY = 229, 169 | PACKET = 231, 170 | ATTN = 246, 171 | CRSEL, 172 | EXSEL, 173 | EREOF, 174 | PLAY, 175 | ZOOM, 176 | NONAME, 177 | PA1, 178 | OEM_CLEAR 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /WpfKb/Controls/FloatingTouchScreenKeyboard.xaml: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 42 | 43 | 45 | 46 | 48 | 49 | 50 | 51 | 52 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /WpfKb/Styles/OnScreenKeyoard.xaml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 83 | 84 | -------------------------------------------------------------------------------- /WpfKb/Controls/OnScreenKeyboardSection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Windows; 5 | using System.Windows.Controls; 6 | using System.Collections.ObjectModel; 7 | 8 | namespace WpfKb.Controls 9 | { 10 | public class OnScreenKeyboardSection : Grid 11 | { 12 | private ObservableCollection _keys; 13 | private List _buttonRows; 14 | 15 | public ObservableCollection Keys 16 | { 17 | get { return _keys; } 18 | set 19 | { 20 | if (value == _keys) return; 21 | 22 | Reset(); 23 | _keys = value; 24 | LayoutKeys(); 25 | } 26 | } 27 | 28 | public OnScreenKeyboardSection() 29 | { 30 | Margin = new Thickness(5); 31 | _buttonRows = new List(); 32 | _keys = new ObservableCollection(); 33 | _keys.CollectionChanged += Keys_CollectionChanged; 34 | } 35 | 36 | private void Keys_CollectionChanged(object sender, 37 | System.Collections.Specialized.NotifyCollectionChangedEventArgs e) 38 | { 39 | switch (e.Action) 40 | { 41 | case System.Collections.Specialized.NotifyCollectionChangedAction.Add: 42 | case System.Collections.Specialized.NotifyCollectionChangedAction.Remove: 43 | LayoutKeys(); 44 | break; 45 | case System.Collections.Specialized.NotifyCollectionChangedAction.Replace: 46 | throw new NotSupportedException("You cannot currently replace keys."); 47 | case System.Collections.Specialized.NotifyCollectionChangedAction.Reset: 48 | Reset(); 49 | break; 50 | default: 51 | break; 52 | } 53 | } 54 | 55 | private void LayoutKeys() 56 | { 57 | if (_keys == null || _keys.Count == 0) return; 58 | 59 | ResizeGrid(_keys); 60 | for (var buttonRowIndex = 0; buttonRowIndex < _buttonRows.Count; buttonRowIndex++) 61 | { 62 | var grid = _buttonRows[buttonRowIndex]; 63 | grid.Children.Clear(); 64 | foreach (var key in _keys.Where(x => x.GridRow == buttonRowIndex)) 65 | { 66 | grid.Children.Add(key); 67 | } 68 | } 69 | } 70 | 71 | private void ResizeGrid(IEnumerable keys) 72 | { 73 | if (keys == null) throw new ArgumentNullException("keys"); 74 | 75 | // Make sure there's enough rows 76 | var rowCount = keys.Max(x => x.GridRow) + 1; 77 | for (var extraRowIndex = RowDefinitions.Count; extraRowIndex < rowCount; extraRowIndex++) 78 | { 79 | // Button Row 80 | RowDefinitions.Add(new RowDefinition()); 81 | 82 | // Add a grid for the buttons 83 | var g = new Grid(); 84 | _buttonRows.Add(g); 85 | Children.Add(g); 86 | g.SetValue(RowProperty, extraRowIndex); 87 | } 88 | 89 | // Make sure each row has enough columns 90 | for (var buttonRowIndex = 0; buttonRowIndex < rowCount; buttonRowIndex++) 91 | { 92 | var grid = _buttonRows[buttonRowIndex]; 93 | var colCount = keys.Where(x => x.GridRow == buttonRowIndex).Max(x => x.GridColumn) + 1; 94 | for (var colsToAdd = colCount - grid.ColumnDefinitions.Count; colsToAdd > 0; colsToAdd--) 95 | { 96 | // Add the extra Column 97 | grid.ColumnDefinitions.Add(new ColumnDefinition()); 98 | } 99 | for (var colsToRemove = grid.ColumnDefinitions.Count - colCount; colsToRemove > 0; colsToRemove--) 100 | { 101 | // Remove the extra Column 102 | grid.ColumnDefinitions.RemoveAt(0); 103 | } 104 | 105 | // Set the width of each column according to the key's GridWidth definition 106 | keys.Where(x => x.GridRow == buttonRowIndex && x.GridWidth.Value != 1).ToList() 107 | .ForEach(x => grid.ColumnDefinitions[x.GridColumn].Width = x.GridWidth); 108 | 109 | } 110 | } 111 | 112 | private void Reset() 113 | { 114 | _keys = null; 115 | Children.Clear(); 116 | RowDefinitions.Clear(); 117 | ColumnDefinitions.Clear(); 118 | } 119 | } 120 | } -------------------------------------------------------------------------------- /WpfKb.TestClient/Resources/Styles.xaml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 17 | 18 | 92 | 93 | -------------------------------------------------------------------------------- /WpfKb/Controls/UniformOnScreenKeyboard.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Collections.ObjectModel; 3 | using System.Collections.Specialized; 4 | using System.Windows.Controls; 5 | using System.Windows; 6 | 7 | namespace WpfKb.Controls 8 | { 9 | public class UniformOnScreenKeyboard : Grid 10 | { 11 | public static readonly DependencyProperty AreAnimationsEnabledProperty = DependencyProperty.Register("AreAnimationsEnabled", typeof(bool), typeof(UniformOnScreenKeyboard), new UIPropertyMetadata(true, OnAreAnimationsEnabledPropertyChanged)); 12 | 13 | private ObservableCollection _keys; 14 | 15 | public bool AreAnimationsEnabled 16 | { 17 | get { return (bool)GetValue(AreAnimationsEnabledProperty); } 18 | set { SetValue(AreAnimationsEnabledProperty, value); } 19 | } 20 | 21 | public ObservableCollection Keys 22 | { 23 | get { return _keys; } 24 | set 25 | { 26 | if (value == _keys) return; 27 | 28 | Reset(); 29 | _keys = value; 30 | if (_keys != null) 31 | foreach (var key in _keys) 32 | { 33 | Children.Add(key); 34 | } 35 | ResizeGrid(); 36 | } 37 | } 38 | 39 | public UniformOnScreenKeyboard() 40 | { 41 | _keys = new ObservableCollection(); 42 | _keys.CollectionChanged += Keys_CollectionChanged; 43 | } 44 | 45 | private static void OnAreAnimationsEnabledPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 46 | { 47 | var keyboard = (UniformOnScreenKeyboard)d; 48 | keyboard.Keys.ToList().ForEach(x => x.AreAnimationsEnabled = (bool)e.NewValue); 49 | } 50 | 51 | private void Keys_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) 52 | { 53 | switch (e.Action) 54 | { 55 | case NotifyCollectionChangedAction.Add: 56 | // Add the keys to the Grid's Children collection and resize the grid 57 | foreach (var key in e.NewItems.OfType()) 58 | { 59 | key.AreAnimationsEnabled = AreAnimationsEnabled; 60 | Children.Add(key); 61 | } 62 | ResizeGrid(); 63 | break; 64 | case NotifyCollectionChangedAction.Remove: 65 | // Remove the keys from the Grid's Children collection and resize the grid 66 | foreach (var key in e.OldItems.OfType()) 67 | { 68 | Children.Remove(key); 69 | } 70 | ResizeGrid(); 71 | break; 72 | case NotifyCollectionChangedAction.Replace: 73 | // Throw everything away and start again 74 | Children.Clear(); 75 | foreach (var key in _keys) 76 | { 77 | Children.Add(key); 78 | } 79 | ResizeGrid(); 80 | break; 81 | case NotifyCollectionChangedAction.Reset: 82 | Reset(); 83 | break; 84 | default: 85 | break; 86 | } 87 | } 88 | 89 | 90 | private void ResizeGrid() 91 | { 92 | if (_keys == null) 93 | { 94 | Reset(); 95 | return; 96 | } 97 | 98 | // Make sure there's the right number of rows 99 | var rowCount = _keys.Max(x => x.GridRow) + 1; 100 | for (var rowsToAdd = rowCount - RowDefinitions.Count; rowsToAdd > 0; rowsToAdd--) 101 | { 102 | // Add the extra Row 103 | RowDefinitions.Add(new RowDefinition()); 104 | } 105 | for (var rowsToRemove = RowDefinitions.Count - rowCount; rowsToRemove > 0; rowsToRemove--) 106 | { 107 | // Remove the extra Row 108 | RowDefinitions.RemoveAt(0); 109 | } 110 | 111 | // Make sure there's the right number of cols 112 | var colCount = _keys.Max(x => x.GridColumn) + 1; 113 | for (var colsToAdd = colCount - ColumnDefinitions.Count; colsToAdd > 0; colsToAdd--) 114 | { 115 | // Add the extra Column 116 | ColumnDefinitions.Add(new ColumnDefinition()); 117 | } 118 | for (var colsToRemove = ColumnDefinitions.Count - colCount; colsToRemove > 0; colsToRemove--) 119 | { 120 | // Remove the extra Column 121 | ColumnDefinitions.RemoveAt(0); 122 | } 123 | } 124 | 125 | private void Reset() 126 | { 127 | _keys = null; 128 | Children.Clear(); 129 | RowDefinitions.Clear(); 130 | ColumnDefinitions.Clear(); 131 | } 132 | } 133 | } -------------------------------------------------------------------------------- /WpfKb/Input/InputSimulator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | using WpfKb.Input.Native; 7 | 8 | namespace WpfKb.Input 9 | { 10 | public static class InputSimulator 11 | { 12 | [DllImport("user32.dll", SetLastError = true)] 13 | private static extern uint SendInput(uint numberOfInputs, INPUT[] inputs, int sizeOfInputStructure); 14 | 15 | [DllImport("user32.dll", SetLastError = true)] 16 | private static extern short GetAsyncKeyState(ushort virtualKeyCode); 17 | 18 | [DllImport("user32.dll", SetLastError = true)] 19 | private static extern short GetKeyState(ushort virtualKeyCode); 20 | 21 | [DllImport("user32.dll")] 22 | private static extern IntPtr GetMessageExtraInfo(); 23 | 24 | public static bool IsKeyDownAsync(VirtualKeyCode keyCode) 25 | { 26 | short result = GetAsyncKeyState((ushort)keyCode); 27 | return result < 0; 28 | } 29 | 30 | public static bool IsKeyDown(VirtualKeyCode keyCode) 31 | { 32 | short result = GetKeyState((ushort)keyCode); 33 | return result < 0; 34 | } 35 | 36 | public static bool IsTogglingKeyInEffect(VirtualKeyCode keyCode) 37 | { 38 | short result = GetKeyState((ushort)keyCode); 39 | return (result & 1) == 1; 40 | } 41 | 42 | public static void SimulateKeyDown(VirtualKeyCode keyCode) 43 | { 44 | INPUT down = default(INPUT); 45 | down.Type = InputType.KEYBOARD; 46 | down.Data.Keyboard = default(KEYBDINPUT); 47 | down.Data.Keyboard.Vk = keyCode; 48 | down.Data.Keyboard.Scan = 0; 49 | down.Data.Keyboard.Flags = 0u; 50 | down.Data.Keyboard.Time = 0u; 51 | down.Data.Keyboard.ExtraInfo = UIntPtr.Zero; 52 | uint numberOfSuccessfulSimulatedInputs = SendInput(1u, new INPUT[] 53 | { 54 | down 55 | }, Marshal.SizeOf(typeof(INPUT))); 56 | if (numberOfSuccessfulSimulatedInputs == 0u) 57 | { 58 | throw new Exception(string.Format("The key down simulation for {0} was not successful.", keyCode)); 59 | } 60 | } 61 | 62 | public static void SimulateKeyUp(VirtualKeyCode keyCode) 63 | { 64 | INPUT up = default(INPUT); 65 | up.Type = InputType.KEYBOARD; 66 | up.Data.Keyboard = default(KEYBDINPUT); 67 | up.Data.Keyboard.Vk = keyCode; 68 | up.Data.Keyboard.Scan = 0; 69 | up.Data.Keyboard.Flags = KEYEVENTF.KEYUP; 70 | up.Data.Keyboard.Time = 0u; 71 | up.Data.Keyboard.ExtraInfo = UIntPtr.Zero; 72 | uint numberOfSuccessfulSimulatedInputs = SendInput(1u, new INPUT[] 73 | { 74 | up 75 | }, Marshal.SizeOf(typeof(INPUT))); 76 | if (numberOfSuccessfulSimulatedInputs == 0u) 77 | { 78 | throw new Exception(string.Format("The key up simulation for {0} was not successful.", keyCode)); 79 | } 80 | } 81 | 82 | public static void SimulateKeyPress(VirtualKeyCode keyCode) 83 | { 84 | INPUT down = default(INPUT); 85 | down.Type = InputType.KEYBOARD; 86 | down.Data.Keyboard = default(KEYBDINPUT); 87 | down.Data.Keyboard.Vk = keyCode; 88 | down.Data.Keyboard.Scan = 0; 89 | down.Data.Keyboard.Flags = 0u; 90 | down.Data.Keyboard.Time = 0u; 91 | down.Data.Keyboard.ExtraInfo = UIntPtr.Zero; 92 | 93 | INPUT up = default(INPUT); 94 | up.Type = InputType.KEYBOARD; 95 | up.Data.Keyboard = default(KEYBDINPUT); 96 | up.Data.Keyboard.Vk = keyCode; 97 | up.Data.Keyboard.Scan = 0; 98 | up.Data.Keyboard.Flags = KEYEVENTF.KEYUP; 99 | up.Data.Keyboard.Time = 0u; 100 | up.Data.Keyboard.ExtraInfo = UIntPtr.Zero; 101 | uint numberOfSuccessfulSimulatedInputs = SendInput(2u, new INPUT[] 102 | { 103 | down, 104 | up 105 | }, Marshal.SizeOf(typeof(INPUT))); 106 | if (numberOfSuccessfulSimulatedInputs == 0u) 107 | { 108 | throw new Exception(string.Format("The key press simulation for {0} was not successful.", keyCode)); 109 | } 110 | } 111 | 112 | public static void SimulateTextEntry(string text) 113 | { 114 | if ((uint) text.Length > int.MaxValue) 115 | { 116 | throw new ArgumentException( 117 | string.Format("The text parameter is too long. It must be less than {0} characters.", 2147483647u), "text"); 118 | } 119 | 120 | byte[] chars = Encoding.ASCII.GetBytes(text); 121 | int len = chars.Length; 122 | INPUT[] inputList = new INPUT[len * 2]; 123 | for (int x = 0; x < len; x++) 124 | { 125 | ushort scanCode = (ushort)chars[x]; 126 | INPUT down = default(INPUT); 127 | down.Type = InputType.KEYBOARD; 128 | down.Data.Keyboard = default(KEYBDINPUT); 129 | down.Data.Keyboard.Vk = 0; 130 | down.Data.Keyboard.Scan = scanCode; 131 | down.Data.Keyboard.Flags = KEYEVENTF.UNICODE; 132 | down.Data.Keyboard.Time = 0u; 133 | down.Data.Keyboard.ExtraInfo = UIntPtr.Zero; 134 | 135 | INPUT up = default(INPUT); 136 | up.Type = InputType.KEYBOARD; 137 | up.Data.Keyboard = default(KEYBDINPUT); 138 | up.Data.Keyboard.Vk = 0; 139 | up.Data.Keyboard.Scan = scanCode; 140 | up.Data.Keyboard.Flags = KEYEVENTF.UNICODE | KEYEVENTF.KEYUP; 141 | up.Data.Keyboard.Time = 0u; 142 | up.Data.Keyboard.ExtraInfo = UIntPtr.Zero; 143 | if ((scanCode & 65280) == 57344) 144 | { 145 | down.Data.Keyboard.Flags = (down.Data.Keyboard.Flags | KEYEVENTF.EXTENDEDKEY); 146 | up.Data.Keyboard.Flags = (up.Data.Keyboard.Flags | KEYEVENTF.EXTENDEDKEY); 147 | } 148 | inputList[2 * x] = down; 149 | inputList[2 * x + 1] = up; 150 | } 151 | uint numberOfSuccessfulSimulatedInputs = InputSimulator.SendInput((uint)(len * 2), inputList, 152 | Marshal.SizeOf(typeof(INPUT))); 153 | } 154 | 155 | public static void SimulateModifiedKeyStroke(VirtualKeyCode modifierKeyCode, VirtualKeyCode keyCode) 156 | { 157 | SimulateKeyDown(modifierKeyCode); 158 | SimulateKeyPress(keyCode); 159 | SimulateKeyUp(modifierKeyCode); 160 | } 161 | 162 | public static void SimulateModifiedKeyStroke(IEnumerable modifierKeyCodes, 163 | VirtualKeyCode keyCode) 164 | { 165 | List modifierKeyCodesList = modifierKeyCodes?.ToList(); 166 | modifierKeyCodesList?.ForEach(SimulateKeyDown); 167 | SimulateKeyPress(keyCode); 168 | modifierKeyCodesList?.Reverse().ToList().ForEach(SimulateKeyUp); 169 | } 170 | 171 | 172 | public static void SimulateModifiedKeyStroke(VirtualKeyCode modifierKey, IEnumerable keyCodes) 173 | { 174 | SimulateKeyDown(modifierKey); 175 | keyCodes?.ToList().ForEach(SimulateKeyPress); 176 | SimulateKeyUp(modifierKey); 177 | } 178 | 179 | public static void SimulateModifiedKeyStroke(IEnumerable modifierKeyCodes, 180 | IEnumerable keyCodes) 181 | { 182 | List modifierKeyCodesList = modifierKeyCodes?.ToList(); 183 | 184 | modifierKeyCodesList?.ForEach(SimulateKeyDown); 185 | 186 | keyCodes?.ToList().ForEach(SimulateKeyPress); 187 | 188 | modifierKeyCodesList?.Reverse().ToList().ForEach(SimulateKeyUp); 189 | 190 | } 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /WpfKb/Behaviors/AutoHideBehavior.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Windows.Input; 4 | using Microsoft.Xaml.Behaviors; 5 | using System.Windows; 6 | using System.Windows.Media.Animation; 7 | using System.Windows.Threading; 8 | 9 | namespace WpfKb.Behaviors 10 | { 11 | public class AutoHideBehavior : Behavior 12 | { 13 | public enum ClickAction 14 | { 15 | None, 16 | Show, 17 | AcceleratedHide, 18 | } 19 | 20 | public static readonly DependencyProperty ActionWhenClickedProperty = DependencyProperty.Register("ActionWhenClicked", typeof(ClickAction), typeof(AutoHideBehavior), new UIPropertyMetadata(ClickAction.Show)); 21 | public static readonly DependencyProperty AreAnimationsEnabledProperty = DependencyProperty.Register("AreAnimationsEnabled", typeof(bool), typeof(AutoHideBehavior), new UIPropertyMetadata(true)); 22 | public static readonly DependencyProperty HideDelayProperty = DependencyProperty.Register("HideDelay", typeof(double), typeof(AutoHideBehavior), new UIPropertyMetadata(5d)); 23 | public static readonly DependencyProperty HideDurationProperty = DependencyProperty.Register("HideDuration", typeof(double), typeof(AutoHideBehavior), new UIPropertyMetadata(0.5d)); 24 | public static readonly DependencyProperty IsAllowedToHideProperty = DependencyProperty.Register("IsAllowedToHide", typeof(bool), typeof(AutoHideBehavior), new UIPropertyMetadata(true, OnIsAllowedToHidePropertyChanged)); 25 | public static readonly DependencyProperty IsAllowedToShowProperty = DependencyProperty.Register("IsAllowedToShow", typeof(bool), typeof(AutoHideBehavior), new UIPropertyMetadata(true)); 26 | public static readonly DependencyProperty IsShownProperty = DependencyProperty.Register("IsShown", typeof(bool), typeof(AutoHideBehavior), new UIPropertyMetadata(true, OnIsShownPropertyChanged)); 27 | public static readonly DependencyProperty MaxOpacityProperty = DependencyProperty.Register("MaxOpacity", typeof(double), typeof(AutoHideBehavior), new UIPropertyMetadata(1d)); 28 | public static readonly DependencyProperty MinOpacityProperty = DependencyProperty.Register("MinOpacity", typeof(double), typeof(AutoHideBehavior), new UIPropertyMetadata(0d)); 29 | public static readonly DependencyProperty ShowDurationProperty = DependencyProperty.Register("ShowDuration", typeof(double), typeof(AutoHideBehavior), new UIPropertyMetadata(0d)); 30 | public static readonly DependencyProperty TimerIntervalProperty = DependencyProperty.Register("TimerInterval", typeof(double), typeof(AutoHideBehavior), new UIPropertyMetadata(0.3d)); 31 | 32 | private DispatcherTimer _timer; 33 | private DateTime _lastActivityTime; 34 | 35 | public ClickAction ActionWhenClicked 36 | { 37 | get { return (ClickAction)GetValue(ActionWhenClickedProperty); } 38 | set { SetValue(ActionWhenClickedProperty, value); } 39 | } 40 | 41 | public bool AreAnimationsEnabled 42 | { 43 | get { return (bool)GetValue(AreAnimationsEnabledProperty); } 44 | set { SetValue(AreAnimationsEnabledProperty, value); } 45 | } 46 | 47 | public double HideDelay 48 | { 49 | get { return (double)GetValue(HideDelayProperty); } 50 | set { SetValue(HideDelayProperty, value); } 51 | } 52 | 53 | public double HideDuration 54 | { 55 | get { return (double)GetValue(HideDurationProperty); } 56 | set { SetValue(HideDurationProperty, value); } 57 | } 58 | 59 | public bool IsAllowedToHide 60 | { 61 | get { return (bool)GetValue(IsAllowedToHideProperty); } 62 | set { SetValue(IsAllowedToHideProperty, value); } 63 | } 64 | 65 | public bool IsAllowedToShow 66 | { 67 | get { return (bool)GetValue(IsAllowedToShowProperty); } 68 | set { SetValue(IsAllowedToShowProperty, value); } 69 | } 70 | 71 | public bool IsShown 72 | { 73 | get { return (bool)GetValue(IsShownProperty); } 74 | set { SetValue(IsShownProperty, value); } 75 | } 76 | 77 | public double MaxOpacity 78 | { 79 | get { return (double)GetValue(MaxOpacityProperty); } 80 | set { SetValue(MaxOpacityProperty, value); } 81 | } 82 | 83 | public double MinOpacity 84 | { 85 | get { return (double)GetValue(MinOpacityProperty); } 86 | set { SetValue(MinOpacityProperty, value); } 87 | } 88 | 89 | public double ShowDuration 90 | { 91 | get { return (double)GetValue(ShowDurationProperty); } 92 | set { SetValue(ShowDurationProperty, value); } 93 | } 94 | 95 | public double TimerInterval 96 | { 97 | get { return (double)GetValue(TimerIntervalProperty); } 98 | set { SetValue(TimerIntervalProperty, value); } 99 | } 100 | 101 | protected override void OnAttached() 102 | { 103 | base.OnAttached(); 104 | AssociatedObject.PreviewMouseDown += HandlePreviewMouseDown; 105 | Show(); 106 | PrepareToHide(); 107 | } 108 | 109 | protected override void OnDetaching() 110 | { 111 | base.OnDetaching(); 112 | AssociatedObject.PreviewMouseDown -= HandlePreviewMouseDown; 113 | } 114 | 115 | private static void OnIsAllowedToHidePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 116 | { 117 | var behavior = (AutoHideBehavior)d; 118 | behavior.PingActivity(); 119 | } 120 | 121 | private static void OnIsShownPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 122 | { 123 | var behavior = (AutoHideBehavior) d; 124 | if ((bool)e.NewValue) behavior.Show(); 125 | else behavior.Hide(); 126 | } 127 | 128 | private void HandlePreviewMouseDown(object sender, MouseButtonEventArgs e) 129 | { 130 | _lastActivityTime = DateTime.Now; 131 | switch (ActionWhenClicked) 132 | { 133 | case ClickAction.Show: 134 | Show(); 135 | break; 136 | 137 | case ClickAction.AcceleratedHide: 138 | HideFast(); 139 | break; 140 | } 141 | } 142 | 143 | private void PrepareToHide() 144 | { 145 | PingActivity(); 146 | if (_timer == null) 147 | { 148 | _timer = new DispatcherTimer(TimeSpan.FromSeconds(TimerInterval), DispatcherPriority.Background, 149 | Tick, Dispatcher); 150 | } 151 | } 152 | 153 | private void Tick(object sender, EventArgs e) 154 | { 155 | AssociatedObject.IsHitTestVisible = AssociatedObject.Opacity > 0; 156 | if (DateTime.Now - _lastActivityTime > TimeSpan.FromSeconds(HideDelay)) 157 | { 158 | if (AssociatedObject.Opacity >= MaxOpacity && IsAllowedToHide) Hide(); 159 | else _lastActivityTime = DateTime.Now; 160 | } 161 | } 162 | 163 | public void PingActivity() 164 | { 165 | _lastActivityTime = DateTime.Now; 166 | } 167 | 168 | public void Show() 169 | { 170 | if (AssociatedObject == null) return; 171 | PingActivity(); 172 | PrepareToHide(); 173 | if (IsAllowedToShow) 174 | { 175 | var duration = AreAnimationsEnabled 176 | ? new Duration(TimeSpan.FromSeconds(ShowDuration)) 177 | : new Duration(TimeSpan.Zero); 178 | 179 | IsShown = true; 180 | AssociatedObject.BeginAnimation(UIElement.OpacityProperty, new DoubleAnimation(MaxOpacity, duration)); 181 | } 182 | } 183 | 184 | public void Hide() 185 | { 186 | if (AssociatedObject == null) return; 187 | 188 | var duration = AreAnimationsEnabled 189 | ? new Duration(TimeSpan.FromSeconds(HideDuration)) 190 | : new Duration(TimeSpan.Zero); 191 | 192 | IsShown = false; 193 | AssociatedObject.BeginAnimation(UIElement.OpacityProperty, new DoubleAnimation(MinOpacity, duration)); 194 | } 195 | 196 | public void HideFast() 197 | { 198 | if (AssociatedObject == null) return; 199 | IsShown = false; 200 | AssociatedObject.BeginAnimation(UIElement.OpacityProperty, null); 201 | AssociatedObject.BeginAnimation(UIElement.OpacityProperty, 202 | new DoubleAnimation(MinOpacity, new Duration(TimeSpan.Zero))); 203 | } 204 | } 205 | } -------------------------------------------------------------------------------- /WpfKb/Controls/FloatingTouchScreenKeyboard.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Windows; 4 | using System.Windows.Input; 5 | 6 | namespace WpfKb.Controls 7 | { 8 | public partial class FloatingTouchScreenKeyboard 9 | { 10 | public static readonly DependencyProperty AreAnimationsEnabledProperty = DependencyProperty.Register("AreAnimationsEnabled", typeof(bool), typeof(FloatingTouchScreenKeyboard), new UIPropertyMetadata(true)); 11 | public static readonly DependencyProperty IsAllowedToFadeProperty = DependencyProperty.Register("IsAllowedToFade", typeof(bool), typeof(FloatingTouchScreenKeyboard), new UIPropertyMetadata(true)); 12 | public static readonly DependencyProperty IsDraggingProperty = DependencyProperty.Register("IsDragging", typeof(bool), typeof(FloatingTouchScreenKeyboard), new UIPropertyMetadata(false)); 13 | public static readonly DependencyProperty IsDragHelperAllowedToHideProperty = DependencyProperty.Register("IsDragHelperAllowedToHide", typeof(bool), typeof(FloatingTouchScreenKeyboard), new UIPropertyMetadata(false)); 14 | public static readonly DependencyProperty IsKeyboardShownProperty = DependencyProperty.Register("IsKeyboardShown", typeof(bool), typeof(FloatingTouchScreenKeyboard), new UIPropertyMetadata(true)); 15 | public static readonly DependencyProperty MaximumKeyboardOpacityProperty = DependencyProperty.Register("MaximumKeyboardOpacity", typeof(double), typeof(FloatingTouchScreenKeyboard), new UIPropertyMetadata(0.9d)); 16 | public static readonly DependencyProperty MinimumKeyboardOpacityProperty = DependencyProperty.Register("MinimumKeyboardOpacity", typeof(double), typeof(FloatingTouchScreenKeyboard), new UIPropertyMetadata(0.2d)); 17 | public static readonly DependencyProperty KeyboardHideDelayProperty = DependencyProperty.Register("KeyboardHideDelay", typeof(double), typeof(FloatingTouchScreenKeyboard), new UIPropertyMetadata(5d)); 18 | public static readonly DependencyProperty KeyboardHideAnimationDurationProperty = DependencyProperty.Register("KeyboardHideAnimationDuration", typeof(double), typeof(FloatingTouchScreenKeyboard), new UIPropertyMetadata(0.5d)); 19 | public static readonly DependencyProperty KeyboardShowAnimationDurationProperty = DependencyProperty.Register("KeyboardShowAnimationDuration", typeof(double), typeof(FloatingTouchScreenKeyboard), new UIPropertyMetadata(0.5d)); 20 | public static readonly DependencyProperty DeadZoneProperty = DependencyProperty.Register("DeadZone", typeof(double), typeof(FloatingTouchScreenKeyboard), new UIPropertyMetadata(5d)); 21 | 22 | private Point _mouseDownPosition; 23 | private Point _mouseDownOffset; 24 | private bool _isAllowedToFadeValueBeforeDrag; 25 | 26 | public FloatingTouchScreenKeyboard() 27 | { 28 | InitializeComponent(); 29 | } 30 | 31 | /// 32 | /// Gets or sets a value indicating whether animations are enabled. 33 | /// 34 | /// 35 | /// true if animations are enabled; otherwise, false. 36 | /// 37 | public bool AreAnimationsEnabled 38 | { 39 | get { return (bool)GetValue(AreAnimationsEnabledProperty); } 40 | set { SetValue(AreAnimationsEnabledProperty, value); } 41 | } 42 | 43 | /// 44 | /// Gets or sets a value that indicates whether the Keyboard is allowed to fade. This is a dependency property. 45 | /// 46 | public bool IsAllowedToFade 47 | { 48 | get { return (bool)GetValue(IsAllowedToFadeProperty); } 49 | set { SetValue(IsAllowedToFadeProperty, value); } 50 | } 51 | 52 | /// 53 | /// Gets a value that indicates if the keyboard is currently being dragged. This is a dependency property. 54 | /// 55 | public bool IsDragging 56 | { 57 | get { return (bool)GetValue(IsDraggingProperty); } 58 | private set { SetValue(IsDraggingProperty, value); } 59 | } 60 | 61 | /// 62 | /// Gets or sets a value that indicates if the drag helper text is allowed to hide. This is a dependency property. 63 | /// 64 | public bool IsDragHelperAllowedToHide 65 | { 66 | get { return (bool)GetValue(IsDragHelperAllowedToHideProperty); } 67 | set { SetValue(IsDragHelperAllowedToHideProperty, value); } 68 | } 69 | 70 | /// 71 | /// Gets a value that indicates that the keyboard is shown (not faded). This is a dependency property. 72 | /// 73 | public bool IsKeyboardShown 74 | { 75 | get { return (bool)GetValue(IsKeyboardShownProperty); } 76 | private set { SetValue(IsKeyboardShownProperty, value); } 77 | } 78 | 79 | /// 80 | /// Gets or sets the maximum opacity for a fully displayed keyboard. This is a dependency property. 81 | /// 82 | public double MaximumKeyboardOpacity 83 | { 84 | get { return (double)GetValue(MaximumKeyboardOpacityProperty); } 85 | set { SetValue(MaximumKeyboardOpacityProperty, value); } 86 | } 87 | 88 | /// 89 | /// Gets or sets the opacity to use for a partially hidden keyboard. This is a dependency property. 90 | /// 91 | public double MinimumKeyboardOpacity 92 | { 93 | get { return (double)GetValue(MinimumKeyboardOpacityProperty); } 94 | set { SetValue(MinimumKeyboardOpacityProperty, value); } 95 | } 96 | 97 | /// 98 | /// Gets or sets the number of seconds to wait after the last keyboard activity before hiding the keyboard. This is a dependency property. 99 | /// 100 | public double KeyboardHideDelay 101 | { 102 | get { return (double)GetValue(KeyboardHideDelayProperty); } 103 | set { SetValue(KeyboardHideDelayProperty, value); } 104 | } 105 | 106 | /// 107 | /// Gets or sets the duration in seconds for the keyboard hide animation. This is a dependency property. 108 | /// 109 | public double KeyboardHideAnimationDuration 110 | { 111 | get { return (double)GetValue(KeyboardHideAnimationDurationProperty); } 112 | set { SetValue(KeyboardHideAnimationDurationProperty, value); } 113 | } 114 | 115 | /// 116 | /// Gets or sets the duration in seconds for the keyboard show animation. This is a dependency property. 117 | /// 118 | public double KeyboardShowAnimationDuration 119 | { 120 | get { return (double)GetValue(KeyboardShowAnimationDurationProperty); } 121 | set { SetValue(KeyboardShowAnimationDurationProperty, value); } 122 | } 123 | 124 | /// 125 | /// Gets or sets the maximum opacity for a fully displayed keyboard. This is a dependency property. 126 | /// 127 | public double DeadZone 128 | { 129 | get { return (double)GetValue(DeadZoneProperty); } 130 | set { SetValue(DeadZoneProperty, value); } 131 | } 132 | 133 | protected override void OnOpened(EventArgs e) 134 | { 135 | IsKeyboardShown = true; 136 | base.OnOpened(e); 137 | } 138 | 139 | protected override void OnClosed(EventArgs e) 140 | { 141 | IsKeyboardShown = false; 142 | base.OnClosed(e); 143 | } 144 | 145 | private void DragHandle_PreviewMouseDown(object sender, MouseButtonEventArgs e) 146 | { 147 | _mouseDownPosition = e.GetPosition(PlacementTarget); 148 | _mouseDownOffset = new Point(HorizontalOffset, VerticalOffset); 149 | } 150 | 151 | private void DragHandle_PreviewMouseMove(object sender, MouseEventArgs e) 152 | { 153 | if (e.LeftButton == MouseButtonState.Pressed) 154 | { 155 | var delta = e.GetPosition(PlacementTarget) - _mouseDownPosition; 156 | if (!IsDragging && delta.Length > DeadZone) 157 | { 158 | IsDragging = true; 159 | IsDragHelperAllowedToHide = true; 160 | _isAllowedToFadeValueBeforeDrag = IsAllowedToFade; 161 | IsAllowedToFade = false; 162 | DragHandle.CaptureMouse(); 163 | } 164 | 165 | if (IsDragging) 166 | { 167 | HorizontalOffset = _mouseDownOffset.X + delta.X; 168 | VerticalOffset = _mouseDownOffset.Y + delta.Y; 169 | } 170 | } 171 | } 172 | 173 | private void DragHandle_PreviewMouseUp(object sender, MouseButtonEventArgs e) 174 | { 175 | DragHandle.ReleaseMouseCapture(); 176 | IsDragging = false; 177 | IsAllowedToFade = _isAllowedToFadeValueBeforeDrag; 178 | } 179 | 180 | } 181 | } -------------------------------------------------------------------------------- /WpfKb/Controls/OnScreenKeyboard.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Windows; 5 | using System.Windows.Controls; 6 | using System.Windows.Input; 7 | using System.Collections.ObjectModel; 8 | using WpfKb.Input; 9 | using WpfKb.LogicalKeys; 10 | 11 | namespace WpfKb.Controls 12 | { 13 | public class OnScreenKeyboard : Grid 14 | { 15 | 16 | public static readonly DependencyProperty AreAnimationsEnabledProperty = DependencyProperty.Register("AreAnimationsEnabled", typeof(bool), typeof(OnScreenKeyboard), new UIPropertyMetadata(true, OnAreAnimationsEnabledPropertyChanged)); 17 | 18 | private ObservableCollection _sections; 19 | private List _modifierKeys; 20 | private List _allLogicalKeys; 21 | private List _allOnScreenKeys; 22 | 23 | public bool AreAnimationsEnabled 24 | { 25 | get { return (bool)GetValue(AreAnimationsEnabledProperty); } 26 | set { SetValue(AreAnimationsEnabledProperty, value); } 27 | } 28 | 29 | private static void OnAreAnimationsEnabledPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 30 | { 31 | var keyboard = (OnScreenKeyboard)d; 32 | keyboard._allOnScreenKeys.ToList().ForEach(x => x.AreAnimationsEnabled = (bool)e.NewValue); 33 | } 34 | 35 | 36 | public override void BeginInit() 37 | { 38 | SetValue(FocusManager.IsFocusScopeProperty, true); 39 | _modifierKeys = new List(); 40 | _allLogicalKeys = new List(); 41 | _allOnScreenKeys = new List(); 42 | 43 | _sections = new ObservableCollection(); 44 | 45 | var mainSection = new OnScreenKeyboardSection(); 46 | var mainKeys = new ObservableCollection 47 | { 48 | new OnScreenKey { GridRow = 0, GridColumn = 0, Key = new ShiftSensitiveKey(VirtualKeyCode.OEM_3, new List { "`", "~" })}, 49 | new OnScreenKey { GridRow = 0, GridColumn = 1, Key = new ShiftSensitiveKey(VirtualKeyCode.VK_1, new List { "1", "!" })}, 50 | new OnScreenKey { GridRow = 0, GridColumn = 2, Key = new ShiftSensitiveKey(VirtualKeyCode.VK_2, new List { "2", "@" })}, 51 | new OnScreenKey { GridRow = 0, GridColumn = 3, Key = new ShiftSensitiveKey(VirtualKeyCode.VK_3, new List { "3", "#" })}, 52 | new OnScreenKey { GridRow = 0, GridColumn = 4, Key = new ShiftSensitiveKey(VirtualKeyCode.VK_4, new List { "4", "$" })}, 53 | new OnScreenKey { GridRow = 0, GridColumn = 5, Key = new ShiftSensitiveKey(VirtualKeyCode.VK_5, new List { "5", "%" })}, 54 | new OnScreenKey { GridRow = 0, GridColumn = 6, Key = new ShiftSensitiveKey(VirtualKeyCode.VK_6, new List { "6", "^" })}, 55 | new OnScreenKey { GridRow = 0, GridColumn = 7, Key = new ShiftSensitiveKey(VirtualKeyCode.VK_7, new List { "7", "&" })}, 56 | new OnScreenKey { GridRow = 0, GridColumn = 8, Key = new ShiftSensitiveKey(VirtualKeyCode.VK_8, new List { "8", "*" })}, 57 | new OnScreenKey { GridRow = 0, GridColumn = 9, Key = new ShiftSensitiveKey(VirtualKeyCode.VK_9, new List { "9", "(" })}, 58 | new OnScreenKey { GridRow = 0, GridColumn = 10, Key = new ShiftSensitiveKey(VirtualKeyCode.VK_0, new List { "0", ")" })}, 59 | new OnScreenKey { GridRow = 0, GridColumn = 11, Key = new ShiftSensitiveKey(VirtualKeyCode.OEM_MINUS, new List { "-", "_" })}, 60 | new OnScreenKey { GridRow = 0, GridColumn = 12, Key = new ShiftSensitiveKey(VirtualKeyCode.OEM_PLUS, new List { "=", "+" })}, 61 | new OnScreenKey { GridRow = 0, GridColumn = 13, Key = new VirtualKey(VirtualKeyCode.BACK, "Bksp"), GridWidth = new GridLength(2, GridUnitType.Star)}, 62 | 63 | new OnScreenKey { GridRow = 1, GridColumn = 0, Key = new VirtualKey(VirtualKeyCode.TAB, "Tab"), GridWidth = new GridLength(1.5, GridUnitType.Star)}, 64 | new OnScreenKey { GridRow = 1, GridColumn = 1, Key = new CaseSensitiveKey(VirtualKeyCode.VK_Q, new List { "q", "Q" })}, 65 | new OnScreenKey { GridRow = 1, GridColumn = 2, Key = new CaseSensitiveKey(VirtualKeyCode.VK_W, new List { "w", "W" })}, 66 | new OnScreenKey { GridRow = 1, GridColumn = 3, Key = new CaseSensitiveKey(VirtualKeyCode.VK_E, new List { "e", "E" })}, 67 | new OnScreenKey { GridRow = 1, GridColumn = 4, Key = new CaseSensitiveKey(VirtualKeyCode.VK_R, new List { "r", "R" })}, 68 | new OnScreenKey { GridRow = 1, GridColumn = 5, Key = new CaseSensitiveKey(VirtualKeyCode.VK_T, new List { "t", "T" })}, 69 | new OnScreenKey { GridRow = 1, GridColumn = 6, Key = new CaseSensitiveKey(VirtualKeyCode.VK_Y, new List { "y", "Y" })}, 70 | new OnScreenKey { GridRow = 1, GridColumn = 7, Key = new CaseSensitiveKey(VirtualKeyCode.VK_U, new List { "u", "U" })}, 71 | new OnScreenKey { GridRow = 1, GridColumn = 8, Key = new CaseSensitiveKey(VirtualKeyCode.VK_I, new List { "i", "I" })}, 72 | new OnScreenKey { GridRow = 1, GridColumn = 9, Key = new CaseSensitiveKey(VirtualKeyCode.VK_O, new List { "o", "O" })}, 73 | new OnScreenKey { GridRow = 1, GridColumn = 10, Key = new CaseSensitiveKey(VirtualKeyCode.VK_P, new List { "p", "P" })}, 74 | new OnScreenKey { GridRow = 1, GridColumn = 11, Key = new ShiftSensitiveKey(VirtualKeyCode.OEM_4, new List { "[", "{" })}, 75 | new OnScreenKey { GridRow = 1, GridColumn = 12, Key = new ShiftSensitiveKey(VirtualKeyCode.OEM_6, new List { "]", "}" })}, 76 | new OnScreenKey { GridRow = 1, GridColumn = 13, Key = new ShiftSensitiveKey(VirtualKeyCode.OEM_5, new List { "\\", "|" }), GridWidth = new GridLength(1.3, GridUnitType.Star)}, 77 | 78 | new OnScreenKey { GridRow = 2, GridColumn = 0, Key = new TogglingModifierKey("Caps", VirtualKeyCode.CAPITAL), GridWidth = new GridLength(1.7, GridUnitType.Star)}, 79 | new OnScreenKey { GridRow = 2, GridColumn = 1, Key = new CaseSensitiveKey(VirtualKeyCode.VK_A, new List { "a", "A" })}, 80 | new OnScreenKey { GridRow = 2, GridColumn = 2, Key = new CaseSensitiveKey(VirtualKeyCode.VK_S, new List { "s", "S" })}, 81 | new OnScreenKey { GridRow = 2, GridColumn = 3, Key = new CaseSensitiveKey(VirtualKeyCode.VK_D, new List { "d", "D" })}, 82 | new OnScreenKey { GridRow = 2, GridColumn = 4, Key = new CaseSensitiveKey(VirtualKeyCode.VK_F, new List { "f", "F" })}, 83 | new OnScreenKey { GridRow = 2, GridColumn = 5, Key = new CaseSensitiveKey(VirtualKeyCode.VK_G, new List { "g", "G" })}, 84 | new OnScreenKey { GridRow = 2, GridColumn = 6, Key = new CaseSensitiveKey(VirtualKeyCode.VK_H, new List { "h", "H" })}, 85 | new OnScreenKey { GridRow = 2, GridColumn = 7, Key = new CaseSensitiveKey(VirtualKeyCode.VK_J, new List { "j", "J" })}, 86 | new OnScreenKey { GridRow = 2, GridColumn = 8, Key = new CaseSensitiveKey(VirtualKeyCode.VK_K, new List { "k", "K" })}, 87 | new OnScreenKey { GridRow = 2, GridColumn = 9, Key = new CaseSensitiveKey(VirtualKeyCode.VK_L, new List { "l", "L" })}, 88 | new OnScreenKey { GridRow = 2, GridColumn = 10, Key = new ShiftSensitiveKey(VirtualKeyCode.OEM_1, new List { ";", ":" })}, 89 | new OnScreenKey { GridRow = 2, GridColumn = 11, Key = new ShiftSensitiveKey(VirtualKeyCode.OEM_7, new List { "\"", "\"" })}, 90 | new OnScreenKey { GridRow = 2, GridColumn = 12, Key = new VirtualKey(VirtualKeyCode.RETURN, "Enter"), GridWidth = new GridLength(1.8, GridUnitType.Star)}, 91 | 92 | new OnScreenKey { GridRow = 3, GridColumn = 0, Key = new InstantaneousModifierKey("Shift", VirtualKeyCode.SHIFT), GridWidth = new GridLength(2.4, GridUnitType.Star)}, 93 | new OnScreenKey { GridRow = 3, GridColumn = 1, Key = new CaseSensitiveKey(VirtualKeyCode.VK_Z, new List { "z", "Z" })}, 94 | new OnScreenKey { GridRow = 3, GridColumn = 2, Key = new CaseSensitiveKey(VirtualKeyCode.VK_X, new List { "x", "X" })}, 95 | new OnScreenKey { GridRow = 3, GridColumn = 3, Key = new CaseSensitiveKey(VirtualKeyCode.VK_C, new List { "c", "C" })}, 96 | new OnScreenKey { GridRow = 3, GridColumn = 4, Key = new CaseSensitiveKey(VirtualKeyCode.VK_V, new List { "v", "V" })}, 97 | new OnScreenKey { GridRow = 3, GridColumn = 5, Key = new CaseSensitiveKey(VirtualKeyCode.VK_B, new List { "b", "B" })}, 98 | new OnScreenKey { GridRow = 3, GridColumn = 6, Key = new CaseSensitiveKey(VirtualKeyCode.VK_N, new List { "n", "N" })}, 99 | new OnScreenKey { GridRow = 3, GridColumn = 7, Key = new CaseSensitiveKey(VirtualKeyCode.VK_M, new List { "m", "M" })}, 100 | new OnScreenKey { GridRow = 3, GridColumn = 8, Key = new ShiftSensitiveKey(VirtualKeyCode.OEM_COMMA, new List { ",", "<" })}, 101 | new OnScreenKey { GridRow = 3, GridColumn = 9, Key = new ShiftSensitiveKey(VirtualKeyCode.OEM_PERIOD, new List { ".", ">" })}, 102 | new OnScreenKey { GridRow = 3, GridColumn = 10, Key = new ShiftSensitiveKey(VirtualKeyCode.OEM_2, new List { "/", "?" })}, 103 | new OnScreenKey { GridRow = 3, GridColumn = 11, Key = new InstantaneousModifierKey("Shift", VirtualKeyCode.SHIFT), GridWidth = new GridLength(2.4, GridUnitType.Star)}, 104 | 105 | new OnScreenKey { GridRow = 4, GridColumn = 0, Key = new VirtualKey(VirtualKeyCode.SPACE, " "), GridWidth = new GridLength(5, GridUnitType.Star)}, 106 | }; 107 | 108 | mainSection.Keys = mainKeys; 109 | mainSection.SetValue(ColumnProperty, 0); 110 | _sections.Add(mainSection); 111 | ColumnDefinitions.Add(new ColumnDefinition {Width = new GridLength(3, GridUnitType.Star)}); 112 | Children.Add(mainSection); 113 | 114 | _allLogicalKeys.AddRange(mainKeys.Select(x => x.Key)); 115 | _allOnScreenKeys.AddRange(mainSection.Keys); 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | _modifierKeys.AddRange(_allLogicalKeys.OfType()); 124 | _allOnScreenKeys.ForEach(x => x.OnScreenKeyPress += OnScreenKeyPress); 125 | 126 | SynchroniseModifierKeyState(); 127 | 128 | base.BeginInit(); 129 | } 130 | 131 | void OnScreenKeyPress(DependencyObject sender, OnScreenKeyEventArgs e) 132 | { 133 | if (e.OnScreenKey.Key is ModifierKeyBase) 134 | { 135 | var modifierKey = (ModifierKeyBase)e.OnScreenKey.Key; 136 | if (modifierKey.KeyCode == VirtualKeyCode.SHIFT) 137 | { 138 | HandleShiftKeyPressed(modifierKey); 139 | } 140 | else if (modifierKey.KeyCode == VirtualKeyCode.CAPITAL) 141 | { 142 | HandleCapsLockKeyPressed(modifierKey); 143 | } 144 | else if (modifierKey.KeyCode == VirtualKeyCode.NUMLOCK) 145 | { 146 | HandleNumLockKeyPressed(modifierKey); 147 | } 148 | } 149 | else 150 | { 151 | ResetInstantaneousModifierKeys(); 152 | } 153 | _modifierKeys.OfType().ToList().ForEach(x => x.SynchroniseKeyState()); 154 | } 155 | 156 | private void SynchroniseModifierKeyState() 157 | { 158 | _modifierKeys.ToList().ForEach(x => x.SynchroniseKeyState()); 159 | } 160 | 161 | private void ResetInstantaneousModifierKeys() 162 | { 163 | _modifierKeys.OfType().ToList().ForEach(x => { if (x.IsInEffect) x.Press(); }); 164 | } 165 | 166 | void HandleShiftKeyPressed(ModifierKeyBase shiftKey) 167 | { 168 | _allLogicalKeys.OfType().ToList().ForEach(x => x.SelectedIndex = 169 | InputSimulator.IsTogglingKeyInEffect(VirtualKeyCode.CAPITAL) ^ shiftKey.IsInEffect ? 1 : 0); 170 | _allLogicalKeys.OfType().ToList().ForEach(x => x.SelectedIndex = shiftKey.IsInEffect ? 1 : 0); 171 | } 172 | 173 | void HandleCapsLockKeyPressed(ModifierKeyBase capsLockKey) 174 | { 175 | _allLogicalKeys.OfType().ToList().ForEach(x => x.SelectedIndex = 176 | capsLockKey.IsInEffect ^ InputSimulator.IsKeyDownAsync(VirtualKeyCode.SHIFT) ? 1 : 0); 177 | } 178 | 179 | void HandleNumLockKeyPressed(ModifierKeyBase numLockKey) 180 | { 181 | _allLogicalKeys.OfType().ToList().ForEach(x => x.SelectedIndex = numLockKey.IsInEffect? 1 : 0); 182 | } 183 | } 184 | } -------------------------------------------------------------------------------- /WpfKb/Controls/OnScreenKey.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows; 3 | using System.Windows.Controls; 4 | using System.Windows.Data; 5 | using System.Windows.Input; 6 | using System.Windows.Media; 7 | using System.Windows.Media.Animation; 8 | using WpfKb.LogicalKeys; 9 | 10 | namespace WpfKb.Controls 11 | { 12 | [TemplatePart(Name = ElementSurface, Type = typeof(Border))] 13 | [TemplatePart(Name = ElementMouseDownSurface, Type = typeof(Border))] 14 | [TemplatePart(Name = ElementKeyText, Type = typeof(TextBlock))] 15 | public class OnScreenKey : Control 16 | { 17 | private const string ElementSurface = "PART_Surface"; 18 | private const string ElementMouseDownSurface = "PART_MouseDownSurface"; 19 | private const string ElementKeyText = "PART_KeyText"; 20 | 21 | public static readonly DependencyProperty KeyProperty = DependencyProperty.Register("Key", typeof(ILogicalKey), typeof(OnScreenKey), new UIPropertyMetadata(null, OnKeyChanged)); 22 | 23 | public static readonly DependencyProperty AreAnimationsEnabledProperty = DependencyProperty.Register("AreAnimationsEnabled", typeof(bool), typeof(OnScreenKey), new UIPropertyMetadata(true)); 24 | public static readonly DependencyProperty IsMouseOverAnimationEnabledProperty = DependencyProperty.Register("IsMouseOverAnimationEnabled", typeof(bool), typeof(OnScreenKey), new UIPropertyMetadata(false)); 25 | public static readonly DependencyProperty IsOnScreenKeyDownProperty = DependencyProperty.Register("IsOnScreenKeyDown", typeof(bool), typeof(OnScreenKey), new UIPropertyMetadata(false)); 26 | public static readonly DependencyProperty GridWidthProperty = DependencyProperty.Register("GridWidth", typeof(GridLength), typeof(OnScreenKey), new UIPropertyMetadata(new GridLength(1, GridUnitType.Star))); 27 | 28 | public static readonly DependencyProperty TextBrushProperty = DependencyProperty.Register("TextBrush", typeof(Brush), typeof(OnScreenKey), new PropertyMetadata(default(Brush))); 29 | public static readonly DependencyProperty OutsideBorderBrushProperty = DependencyProperty.Register("OutsideBorderBrush", typeof(Brush), typeof(OnScreenKey), new PropertyMetadata(default(Brush))); 30 | public static readonly DependencyProperty OutsideBorderThicknessProperty = DependencyProperty.Register("OutsideBorderThickness", typeof(Thickness), typeof(OnScreenKey), new PropertyMetadata(default(Thickness))); 31 | 32 | public static readonly DependencyProperty MouseOverBrushProperty = DependencyProperty.Register("MouseOverBrush", typeof(Brush), typeof(OnScreenKey), new PropertyMetadata(default(Brush))); 33 | public static readonly DependencyProperty MouseOverBorderBrushProperty = DependencyProperty.Register("MouseOverBorderBrush", typeof(Brush), typeof(OnScreenKey), new PropertyMetadata(default(Brush))); 34 | 35 | public static readonly RoutedEvent PreviewOnScreenKeyDownEvent = EventManager.RegisterRoutedEvent("PreviewOnScreenKeyDown", RoutingStrategy.Direct, typeof(OnScreenKeyEventHandler), typeof(OnScreenKey)); 36 | public static readonly RoutedEvent PreviewOnScreenKeyUpEvent = EventManager.RegisterRoutedEvent("PreviewOnScreenKeyUp", RoutingStrategy.Direct, typeof(OnScreenKeyEventHandler), typeof(OnScreenKey)); 37 | public static readonly RoutedEvent OnScreenKeyDownEvent = EventManager.RegisterRoutedEvent("OnScreenKeyDown", RoutingStrategy.Direct, typeof(OnScreenKeyEventHandler), typeof(OnScreenKey)); 38 | public static readonly RoutedEvent OnScreenKeyUpEvent = EventManager.RegisterRoutedEvent("OnScreenKeyUp", RoutingStrategy.Direct, typeof(OnScreenKeyEventHandler), typeof(OnScreenKey)); 39 | public static readonly RoutedEvent OnScreenKeyPressEvent = EventManager.RegisterRoutedEvent("OnScreenKeyPress", RoutingStrategy.Direct, typeof(OnScreenKeyEventHandler), typeof(OnScreenKey)); 40 | 41 | 42 | private Border _keySurface; 43 | private Border _mouseDownSurface; 44 | private TextBlock _keyText; 45 | private Brush _keySurfaceBorderBrush; 46 | private Brush _keySurfaceBackground; 47 | private Brush _keyTextBrush; 48 | 49 | 50 | private readonly GradientBrush _keySurfaceMouseOverBrush = new LinearGradientBrush( 51 | 52 | new GradientStopCollection 53 | 54 | { 55 | 56 | new GradientStop(Color.FromRgb(0x78, 0x78, 0x78), 0), 57 | 58 | new GradientStop(Color.FromRgb(0x78, 0x78, 0x78), 0.6), 59 | 60 | new GradientStop(Color.FromRgb(0x50, 0x50, 0x50), 1) 61 | 62 | }, 90); 63 | 64 | public ILogicalKey Key 65 | { 66 | get { return (ILogicalKey)GetValue(KeyProperty); } 67 | set { SetValue(KeyProperty, value); } 68 | } 69 | 70 | #region NewTemplateBindings 71 | public Brush TextBrush 72 | { 73 | get { return (Brush)GetValue(TextBrushProperty); } 74 | set { SetValue(TextBrushProperty, value); } 75 | } 76 | 77 | public Brush OutsideBorderBrush 78 | { 79 | get { return (Brush)GetValue(OutsideBorderBrushProperty); } 80 | set { SetValue(OutsideBorderBrushProperty, value); } 81 | } 82 | 83 | public Thickness OutsideBorderThickness 84 | { 85 | get { return (Thickness)GetValue(OutsideBorderThicknessProperty); } 86 | set { SetValue(OutsideBorderThicknessProperty, value); } 87 | } 88 | 89 | public Brush MouseOverBrush 90 | { 91 | get { return (Brush)GetValue(MouseOverBrushProperty); } 92 | set { SetValue(MouseOverBrushProperty, value); } 93 | } 94 | 95 | public Brush MouseOverBorderBrush 96 | { 97 | get { return (Brush)GetValue(MouseOverBorderBrushProperty); } 98 | set { SetValue(MouseOverBorderBrushProperty, value); } 99 | } 100 | #endregion 101 | 102 | 103 | 104 | public bool AreAnimationsEnabled 105 | { 106 | get { return (bool)GetValue(AreAnimationsEnabledProperty); } 107 | set { SetValue(AreAnimationsEnabledProperty, value); } 108 | } 109 | 110 | public bool IsMouseOverAnimationEnabled 111 | { 112 | get { return (bool)GetValue(IsMouseOverAnimationEnabledProperty); } 113 | set { SetValue(IsMouseOverAnimationEnabledProperty, value); } 114 | } 115 | 116 | public bool IsOnScreenKeyDown 117 | { 118 | get { return (bool)GetValue(IsOnScreenKeyDownProperty); } 119 | private set { SetValue(IsOnScreenKeyDownProperty, value); } 120 | } 121 | 122 | public int GridRow 123 | { 124 | get { return (int)GetValue(Grid.RowProperty); } 125 | set { SetValue(Grid.RowProperty, value); } 126 | } 127 | 128 | public int GridColumn 129 | { 130 | get { return (int)GetValue(Grid.ColumnProperty); } 131 | set { SetValue(Grid.ColumnProperty, value); } 132 | } 133 | 134 | public GridLength GridWidth 135 | { 136 | get { return (GridLength)GetValue(GridWidthProperty); } 137 | set { SetValue(GridWidthProperty, value); } 138 | } 139 | 140 | 141 | public override void OnApplyTemplate() 142 | { 143 | base.OnApplyTemplate(); 144 | 145 | _keySurface = Template.FindName(ElementSurface, this) as Border; 146 | _mouseDownSurface = Template.FindName(ElementMouseDownSurface, this) as Border; 147 | _keyText = Template.FindName(ElementKeyText, this) as TextBlock; 148 | 149 | _keySurfaceBorderBrush = _keySurface?.BorderBrush; 150 | _keySurfaceBackground = _keySurface?.Background; 151 | _keyTextBrush = _keyText?.Foreground; 152 | _keyText?.SetBinding(TextBlock.TextProperty, new Binding("DisplayName") { Source = this.Key }); 153 | } 154 | 155 | 156 | protected static void OnKeyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) 157 | { 158 | ((OnScreenKey)sender).SetupControl((ILogicalKey)e.NewValue); 159 | } 160 | 161 | public event OnScreenKeyEventHandler PreviewOnScreenKeyDown 162 | { 163 | add { AddHandler(PreviewOnScreenKeyDownEvent, value); } 164 | remove { RemoveHandler(PreviewOnScreenKeyDownEvent, value); } 165 | } 166 | 167 | protected OnScreenKeyEventArgs RaisePreviewOnScreenKeyDownEvent() 168 | { 169 | var args = new OnScreenKeyEventArgs(PreviewOnScreenKeyDownEvent, this); 170 | RaiseEvent(args); 171 | return args; 172 | } 173 | 174 | public event OnScreenKeyEventHandler PreviewOnScreenKeyUp 175 | { 176 | add { AddHandler(PreviewOnScreenKeyUpEvent, value); } 177 | remove { RemoveHandler(PreviewOnScreenKeyUpEvent, value); } 178 | } 179 | 180 | protected OnScreenKeyEventArgs RaisePreviewOnScreenKeyUpEvent() 181 | { 182 | var args = new OnScreenKeyEventArgs(PreviewOnScreenKeyUpEvent, this); 183 | RaiseEvent(args); 184 | return args; 185 | } 186 | 187 | public event OnScreenKeyEventHandler OnScreenKeyDown 188 | { 189 | add { AddHandler(OnScreenKeyDownEvent, value); } 190 | remove { RemoveHandler(OnScreenKeyDownEvent, value); } 191 | } 192 | 193 | protected OnScreenKeyEventArgs RaiseOnScreenKeyDownEvent() 194 | { 195 | var args = new OnScreenKeyEventArgs(OnScreenKeyDownEvent, this); 196 | RaiseEvent(args); 197 | return args; 198 | } 199 | 200 | public event OnScreenKeyEventHandler OnScreenKeyUp 201 | { 202 | add { AddHandler(OnScreenKeyUpEvent, value); } 203 | remove { RemoveHandler(OnScreenKeyUpEvent, value); } 204 | } 205 | 206 | protected OnScreenKeyEventArgs RaiseOnScreenKeyUpEvent() 207 | { 208 | var args = new OnScreenKeyEventArgs(OnScreenKeyUpEvent, this); 209 | RaiseEvent(args); 210 | return args; 211 | } 212 | 213 | public event OnScreenKeyEventHandler OnScreenKeyPress 214 | { 215 | add { AddHandler(OnScreenKeyPressEvent, value); } 216 | remove { RemoveHandler(OnScreenKeyPressEvent, value); } 217 | } 218 | 219 | protected OnScreenKeyEventArgs RaiseOnScreenKeyPressEvent() 220 | { 221 | var args = new OnScreenKeyEventArgs(OnScreenKeyPressEvent, this); 222 | RaiseEvent(args); 223 | return args; 224 | } 225 | 226 | private void SetupControl(ILogicalKey key) 227 | { 228 | _keyText?.SetBinding(TextBlock.TextProperty, new Binding("DisplayName") {Source = key}); 229 | key.PropertyChanged += Key_PropertyChanged; 230 | key.LogicalKeyPressed += Key_VirtualKeyPressed; 231 | } 232 | 233 | void Key_VirtualKeyPressed(object sender, LogicalKeyEventArgs e) 234 | { 235 | RaiseOnScreenKeyPressEvent(); 236 | } 237 | 238 | void Key_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) 239 | { 240 | if (Key is ModifierKeyBase && e.PropertyName == "IsInEffect") 241 | { 242 | var key = ((ModifierKeyBase)Key); 243 | if (key.IsInEffect) 244 | { 245 | AnimateMouseDown(); 246 | } 247 | else 248 | { 249 | AnimateMouseUp(); 250 | } 251 | } 252 | } 253 | 254 | protected override void OnMouseDown(MouseButtonEventArgs e) 255 | { 256 | HandleKeyDown(); 257 | base.OnMouseDown(e); 258 | } 259 | 260 | protected void HandleKeyDown() 261 | { 262 | var args = RaisePreviewOnScreenKeyDownEvent(); 263 | if (args.Handled == false) 264 | { 265 | IsOnScreenKeyDown = true; 266 | AnimateMouseDown(); 267 | Key.Press(); 268 | } 269 | RaiseOnScreenKeyDownEvent(); 270 | } 271 | 272 | protected override void OnMouseUp(MouseButtonEventArgs e) 273 | { 274 | HandleKeyUp(); 275 | base.OnMouseUp(e); 276 | } 277 | 278 | private void HandleKeyUp() 279 | { 280 | var args = RaisePreviewOnScreenKeyUpEvent(); 281 | if (args.Handled == false) 282 | { 283 | IsOnScreenKeyDown = false; 284 | AnimateMouseUp(); 285 | } 286 | RaiseOnScreenKeyUpEvent(); 287 | } 288 | 289 | private void AnimateMouseDown() 290 | { 291 | _mouseDownSurface.BeginAnimation(OpacityProperty, new DoubleAnimation(1, new Duration(TimeSpan.Zero))); 292 | _keyText.Foreground = OutsideBorderBrush; 293 | } 294 | 295 | private void AnimateMouseUp() 296 | { 297 | if ((Key is TogglingModifierKey || Key is InstantaneousModifierKey) && ((ModifierKeyBase)Key).IsInEffect) return; 298 | _keySurface.BorderBrush = _keySurfaceBorderBrush; 299 | _keyText.Foreground = _keyTextBrush; 300 | if (!AreAnimationsEnabled || Key is TogglingModifierKey || Key is InstantaneousModifierKey) 301 | { 302 | _mouseDownSurface.BeginAnimation(OpacityProperty, new DoubleAnimation(0, new Duration(TimeSpan.Zero))); 303 | } 304 | else 305 | { 306 | _mouseDownSurface.BeginAnimation(OpacityProperty, new DoubleAnimation(0, Duration.Automatic)); 307 | } 308 | } 309 | 310 | protected override void OnMouseEnter(MouseEventArgs e) 311 | { 312 | if (IsMouseOverAnimationEnabled) 313 | { 314 | if (MouseOverBrush != null) 315 | _keySurface.Background = MouseOverBrush; 316 | 317 | if (MouseOverBorderBrush != null) 318 | _keySurface.BorderBrush = MouseOverBorderBrush; 319 | } 320 | base.OnMouseEnter(e); 321 | } 322 | 323 | protected override void OnMouseLeave(MouseEventArgs e) 324 | { 325 | if (IsMouseOverAnimationEnabled) 326 | { 327 | if (Key is TogglingModifierKey && ((ModifierKeyBase)Key).IsInEffect) return; 328 | 329 | _keySurface.Background = _keySurfaceBackground; 330 | _keySurface.BorderBrush = _keySurfaceBorderBrush; 331 | } 332 | if (IsOnScreenKeyDown) 333 | { 334 | HandleKeyUp(); 335 | } 336 | base.OnMouseLeave(e); 337 | } 338 | } 339 | } -------------------------------------------------------------------------------- /WpfKb/Controls/OnScreenWebKeyboard.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Collections.ObjectModel; 3 | using System.Linq; 4 | using System.Windows; 5 | using System.Windows.Controls; 6 | using System.Windows.Input; 7 | using WpfKb.Input; 8 | using WpfKb.LogicalKeys; 9 | 10 | 11 | namespace WpfKb.Controls 12 | { 13 | public class OnScreenWebKeyboard : Grid 14 | { 15 | public static readonly DependencyProperty AreAnimationsEnabledProperty = DependencyProperty.Register("AreAnimationsEnabled", typeof(bool), typeof(OnScreenWebKeyboard), new UIPropertyMetadata(true, OnAreAnimationsEnabledPropertyChanged)); 16 | 17 | private ObservableCollection _sections; 18 | private List _modifierKeys; 19 | private List _allLogicalKeys; 20 | private List _allOnScreenKeys; 21 | 22 | public bool AreAnimationsEnabled 23 | { 24 | get { return (bool)GetValue(AreAnimationsEnabledProperty); } 25 | set { SetValue(AreAnimationsEnabledProperty, value); } 26 | } 27 | 28 | private static void OnAreAnimationsEnabledPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 29 | { 30 | var keyboard = (OnScreenWebKeyboard)d; 31 | keyboard._allOnScreenKeys.ToList().ForEach(x => x.AreAnimationsEnabled = (bool)e.NewValue); 32 | } 33 | 34 | public override void BeginInit() 35 | { 36 | SetValue(FocusManager.IsFocusScopeProperty, true); 37 | _modifierKeys = new List(); 38 | _allLogicalKeys = new List(); 39 | _allOnScreenKeys = new List(); 40 | 41 | _sections = new ObservableCollection(); 42 | 43 | var mainSection = new OnScreenKeyboardSection(); 44 | var mainKeys = new ObservableCollection 45 | { 46 | new OnScreenKey { GridRow = 0, GridColumn = 0, Key = new ShiftSensitiveKey(VirtualKeyCode.OEM_3, new List { "`", "~" })}, 47 | new OnScreenKey { GridRow = 0, GridColumn = 1, Key = new ShiftSensitiveKey(VirtualKeyCode.VK_1, new List { "1", "!" })}, 48 | new OnScreenKey { GridRow = 0, GridColumn = 2, Key = new ShiftSensitiveKey(VirtualKeyCode.VK_2, new List { "2", "@" })}, 49 | new OnScreenKey { GridRow = 0, GridColumn = 3, Key = new ShiftSensitiveKey(VirtualKeyCode.VK_3, new List { "3", "#" })}, 50 | new OnScreenKey { GridRow = 0, GridColumn = 4, Key = new ShiftSensitiveKey(VirtualKeyCode.VK_4, new List { "4", "$" })}, 51 | new OnScreenKey { GridRow = 0, GridColumn = 5, Key = new ShiftSensitiveKey(VirtualKeyCode.VK_5, new List { "5", "%" })}, 52 | new OnScreenKey { GridRow = 0, GridColumn = 6, Key = new ShiftSensitiveKey(VirtualKeyCode.VK_6, new List { "6", "^" })}, 53 | new OnScreenKey { GridRow = 0, GridColumn = 7, Key = new ShiftSensitiveKey(VirtualKeyCode.VK_7, new List { "7", "&" })}, 54 | new OnScreenKey { GridRow = 0, GridColumn = 8, Key = new ShiftSensitiveKey(VirtualKeyCode.VK_8, new List { "8", "*" })}, 55 | new OnScreenKey { GridRow = 0, GridColumn = 9, Key = new ShiftSensitiveKey(VirtualKeyCode.VK_9, new List { "9", "(" })}, 56 | new OnScreenKey { GridRow = 0, GridColumn = 10, Key = new ShiftSensitiveKey(VirtualKeyCode.VK_0, new List { "0", ")" })}, 57 | new OnScreenKey { GridRow = 0, GridColumn = 11, Key = new ShiftSensitiveKey(VirtualKeyCode.OEM_MINUS, new List { "-", "_" })}, 58 | new OnScreenKey { GridRow = 0, GridColumn = 12, Key = new ShiftSensitiveKey(VirtualKeyCode.OEM_PLUS, new List { "=", "+" })}, 59 | new OnScreenKey { GridRow = 0, GridColumn = 13, Key = new VirtualKey(VirtualKeyCode.BACK, "Bksp"), GridWidth = new GridLength(2, GridUnitType.Star)}, 60 | 61 | new OnScreenKey { GridRow = 1, GridColumn = 0, Key = new VirtualKey(VirtualKeyCode.TAB, "Tab"), GridWidth = new GridLength(1.5, GridUnitType.Star)}, 62 | new OnScreenKey { GridRow = 1, GridColumn = 1, Key = new CaseSensitiveKey(VirtualKeyCode.VK_Q, new List { "q", "Q" })}, 63 | new OnScreenKey { GridRow = 1, GridColumn = 2, Key = new CaseSensitiveKey(VirtualKeyCode.VK_W, new List { "w", "W" })}, 64 | new OnScreenKey { GridRow = 1, GridColumn = 3, Key = new CaseSensitiveKey(VirtualKeyCode.VK_E, new List { "e", "E" })}, 65 | new OnScreenKey { GridRow = 1, GridColumn = 4, Key = new CaseSensitiveKey(VirtualKeyCode.VK_R, new List { "r", "R" })}, 66 | new OnScreenKey { GridRow = 1, GridColumn = 5, Key = new CaseSensitiveKey(VirtualKeyCode.VK_T, new List { "t", "T" })}, 67 | new OnScreenKey { GridRow = 1, GridColumn = 6, Key = new CaseSensitiveKey(VirtualKeyCode.VK_Y, new List { "y", "Y" })}, 68 | new OnScreenKey { GridRow = 1, GridColumn = 7, Key = new CaseSensitiveKey(VirtualKeyCode.VK_U, new List { "u", "U" })}, 69 | new OnScreenKey { GridRow = 1, GridColumn = 8, Key = new CaseSensitiveKey(VirtualKeyCode.VK_I, new List { "i", "I" })}, 70 | new OnScreenKey { GridRow = 1, GridColumn = 9, Key = new CaseSensitiveKey(VirtualKeyCode.VK_O, new List { "o", "O" })}, 71 | new OnScreenKey { GridRow = 1, GridColumn = 10, Key = new CaseSensitiveKey(VirtualKeyCode.VK_P, new List { "p", "P" })}, 72 | new OnScreenKey { GridRow = 1, GridColumn = 11, Key = new ShiftSensitiveKey(VirtualKeyCode.OEM_4, new List { "[", "{" })}, 73 | new OnScreenKey { GridRow = 1, GridColumn = 12, Key = new ShiftSensitiveKey(VirtualKeyCode.OEM_6, new List { "]", "}" })}, 74 | new OnScreenKey { GridRow = 1, GridColumn = 13, Key = new ShiftSensitiveKey(VirtualKeyCode.OEM_5, new List { "\\", "|" }), GridWidth = new GridLength(1.3, GridUnitType.Star)}, 75 | 76 | new OnScreenKey { GridRow = 2, GridColumn = 0, Key = new TogglingModifierKey("Caps", VirtualKeyCode.CAPITAL), GridWidth = new GridLength(1.7, GridUnitType.Star)}, 77 | new OnScreenKey { GridRow = 2, GridColumn = 1, Key = new CaseSensitiveKey(VirtualKeyCode.VK_A, new List { "a", "A" })}, 78 | new OnScreenKey { GridRow = 2, GridColumn = 2, Key = new CaseSensitiveKey(VirtualKeyCode.VK_S, new List { "s", "S" })}, 79 | new OnScreenKey { GridRow = 2, GridColumn = 3, Key = new CaseSensitiveKey(VirtualKeyCode.VK_D, new List { "d", "D" })}, 80 | new OnScreenKey { GridRow = 2, GridColumn = 4, Key = new CaseSensitiveKey(VirtualKeyCode.VK_F, new List { "f", "F" })}, 81 | new OnScreenKey { GridRow = 2, GridColumn = 5, Key = new CaseSensitiveKey(VirtualKeyCode.VK_G, new List { "g", "G" })}, 82 | new OnScreenKey { GridRow = 2, GridColumn = 6, Key = new CaseSensitiveKey(VirtualKeyCode.VK_H, new List { "h", "H" })}, 83 | new OnScreenKey { GridRow = 2, GridColumn = 7, Key = new CaseSensitiveKey(VirtualKeyCode.VK_J, new List { "j", "J" })}, 84 | new OnScreenKey { GridRow = 2, GridColumn = 8, Key = new CaseSensitiveKey(VirtualKeyCode.VK_K, new List { "k", "K" })}, 85 | new OnScreenKey { GridRow = 2, GridColumn = 9, Key = new CaseSensitiveKey(VirtualKeyCode.VK_L, new List { "l", "L" })}, 86 | new OnScreenKey { GridRow = 2, GridColumn = 10, Key = new ShiftSensitiveKey(VirtualKeyCode.OEM_1, new List { ";", ":" })}, 87 | new OnScreenKey { GridRow = 2, GridColumn = 11, Key = new ShiftSensitiveKey(VirtualKeyCode.OEM_7, new List { "\"", "\"" })}, 88 | new OnScreenKey { GridRow = 2, GridColumn = 12, Key = new VirtualKey(VirtualKeyCode.RETURN, "Enter"), GridWidth = new GridLength(1.8, GridUnitType.Star)}, 89 | 90 | new OnScreenKey { GridRow = 3, GridColumn = 0, Key = new InstantaneousModifierKey("Shift", VirtualKeyCode.SHIFT), GridWidth = new GridLength(2.4, GridUnitType.Star)}, 91 | new OnScreenKey { GridRow = 3, GridColumn = 1, Key = new CaseSensitiveKey(VirtualKeyCode.VK_Z, new List { "z", "Z" })}, 92 | new OnScreenKey { GridRow = 3, GridColumn = 2, Key = new CaseSensitiveKey(VirtualKeyCode.VK_X, new List { "x", "X" })}, 93 | new OnScreenKey { GridRow = 3, GridColumn = 3, Key = new CaseSensitiveKey(VirtualKeyCode.VK_C, new List { "c", "C" })}, 94 | new OnScreenKey { GridRow = 3, GridColumn = 4, Key = new CaseSensitiveKey(VirtualKeyCode.VK_V, new List { "v", "V" })}, 95 | new OnScreenKey { GridRow = 3, GridColumn = 5, Key = new CaseSensitiveKey(VirtualKeyCode.VK_B, new List { "b", "B" })}, 96 | new OnScreenKey { GridRow = 3, GridColumn = 6, Key = new CaseSensitiveKey(VirtualKeyCode.VK_N, new List { "n", "N" })}, 97 | new OnScreenKey { GridRow = 3, GridColumn = 7, Key = new CaseSensitiveKey(VirtualKeyCode.VK_M, new List { "m", "M" })}, 98 | new OnScreenKey { GridRow = 3, GridColumn = 8, Key = new ShiftSensitiveKey(VirtualKeyCode.OEM_COMMA, new List { ",", "<" })}, 99 | new OnScreenKey { GridRow = 3, GridColumn = 9, Key = new ShiftSensitiveKey(VirtualKeyCode.OEM_PERIOD, new List { ".", ">" })}, 100 | new OnScreenKey { GridRow = 3, GridColumn = 10, Key = new ShiftSensitiveKey(VirtualKeyCode.OEM_2, new List { "/", "?" })}, 101 | new OnScreenKey { GridRow = 3, GridColumn = 11, Key = new InstantaneousModifierKey("Shift", VirtualKeyCode.SHIFT), GridWidth = new GridLength(2.4, GridUnitType.Star)}, 102 | 103 | new OnScreenKey { GridRow = 4, GridColumn = 0, Key = new VirtualKey(VirtualKeyCode.SPACE, " "), GridWidth = new GridLength(5, GridUnitType.Star)}, 104 | new OnScreenKey { GridRow = 4, GridColumn = 1, Key = new StringKey("http://", "http://")}, 105 | new OnScreenKey { GridRow = 4, GridColumn = 2, Key = new StringKey("https://", "https://")}, 106 | }; 107 | 108 | mainSection.Keys = mainKeys; 109 | mainSection.SetValue(ColumnProperty, 0); 110 | _sections.Add(mainSection); 111 | ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(3, GridUnitType.Star) }); 112 | Children.Add(mainSection); 113 | 114 | _allLogicalKeys.AddRange(mainKeys.Select(x => x.Key)); 115 | _allOnScreenKeys.AddRange(mainSection.Keys); 116 | 117 | 118 | var specialSection = new OnScreenKeyboardSection(); 119 | var specialKeys = new ObservableCollection 120 | { 121 | new OnScreenKey { GridRow = 0, GridColumn = 0, Key = new ChordKey("Select All", VirtualKeyCode.CONTROL, VirtualKeyCode.VK_A), GridWidth = new GridLength(2, GridUnitType.Star)}, 122 | new OnScreenKey { GridRow = 0, GridColumn = 1, Key = new ChordKey("Undo", VirtualKeyCode.CONTROL, VirtualKeyCode.VK_Z) }, 123 | new OnScreenKey { GridRow = 1, GridColumn = 0, Key = new ChordKey("Copy", VirtualKeyCode.CONTROL, VirtualKeyCode.VK_C) }, 124 | new OnScreenKey { GridRow = 1, GridColumn = 1, Key = new ChordKey("Cut", VirtualKeyCode.CONTROL, VirtualKeyCode.VK_X) }, 125 | new OnScreenKey { GridRow = 1, GridColumn = 2, Key = new ChordKey("Paste", VirtualKeyCode.CONTROL, VirtualKeyCode.VK_V) }, 126 | new OnScreenKey { GridRow = 2, GridColumn = 0, Key = new VirtualKey(VirtualKeyCode.DELETE, "Del") }, 127 | new OnScreenKey { GridRow = 2, GridColumn = 1, Key = new VirtualKey(VirtualKeyCode.HOME, "Home") }, 128 | new OnScreenKey { GridRow = 2, GridColumn = 2, Key = new VirtualKey(VirtualKeyCode.END, "End") }, 129 | new OnScreenKey { GridRow = 3, GridColumn = 0, Key = new VirtualKey(VirtualKeyCode.PRIOR, "PgUp") }, 130 | new OnScreenKey { GridRow = 3, GridColumn = 1, Key = new VirtualKey(VirtualKeyCode.UP, "Up") }, 131 | new OnScreenKey { GridRow = 3, GridColumn = 2, Key = new VirtualKey(VirtualKeyCode.NEXT, "PgDn") }, 132 | new OnScreenKey { GridRow = 4, GridColumn = 0, Key = new VirtualKey(VirtualKeyCode.LEFT, "Left") }, 133 | new OnScreenKey { GridRow = 4, GridColumn = 1, Key = new VirtualKey(VirtualKeyCode.DOWN, "Down") }, 134 | new OnScreenKey { GridRow = 4, GridColumn = 2, Key = new VirtualKey(VirtualKeyCode.RIGHT, "Right") }, 135 | }; 136 | 137 | specialSection.Keys = specialKeys; 138 | specialSection.SetValue(ColumnProperty, 1); 139 | _sections.Add(specialSection); 140 | ColumnDefinitions.Add(new ColumnDefinition()); 141 | Children.Add(specialSection); 142 | 143 | _allLogicalKeys.AddRange(specialKeys.Select(x => x.Key)); 144 | _allOnScreenKeys.AddRange(specialSection.Keys); 145 | 146 | 147 | 148 | _modifierKeys.AddRange(_allLogicalKeys.OfType()); 149 | _allOnScreenKeys.ForEach(x => x.OnScreenKeyPress += OnScreenKeyPress); 150 | 151 | SynchroniseModifierKeyState(); 152 | 153 | base.BeginInit(); 154 | } 155 | 156 | void OnScreenKeyPress(DependencyObject sender, OnScreenKeyEventArgs e) 157 | { 158 | if (e.OnScreenKey.Key is ModifierKeyBase) 159 | { 160 | var modifierKey = (ModifierKeyBase)e.OnScreenKey.Key; 161 | if (modifierKey.KeyCode == VirtualKeyCode.SHIFT) 162 | { 163 | HandleShiftKeyPressed(modifierKey); 164 | } 165 | else if (modifierKey.KeyCode == VirtualKeyCode.CAPITAL) 166 | { 167 | HandleCapsLockKeyPressed(modifierKey); 168 | } 169 | else if (modifierKey.KeyCode == VirtualKeyCode.NUMLOCK) 170 | { 171 | HandleNumLockKeyPressed(modifierKey); 172 | } 173 | } 174 | else 175 | { 176 | ResetInstantaneousModifierKeys(); 177 | } 178 | _modifierKeys.OfType().ToList().ForEach(x => x.SynchroniseKeyState()); 179 | } 180 | 181 | private void SynchroniseModifierKeyState() 182 | { 183 | _modifierKeys.ToList().ForEach(x => x.SynchroniseKeyState()); 184 | } 185 | 186 | private void ResetInstantaneousModifierKeys() 187 | { 188 | _modifierKeys.OfType().ToList().ForEach(x => { if (x.IsInEffect) x.Press(); }); 189 | } 190 | 191 | void HandleShiftKeyPressed(ModifierKeyBase shiftKey) 192 | { 193 | _allLogicalKeys.OfType().ToList().ForEach(x => x.SelectedIndex = 194 | InputSimulator.IsTogglingKeyInEffect(VirtualKeyCode.CAPITAL) ^ shiftKey.IsInEffect ? 1 : 0); 195 | _allLogicalKeys.OfType().ToList().ForEach(x => x.SelectedIndex = shiftKey.IsInEffect ? 1 : 0); 196 | } 197 | 198 | void HandleCapsLockKeyPressed(ModifierKeyBase capsLockKey) 199 | { 200 | _allLogicalKeys.OfType().ToList().ForEach(x => x.SelectedIndex = 201 | capsLockKey.IsInEffect ^ InputSimulator.IsKeyDownAsync(VirtualKeyCode.SHIFT) ? 1 : 0); 202 | } 203 | 204 | void HandleNumLockKeyPressed(ModifierKeyBase numLockKey) 205 | { 206 | _allLogicalKeys.OfType().ToList().ForEach(x => x.SelectedIndex = numLockKey.IsInEffect ? 1 : 0); 207 | } 208 | } 209 | } --------------------------------------------------------------------------------