using System; using System.Windows; using System.Windows.Controls; // Button, TextBox, SelectionChangedEventArgs using System.Windows.Input; // KeyEventArgs, ExecutedRoutedEventArgs, RoutedUICommand using System.Windows.Media; // FontFamily using System.Globalization; // CultureInfo using System.Windows.Threading; // DispatcherTimer using System.Diagnostics; // Debug using System.Windows.Documents; // EditingCommands using JHLib; using JHVirtualKeyboard.KeyAssignmentSets; // Notes on using this: // // To have the virtual keyboard (VK) remember whether it was open for your Window when it last closed, // and automatically open with your Window when you next open it: // // Add an entry within your application's Settings to store this setting, in UserScope and of type bool. // In this example, the name of the entry is "IsVirtualKeyboardUpForItemEditDialog". // // Handle the ContentRendered event thus: // // void OnContentRendered(object sender, EventArgs e) // { // if (Properties.Settings.Default.IsVirtualKeyboardUpForItemEditDialog) // { // VirtualKeyboard.ShowOrAttachTo(this, ref _virtualKeyboard); // } // } // // // Handle the Closing event thus: // // void OnClosing(object sender, System.ComponentModel.CancelEventArgs e) // { // // If the virtual keyboard was up, then remember that so we can bring it up next time this same dialog-window is opened. // bool virtualKeyboardIsUp = _virtualKeyboard != null && _virtualKeyboard.IsUp; // Properties.Settings.Default.IsVirtualKeyboardUpForItemEditDialog = virtualKeyboardIsUp; // Properties.Settings.Default.Save(); // } // // That needs to be in Closing, not Closed, because it needs to happen before the VK itself closes, // which it automatically does if your Window is opened as a modal dialog. namespace JHVirtualKeyboard { public enum WhichKeyboardLayout { Unknown, English, Arabic, French, GermanAustrian, Italian, Mandarin, Russian, Spanish }; #region class VirtualKeyboard /// <summary> /// Interaction logic for the Virtual Keyboard. /// </summary> public partial class VirtualKeyboard : Window { #region Constructor private VirtualKeyboard() { try { _isSensitive = false; _domain = new KeyboardViewModel(); InitializeComponent(); LayoutUpdated += OnLayoutUpdated; Loaded += OnLoaded; ContentRendered += OnContentRendered; LocationChanged += OnLocationChanged; Closing += new System.ComponentModel.CancelEventHandler(OnClosing); Closed += OnClosed; } catch (Exception x) { Console.WriteLine("Exception in VirtualKeyboard ctor: " + x.Message); // Below, is what I normally use as my standard for raising objections within library routines (using my own std MessageBox substitute). //IApp app = Application.Current as IApp; //if (app != null) //{ // app.TheInterlocution.NotifyUserOfError("Well this is embarrassing!", "in VirtualKeyboard.Ctor", x, this); //} } } #endregion #region Event Handlers #region LayoutUpdated event handler /// <summary> /// We use this flag to remember whether we've already done the alignment, as we only want to do it once. /// </summary> private bool _HasBeenSelfPositioned; /// <summary> /// Handle the LayoutUpdated event. /// We choose this event to do the alignment of this Window, because by doing so we avoid the brief flash /// of this Window being rendered and then moved. /// </summary> void OnLayoutUpdated(object sender, EventArgs e) { if (!_HasBeenSelfPositioned) { // Try to align it along the right side of the main window. this.AlignToParent(true); _HasBeenSelfPositioned = true; } } #endregion #region Loaded event handler /// <summary> /// Handle the Loaded event by completing the initialization of this Window. /// </summary> void OnLoaded(object sender, RoutedEventArgs e) { try { // If TargetWindow hasn't been set yet, then default it to the owning Window. if (_targetWindow == null) { _targetWindow = Owner as IVirtualKeyboardInjectable; } //TODO: Also need to capture the keystrokes for the owning window, // as I'd like to re-map certain of those keypresses. // Initialize our arrays of Key Buttons.. _keyboardButtons = new Button[] { btnVK_OEM_3, btnVK_1, btnVK_2, btnVK_3, btnVK_4, btnVK_5, btnVK_6, btnVK_7, btnVK_8, btnVK_9, btnVK_0, btnVK_OEM_MINUS, btnVK_OEM_PLUS, btnVK_Q, btnVK_W, btnVK_E, btnVK_R, btnVK_T, btnVK_Y, btnVK_U, btnVK_I, btnVK_O, btnVK_P, btnVK_OEM_4, btnVK_OEM_6, btnVK_OEM_5, btnVK_A, btnVK_S, btnVK_D, btnVK_F, btnVK_G, btnVK_H, btnVK_J, btnVK_K, btnVK_L, btnVK_OEM_1, btnVK_OEM_7, btnVK_Z, btnVK_X, btnVK_C, btnVK_V, btnVK_B, btnVK_N, btnVK_M, btnVK_OEM_COMMA, btnVK_OEM_PERIOD, btnVK_OEM_2 }; // Could also do this in Xaml. int n = _keyboardButtons.Length; for (int i = 0; i < n; i++) { Button button = _keyboardButtons[i]; button.Command = KeyPressedCommand; KeyViewModel keyViewModel = _domain.TheKeyViewModels[i]; button.CommandParameter = keyViewModel; keyViewModel.Button = button; } btnTab.Command = KeyPressedCommand; btnTab.CommandParameter = _domain.TabKey; btnVK_SPACE.Command = KeyPressedCommand; btnVK_SPACE.CommandParameter = _domain.VK_SPACE; btnEnter.Command = KeyPressedCommand; btnEnter.CommandParameter = _domain.EnterKey; // Pre-select the language. //Console.WriteLine("in VirtualKeyboard.OnLoaded, _lastKeyboardLayoutWasSetTo is " + _lastKeyboardLayoutWasSetTo); WhichKeyboardLayout lastKeyboardLayout = WhichKeyboardLayout.English; if (_appSettings != null) { string sLastLanguage = _appSettings["VKLayout"] as String; //Console.WriteLine(" and _appSettings indicates " + sLastLanguage); Enum.TryParse(sLastLanguage, out lastKeyboardLayout); } _domain.SelectedKeyboardLayout = lastKeyboardLayout; SetKeyAssignmentsTo(lastKeyboardLayout); DataContext = _domain; } catch (Exception x) { Console.WriteLine("Exception in VirtualKeyboard.OnLoaded: " + x.Message); // Below, is what I normally use as my standard for raising objections within library routines (using my own std MessageBox substitute). //IApp app = Application.Current as IApp; //if (app != null) //{ // app.TheInterlocution.NotifyUserOfError("Well this is embarrassing!", "in VirtualKeyboard.OnLoaded", x, this); //} } } #endregion OnLoaded #region ContentRendered event handler /// <summary> /// Handle the ContentRendered event. I had to use this because the cbLanguage was getting a SelectionChanged event (to English) /// after the Loaded event. /// </summary> void OnContentRendered(object sender, EventArgs e) { _isSensitive = true; _isKeyboardLayoutChanged = false; } #endregion #region LocationChanged event handler /// <summary> /// Use the LocationChanged event to remember where I am relative to the Owner window. /// </summary> void OnLocationChanged(object sender, EventArgs e) { if (Owner != null && !_isIgnoringLocationChangedEvent) { _relativeXDisplacement = this.Left - Owner.Left; _relativeYDisplacement = this.Top - Owner.Top; } } #endregion #region cbKeyboardLayout_SelectionChanged /// <summary> /// Handle the event that ocurrs when the user selects something within the Language ComboBox. /// </summary> private void cbKeyboardLayout_SelectionChanged(object sender, SelectionChangedEventArgs e) { //Console.WriteLine("enter VirtualKeyboard.cbKeyboardLayout_SelectionChanged,"); if (_isSensitive) { if (e.AddedItems.Count > 0 && e.AddedItems[0] != null) { if (_domain.SelectedKeyboardLayout != WhichKeyboardLayout.Unknown) { SetKeyAssignmentsTo(_domain.SelectedKeyboardLayout); } _isKeyboardLayoutChanged = true; } } SetFocusOnTarget(); } #endregion #region btnCapsLock_Click /// <summary> /// Handle the event that happens when the user presses the CAPS-LOCK key. /// </summary> private void btnCapsLock_Click(object sender, RoutedEventArgs e) { ToggleCapsLock(); } #endregion #region btnShift_Click /// <summary> /// Handle the event that corresponds to the user pressing the "Shift" key. /// </summary> private void btnShift_Click(object sender, RoutedEventArgs e) { PutIntoShiftState(); } #endregion #region OnResetTimerTick /// <summary> /// Handle the reset-timer's "Tick" event. /// </summary> private void OnResetTimerTick(object sender, EventArgs args) { ClearTimer(); // Clear the Shift-Lock state. if (_domain.IsShiftLock) { _domain.IsShiftLock = false; } } #endregion #region btnBackspace_Click /// <summary> /// Handle the event that happens when the user presses the BackSpace key. /// </summary> private void btnBackspace_Click(object sender, RoutedEventArgs e) { DoBackspace(); } #endregion #region btnDismiss_Click /// <summary> /// Handler for when the user clicks on the Dismiss This button. /// </summary> private void btnDismiss_Click(object sender, RoutedEventArgs e) { Close(); } #endregion #region Closing event handler /// <summary> /// Handle the Closing event. /// </summary> void OnClosing(object sender, System.ComponentModel.CancelEventArgs e) { if (TargetWindow != null) { //TODO: This needs to be expanded to work with more than just the TextBox. TextBox txtTarget = TargetWindow.ControlToInjectInto as TextBox; if (txtTarget != null) { if (txtTarget.CommandBindings.Count > 0) { if (_backspaceCommandBinding != null) { txtTarget.CommandBindings.Remove(_backspaceCommandBinding); _backspaceCommandBinding = null; } } } } } #endregion #region Closed event handler /// <summary> /// Handle the Closed event /// </summary> void OnClosed(object sender, EventArgs e) { //Console.WriteLine("VirtualKeyboard.OnClosed, VirtualKeyboard.LastKeyboardLayoutWasSetTo = " + VirtualKeyboard.LastKeyboardLayoutWasSetTo.ToString()); ClearTimer(); // Remembered which language it was set to last time, so that we can pre-set it to that next time around. if (_appSettings != null) { if (_isKeyboardLayoutChanged) { //Console.WriteLine(" Writing that to setting VKLayout."); _appSettings["VKLayout"] = VirtualKeyboard.LastKeyboardLayoutWasSetTo.ToString(); _appSettings.Save(); } } // Set the static object to null so that we know that this has closed // and would need to be recreated the next time. VirtualKeyboard.s_TheVirtualKeyboard = null; } #endregion #region OnTheKeyboardClosed (static method) /// <summary> /// Handle the static Closed event of the virtual keyboard - for when I close it in order to re-open it for a different dialog-window /// (necessary for modal dialogs). /// </summary> private static void OnTheKeyboardClosed(object sender, EventArgs e) { s_TheVirtualKeyboard = ShowIt(s_desiredTargetWindow); } #endregion #endregion Event Handlers #region ExecuteKeyPressedCommand /// <summary> /// Handle the ExecuteKeyPressed command. /// </summary> /// <param name="sender">The Window that this originated from</param> /// <param name="args">The Parameter property of args is the Button that issued this command</param> private void ExecuteKeyPressedCommand(object sender, ExecutedRoutedEventArgs args) { KeyViewModel vm = args.Parameter as KeyViewModel; if (vm != null) { //Console.WriteLine("ExecuteKeyPressedCommand, btn.Name = " + btn.Name); Inject(vm); } else { String sWhat = args.Parameter as String; if (sWhat != null && sWhat.Length > 0) { Inject(sWhat); } } args.Handled = true; } #endregion #region Methods #region The /// <summary> /// Get the (only) VirtualKeyboard instance. /// </summary> public static VirtualKeyboard The { get { return s_TheVirtualKeyboard; } } #endregion #region ShowOrAttachTo - this is what you call from your own application. /// <summary> /// Either create and show, or else re-attach to (if it's already up) the virtual keyboard. /// To be called from the desired target-Window that wants to use it. /// </summary> /// <param name="targetWindow">The Window that wants to be the owner of the virtual-keyboard Window and the target of it's output</param> /// <param name="myPointerToIt">The targetWindow's own instance-variable that will point to the virtual keyboard object</param> public static void ShowOrAttachTo(IVirtualKeyboardInjectable targetWindow, ref VirtualKeyboard myPointerToIt) { try { s_desiredTargetWindow = targetWindow; // Evidently, for modal Windows I can't share user-focus with another Window unless I first close and then recreate it. // A shame. Seems like a waste of time. But I don't know of a work-around to it (yet). if (IsUp) { Console.WriteLine("VirtualKeyboard: re-attaching to a different Window."); VirtualKeyboard.The.Closed += new EventHandler(OnTheKeyboardClosed); VirtualKeyboard.The.Close(); myPointerToIt = null; } else { myPointerToIt = ShowIt(targetWindow); } } catch (Exception x) { Console.WriteLine("Exception in VirtualKeyboard.ShowOrAttachTo: " + x.Message); // Below, is what I normally use as my standard for raising objections within library routines (using my own std MessageBox substitute). //IInterlocution inter = Application.Current as IInterlocution; //if (inter != null) //{ // inter.NotifyUserOfError("Well, now this is embarrassing.", "in VirtualKeyboard.ShowOrAttachTo.", x); //} } } #endregion #region ShowIt - this is the factory method for this class /// <summary> /// Create (and return) the VirtualKeyboard, and Show it. /// This is the factory-method for instantiating and showing the virtual keyboard. /// </summary> /// <param name="parentWindow">The window that will accept input from the keyboard</param> /// <returns>a reference to the VirtualKeyboard</returns> private static VirtualKeyboard ShowIt(IVirtualKeyboardInjectable targetWindow) { if (s_TheVirtualKeyboard == null) { s_TheVirtualKeyboard = new VirtualKeyboard(); } s_TheVirtualKeyboard.Owner = (Window)targetWindow; s_TheVirtualKeyboard.ShowActivated = false; if (!s_TheVirtualKeyboard.IsLoaded) { s_TheVirtualKeyboard.Show(); } s_TheVirtualKeyboard.TargetWindow = targetWindow; targetWindow.ControlToInjectInto.Focus(); return s_TheVirtualKeyboard; } #endregion #region Movement as a group public void MoveAlongWith() { this.MoveAsAGroup(_relativeXDisplacement, _relativeYDisplacement, ref _isIgnoringLocationChangedEvent); } #endregion #region Inject /// <summary> /// Type the given string into whatever the target TextBox (or similar control) is. /// </summary> /// <param name="sWhat"></param> protected void Inject(string sWhat) { if (TargetWindow != null) { ((Window)TargetWindow).Focus(); TextBox txtTarget = TargetWindow.ControlToInjectInto as TextBox; if (txtTarget != null) { txtTarget.InsertText(sWhat); } else { RichTextBox richTextBox = TargetWindow.ControlToInjectInto as RichTextBox; if (richTextBox != null) { richTextBox.InsertText(sWhat); } else // let's hope it's an IInjectableControl { IInjectableControl targetControl = TargetWindow.ControlToInjectInto as IInjectableControl; if (targetControl != null) { targetControl.InsertText(sWhat); } } //else // if you have other text-entry controls such as a rich-text box, include them here. //{ // FsWpfControls.FsRichTextBox.FsRichTextBox txtrTarget = TargetWindow.ControlToInjectInto as FsWpfControls.FsRichTextBox.FsRichTextBox; // if (txtrTarget != null) // { // txtrTarget.InsertThis(sWhat); // } } } } /// <summary> /// Inject the character represented by the given "key" switch view-model, into whatever the target TextBox is. /// </summary> /// <param name="vm">The KeyViewModel that carries the information of what to inject</param> public void Inject(KeyViewModel vm) { // The Tag would've been another way to associate the view-model with the button. if (vm != null) { if (vm == _domain.EnterKey) { Inject(Environment.NewLine); } else { Inject(vm.CodePoint.ToString()); } } // If we were in Shift-mode, reset that now. _domain.IsShiftLock = false; ClearTimer(); } #endregion #region DoBackspace /// <summary> /// Simulate the press of the Backspace key. /// </summary> public void DoBackspace() { if (TargetWindow != null) { ((Window)TargetWindow).Focus(); TextBox txtTarget = TargetWindow.ControlToInjectInto as TextBox; if (txtTarget != null) { // Use the built-in Backspace command. Create it only once, and remove it when this VirtualKeyboard window is closing. if (_backspaceCommandBinding == null) { _backspaceCommandBinding = new CommandBinding(EditingCommands.Backspace); txtTarget.CommandBindings.Add(_backspaceCommandBinding); } _backspaceCommandBinding.Command.Execute(null); // This was how I did the Backspace operation without using the command.. //int iCaretIndex = txtTarget.CaretIndex; //if (iCaretIndex > 0) //{ // string sOriginalContent = txtTarget.Text; // int nLength = sOriginalContent.Length; // if (iCaretIndex >= nLength) // { // txtTarget.Text = sOriginalContent.Substring(0, nLength - 1); // } // else // { // string sPartBeforeCaret = sOriginalContent.Substring(0, iCaretIndex - 1); // string sPartAfterCaret = sOriginalContent.Substring(iCaretIndex, nLength - iCaretIndex); // txtTarget.Text = sPartBeforeCaret + sPartAfterCaret; // } // txtTarget.CaretIndex = iCaretIndex - 1; //} } else // let's hope it's an IInjectableControl { IInjectableControl targetControl = TargetWindow.ControlToInjectInto as IInjectableControl; if (targetControl != null) { targetControl.Backspace(); } //else //{ // FsWpfControls.FsRichTextBox.FsRichTextBox txtrTarget = TargetWindow.ControlToInjectInto as FsWpfControls.FsRichTextBox.FsRichTextBox; // if (txtrTarget != null) // { // txtrTarget.InsertThis(chWhat.ToString()); // } } } //textbox.Text = sOriginalContent.Insert(iCaretIndex, sStuffToInsert); } #endregion #region SetCulture /// <summary> /// Set the language of the GUI itself (as distinct from the setting of the keyboard layout). /// </summary> /// <param name="culture"></param> public void SetCulture(CultureInfo culture) { //TODO This is just tinkering at this point -- it all needs to be implemented. string sCode = culture.TwoLetterISOLanguageName; string sName = culture.Name; int iKeyboardLayoutId = culture.KeyboardLayoutId; //if (sCode == "de") // German //{ // btnEnter.Content = "?"; // btn //} if (sCode == "es") // Spanish { btnEnter.Content = "Intro"; btnVK_SPACE.Content = "Spacio"; } else if (sCode == "fr") // French { btnEnter.Content = "Entree"; btnVK_SPACE.Content = "Space"; btnCapsLock.Content = "Verr Maj"; } else if (sCode == "ru") // Russian { btnEnter.Content = "Enter"; btnVK_SPACE.Content = "Mecto"; } else // default to English { btnEnter.Content = "Enter"; btnVK_SPACE.Content = "Space"; } } #endregion #region SetKeyAssignmentsTo /// <summary> /// Set the key assignments to those of the give language. /// </summary> public void SetKeyAssignmentsTo(WhichKeyboardLayout whichKeyboardLayout) { //Console.WriteLine("in SetKeyAssignmentsTo(" + whichKeyboardLayout.ToString() + "), "); switch (whichKeyboardLayout) { case WhichKeyboardLayout.Russian: //TODO: Might we possibly need to assign different fonts within the same set? //string sFontFamilyForSha = "Aboriginal Serif"; //string sFontFamilyForSha = "Athena Unicode"; //btnVK_I.FontFamily = new FontFamily(sFontFamilyForSha); //btnVK_O.FontFamily = new FontFamily(sFontFamilyForSha); break; default: // default to English break; } AssignKeys(KeyAssignmentSet.For(whichKeyboardLayout)); } #endregion SetKeyAssignmentsTo #region AssignKeys /// <summary> /// Modify the actual keyboard keys (which are WPF Buttons) based upon the given KeyAssignmentSet /// (ie, change the actual appearance of the Buttons). /// </summary> /// <param name="keyAssignmentSet">What to assign to the keys</param> public void AssignKeys(KeyAssignmentSet keyAssignmentSet) { //Console.Write("AssignKeys(" + keyAssignmentSet.Language + "), "); int n = _domain.TheKeyViewModels.Length; Debug.Assert(n == keyAssignmentSet.KeyAssignments.Length, "These collections should be the same length."); for (int i = 0; i < n; i++) { KeyViewModel vm = _domain.TheKeyViewModels[i]; KeyAssignment ka = keyAssignmentSet.KeyAssignments[i]; vm.IsLetter = ka.IsLetter; vm.UnshiftedCodePoint = (char)ka.UnshiftedCodePoint; vm.ShiftedCodePoint = (char)ka.ShiftedCodePoint; if (ka.IsToShowBothGlyphs) { // If it's an underscore, if (ka.ShiftedCodePoint == 0x005F) { // double it up because the first one gets eaten string sText = (char)ka.ShiftedCodePoint + Environment.NewLine + (char)ka.UnshiftedCodePoint; string sTextWithUnderscoresDoubled = sText.Replace("_", @"__"); vm.Text = sTextWithUnderscoresDoubled; } else { vm.Text = (char)ka.ShiftedCodePoint + Environment.NewLine + (char)ka.UnshiftedCodePoint; } } else { vm.Text = null; } vm.UnshiftedToolTip = ka.UnshiftedToolTip; vm.ShiftedToolTip = ka.ShiftedToolTip; // Assign the font family, if one is specified.. if (!String.IsNullOrEmpty(ka.FontFamilyName)) { // Those assigned to this specific key-assignment, override those assigned to the key-assignment-set. vm.FontFamily = new FontFamily(ka.FontFamilyName); } else { vm.FontFamily = null; } } } #endregion #region ToggleCapsLock /// <summary> /// Flip the state of the CAPS-LOCK key-button. /// </summary> public void ToggleCapsLock() { if (_domain.IsCapsLock) { _domain.IsCapsLock = false; } else { _domain.IsCapsLock = true; } SetFocusOnTarget(); } #endregion #region PutIntoShiftState /// <summary> /// Flip the state of the SHIFT key-button into the shifted state. /// </summary> public void PutIntoShiftState() { // Toggle the shift-lock state. _domain.IsShiftLock = !_domain.IsShiftLock; // If we're turning Shiftlock on, give that a 10-second timeout before it resets by itself. if (_domain.IsShiftLock) { ClearTimer(); _resetTimer = new DispatcherTimer(TimeSpan.FromSeconds(10), DispatcherPriority.ApplicationIdle, new EventHandler(OnResetTimerTick), this.Dispatcher); } SetFocusOnTarget(); } #endregion #region SetFocusOnTarget /// <summary> /// Set focus to the TextBox (or whatever we're injecting keystrokes into) of the target window. /// </summary> public void SetFocusOnTarget() { if (TargetWindow != null) { ((Window)TargetWindow).Focus(); } } #endregion #region ClearTimer /// <summary> /// Reset the dispose of the timer that's used to reset the shift-lock state back to the unshifted state. /// </summary> private void ClearTimer() { // Dispose of the reset-timer, if that's still around. if (_resetTimer != null) { if (_resetTimer.IsEnabled) { _resetTimer.Stop(); } _resetTimer = null; } } #endregion #endregion Methods #region Properties #region TargetWindow /// <summary> /// Get/set which Window we're going to inject characters into. /// </summary> public IVirtualKeyboardInjectable TargetWindow { get { return _targetWindow; } set { _targetWindow = value; } } #endregion #region IsUp /// <summary> /// Get whether this virtual keyboard is currently showing, /// as indicated by whether the class-variable has been set to null. /// </summary> public static bool IsUp { get { return s_TheVirtualKeyboard != null; } } #endregion #region SelectedKeyboardLayout /// <summary> /// Get which language this virtual keyboard is currently set to. /// </summary> public WhichKeyboardLayout SelectedKeyboardLayout { get { return _domain.SelectedKeyboardLayout; } } #endregion #region LastKeyboardLayoutWasSetTo /// <summary> /// Get the most recent setting for this keyboard, so that it may be pre-selected next time. /// </summary> public static WhichKeyboardLayout LastKeyboardLayoutWasSetTo { get { return _lastKeyboardLayoutWasSetTo; } set { _lastKeyboardLayoutWasSetTo = value; } } #endregion #region SaveStateToMySettings /// <summary> /// Your Window should call this before showing the virtual keyboard, if you want it to remember which language it was set to. /// </summary> /// <param name="appSettings">Your application's Settings object</param> public static void SaveStateToMySettings(System.Configuration.ApplicationSettingsBase appSettings) { _appSettings = appSettings; } #endregion #endregion Properties #region The RoutedUICommands public static RoutedUICommand KeyPressedCommand = new RoutedUICommand(); #endregion The RoutedUICommands #region Fields private static VirtualKeyboard s_TheVirtualKeyboard; private static IVirtualKeyboardInjectable s_desiredTargetWindow; private IVirtualKeyboardInjectable _targetWindow; private KeyboardViewModel _domain; private Button[] _keyboardButtons; private double _relativeXDisplacement; private double _relativeYDisplacement; private bool _isIgnoringLocationChangedEvent; private DispatcherTimer _resetTimer; private static WhichKeyboardLayout _lastKeyboardLayoutWasSetTo; private static System.Configuration.ApplicationSettingsBase _appSettings; private bool _isSensitive; private bool _isKeyboardLayoutChanged; private CommandBinding _backspaceCommandBinding; #endregion Fields } #endregion class VirtualKeyboard }
By viewing downloads associated with this article you agree to the Terms of use and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.
This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)
Math Primers for Programmers