875 lines
31 KiB
C#
875 lines
31 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Drawing;
|
|
using System.Text;
|
|
using System.Windows.Forms;
|
|
using System.ComponentModel;
|
|
using System.Collections;
|
|
using System.Runtime.InteropServices;
|
|
using DevComponents.DotNetBar.Touch;
|
|
|
|
|
|
namespace DevComponents.DotNetBar.Keyboard
|
|
{
|
|
/// <summary>
|
|
/// Represents virtual keyboard control.
|
|
/// </summary>
|
|
[ToolboxItem(true)]
|
|
public class KeyboardControl : Control
|
|
{
|
|
private TouchHandler _TouchHandler = null;
|
|
/// <summary>
|
|
/// Creates a new VirtualKeyboard control.
|
|
/// </summary>
|
|
public KeyboardControl()
|
|
{
|
|
_ColorTable = _DefaultColorTable;
|
|
|
|
// Do not allow the control to receive focus.
|
|
SetStyle(ControlStyles.Selectable, false);
|
|
|
|
// Do not call OnPaintBackground().
|
|
SetStyle(ControlStyles.Opaque, true);
|
|
|
|
// Enable double buffering. OptimizedDoubleBuffer and AllPaintingInWmPaint are set by the DoubleBuffered property below.
|
|
//SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
|
|
//SetStyle(ControlStyles.AllPaintingInWmPaint, true);
|
|
|
|
DoubleBuffered = true;
|
|
|
|
ResizeRedraw = true;
|
|
|
|
_RepeatTimer = new Timer();
|
|
_RepeatTimer.Tick += new EventHandler(RepeatTimer_Tick);
|
|
}
|
|
|
|
// The model for the Keyboard. This object contains all the information about the keys
|
|
// and the possible layouts of the keyboard.
|
|
private Keyboard _Keyboard = null; //Keyboard.CreateDefaultKeyboard();
|
|
|
|
/// <summary>
|
|
/// Gets or sets the Keyboard object that describes the VirtualKeyboard.
|
|
/// </summary>
|
|
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
|
public Keyboard Keyboard
|
|
{
|
|
get
|
|
{
|
|
if(_Keyboard==null)
|
|
_Keyboard = Keyboard.CreateDefaultKeyboard();
|
|
return _Keyboard;
|
|
}
|
|
set { _Keyboard = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Default width for the keyboard in pixels.
|
|
/// </summary>
|
|
public static int DefaultWidth
|
|
{
|
|
get { return 740; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Default height for the keyboard in pixels.
|
|
/// </summary>
|
|
public static int DefaultHeight {
|
|
get { return 250; }
|
|
}
|
|
|
|
protected override Size DefaultSize
|
|
{
|
|
get
|
|
{
|
|
return Dpi.Size(new Size(DefaultWidth, DefaultHeight));
|
|
}
|
|
}
|
|
|
|
|
|
private VirtualKeyboardColorTable _DefaultColorTable = new VirtualKeyboardColorTable();
|
|
|
|
private VirtualKeyboardColorTable _ColorTable;
|
|
/// <summary>
|
|
/// Gets or set the ColorTable used to draw the keyboard.
|
|
/// </summary>
|
|
public VirtualKeyboardColorTable ColorTable
|
|
{
|
|
get { return _ColorTable; }
|
|
set
|
|
{
|
|
_ColorTable = value;
|
|
Invalidate(); // Ok to invalidate entire control, to render everything with the new colors.
|
|
}
|
|
}
|
|
|
|
|
|
private Renderer _Renderer = new FlatStyleRenderer();
|
|
/// <summary>
|
|
/// Gets or sets the Renderer used to render the keyboard.
|
|
/// </summary>
|
|
public Renderer Renderer
|
|
{
|
|
get { return _Renderer; }
|
|
set
|
|
{
|
|
if (value == null)
|
|
value = new FlatStyleRenderer();
|
|
_Renderer = value;
|
|
Invalidate(); // Ok to invalidate entire control, to redraw everything with the new renderer.
|
|
}
|
|
}
|
|
|
|
// Through this method, the keys generated by the Virtual Keyboard are sent to the target control.
|
|
// In this version, the current target control is always the control with input focus and the keyboard
|
|
// is only visible when the control (to which the keyboard was previously attached) has input focus.
|
|
// Therefore, SendKeys.Send offers all the logic needed for this version of the keyboard.
|
|
//
|
|
// For future versions, we might consider using the Win32 API SendInput function for greater flexibility.
|
|
private void SendKeysToTarget(string info)
|
|
{
|
|
KeyboardKeyCancelEventArgs cancelArgs = new KeyboardKeyCancelEventArgs(info);
|
|
OnSendingKey(cancelArgs);
|
|
if (cancelArgs.Cancel) return;
|
|
SendKeys.Send(info);
|
|
OnKeySent(new KeyboardKeyEventArgs(info));
|
|
}
|
|
|
|
|
|
#region Send repeating key if mouse is hold down
|
|
|
|
private Timer _RepeatTimer; // Used to send repeated keys to the target control.
|
|
private Key _PressedKey; // Used to keep the keys that is repeatedly sent to the target control.
|
|
|
|
private void RepeatTimer_Tick(object sender, EventArgs e)
|
|
{
|
|
if (_PressedKey != null)
|
|
{
|
|
if (_RepeatTimer.Interval == KeyboardDelay)
|
|
_RepeatTimer.Interval = KeyboardSpeed;
|
|
SendKeysToTarget(_PressedKey.Info);
|
|
}
|
|
}
|
|
|
|
|
|
private void CancelRepeatKey()
|
|
{
|
|
_PressedKey = null;
|
|
_RepeatTimer.Stop();
|
|
}
|
|
|
|
|
|
private void StartRepeatKey(Key key)
|
|
{
|
|
_PressedKey = key;
|
|
_RepeatTimer.Start();
|
|
_RepeatTimer.Interval = KeyboardDelay;
|
|
}
|
|
|
|
|
|
private static int KeyboardDelay
|
|
{
|
|
get
|
|
{
|
|
// SystemInformation.KeyboardDelay is between 0 (2.5 repetitions per second) and 31 (about 30 per second)
|
|
// but these values arre hardware dependendant, so the fomula below is a fast and accurate enough not to feel
|
|
// any difference between this and a real keyboard at the same values.
|
|
return 1000 / (SystemInformation.KeyboardDelay + 1);
|
|
}
|
|
}
|
|
|
|
|
|
private static int KeyboardSpeed
|
|
{
|
|
get
|
|
{
|
|
// See KeyboardDelay for details.
|
|
return 1000 / (SystemInformation.KeyboardSpeed + 1);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
private bool _IsTopBarVisible = true;
|
|
/// <summary>
|
|
/// Gets or sets a value indicating if the top bar of the keyboard is visible.
|
|
/// </summary>
|
|
[DefaultValue(true)]
|
|
[Description("Indicates if the top bar of the keyboard is visible.")]
|
|
[Category("Appearance")]
|
|
public bool IsTopBarVisible
|
|
{
|
|
get { return _IsTopBarVisible; }
|
|
set
|
|
{
|
|
_IsTopBarVisible = value;
|
|
this.Invalidate();
|
|
}
|
|
}
|
|
|
|
private int _CaptionHeight = 20;
|
|
/// <summary>
|
|
/// Indicates the height of the keyboard caption which hosts the close button.
|
|
/// </summary>
|
|
[DefaultValue(20), Category("Appearance"), Description("Indicates the height of the keyboard caption which hosts the close button.")]
|
|
public int CaptionHeight
|
|
{
|
|
get { return _CaptionHeight; }
|
|
set
|
|
{
|
|
if (value != _CaptionHeight)
|
|
{
|
|
int oldValue = _CaptionHeight;
|
|
_CaptionHeight = value;
|
|
this.Invalidate();
|
|
}
|
|
}
|
|
}
|
|
|
|
//private const int TopBarHeight = 20;
|
|
|
|
private int TopBarVisibleHeight
|
|
{
|
|
get { return IsTopBarVisible ? ActualCaptionHeight + Border : 0; }
|
|
}
|
|
|
|
public int Border {
|
|
get { return Dpi.Width10; }
|
|
}
|
|
|
|
private readonly Key HideKeyboardKey = new Key("Hide", info: null);
|
|
|
|
private int ActualCaptionHeight
|
|
{
|
|
get { return Dpi.Height(_CaptionHeight); }
|
|
}
|
|
private Rectangle TopBarRectangle
|
|
{
|
|
get { return new Rectangle(Border, Border, Width - 2 * Border, ActualCaptionHeight); }
|
|
}
|
|
|
|
private Rectangle CloseButtonRectangle
|
|
{
|
|
get { return new Rectangle(Width - ActualCaptionHeight - Border, Border, ActualCaptionHeight, ActualCaptionHeight); }
|
|
}
|
|
|
|
private Rectangle KeysRectangle
|
|
{
|
|
get
|
|
{
|
|
Rectangle rect = new Rectangle(Border, Border, Width - 2 * Border, Height - 2 * Border);
|
|
if (IsTopBarVisible)
|
|
{
|
|
rect.Y += ActualCaptionHeight + Border;
|
|
rect.Height -= ActualCaptionHeight + Border;
|
|
}
|
|
return rect;
|
|
}
|
|
}
|
|
|
|
protected override void ScaleControl(SizeF factor, BoundsSpecified specified)
|
|
{
|
|
Dpi.SetScaling(factor);
|
|
base.ScaleControl(factor, specified);
|
|
}
|
|
|
|
protected override void OnPaint(PaintEventArgs e)
|
|
{
|
|
KeyboardLayout currentLayout = Keyboard.CurrentLayout;
|
|
|
|
if (currentLayout.LogicalWidth <= 0 || currentLayout.LogicalHeight <= 0)
|
|
return;
|
|
|
|
Rectangle rectKeys = KeysRectangle;
|
|
|
|
float sfx = (float)rectKeys.Width / currentLayout.LogicalWidth;
|
|
float sfy = (float)rectKeys.Height / currentLayout.LogicalHeight;
|
|
|
|
Renderer.ColorTable = ColorTable;
|
|
|
|
Renderer.BeginFrame(new BeginFrameRendererEventArgs(e.Graphics, e.ClipRectangle, rectKeys, Font, currentLayout));
|
|
|
|
Renderer.DrawBackground(new BackgroundRendererEventArgs(e.Graphics, e.ClipRectangle, new Rectangle(0, 0, Width, Height)));
|
|
|
|
if (IsTopBarVisible)
|
|
{
|
|
Renderer.DrawTopBar(new TopBarRendererEventArgs(e.Graphics, e.ClipRectangle, TopBarRectangle, Text, Font));
|
|
Renderer.DrawCloseButton(new CloseButtonRendererEventArgs(e.Graphics, _PressedKey == HideKeyboardKey, e.ClipRectangle, CloseButtonRectangle));
|
|
}
|
|
|
|
foreach (Key key in currentLayout.Keys)
|
|
{
|
|
Rectangle rectKey = new Rectangle((int)(key.Bounds.X * sfx), (int)(key.Bounds.Y * sfy),
|
|
(int)(key.Bounds.Width * sfx), (int)(key.Bounds.Height * sfy));
|
|
|
|
rectKey.Offset(rectKeys.Left, rectKeys.Top);
|
|
|
|
Renderer.DrawKey(new KeyRendererEventArgs(e.Graphics, key, key == _PressedKey, e.ClipRectangle, rectKey));
|
|
}
|
|
|
|
Renderer.EndFrame();
|
|
|
|
#if TRIAL
|
|
using (Font font = new System.Drawing.Font(this.Font.FontFamily, 7, FontStyle.Regular))
|
|
{
|
|
using (SolidBrush brush = new SolidBrush(Color.FromArgb(24, Color.White)))
|
|
e.Graphics.DrawString("DotNetBar Trial Version", font, brush, (this.ClientRectangle.Width - 80) / 2, this.ClientRectangle.Height - 32);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
private void PerformMoveAction(Point location)
|
|
{
|
|
if (IsTopBarVisible && CloseButtonRectangle.Contains(location))
|
|
{
|
|
Cursor = Cursors.Hand;
|
|
}
|
|
else
|
|
{
|
|
KeyboardLayout currentLayout = Keyboard.CurrentLayout;
|
|
Rectangle rectKeys = KeysRectangle;
|
|
|
|
float fx = (float)rectKeys.Width / currentLayout.LogicalWidth;
|
|
float fy = (float)rectKeys.Height / currentLayout.LogicalHeight;
|
|
|
|
if (_PressedKey == HideKeyboardKey)
|
|
{
|
|
if (_PressedKey != null)
|
|
{
|
|
Invalidate(GetKeyPhysicalBounds(_PressedKey, fx, fy));
|
|
}
|
|
_PressedKey = null;
|
|
Invalidate(CloseButtonRectangle);
|
|
this.Update();
|
|
}
|
|
|
|
Key key = null;
|
|
|
|
if (KeysRectangle.Contains(location))
|
|
key = currentLayout.KeyHitTest((int)((location.X - rectKeys.Left) / fx), (int)((location.Y - rectKeys.Top) / fy));
|
|
|
|
// If the mouse is over a key, change the cursor to the Hand cursor, to provide
|
|
// a visual feedback for the user to know that it can interact with this key.
|
|
Key pressedKey = _PressedKey;
|
|
if (key == null)
|
|
{
|
|
Cursor = Cursors.Arrow;
|
|
CancelRepeatKey();
|
|
|
|
if (pressedKey != null)
|
|
{
|
|
Invalidate(GetKeyPhysicalBounds(pressedKey, fx, fy)); // Only invalidate area under the currently pressed key.
|
|
this.Update();
|
|
}
|
|
}
|
|
else if (key != pressedKey)
|
|
{
|
|
Cursor = Cursors.Hand;
|
|
CancelRepeatKey();
|
|
if (pressedKey != null)
|
|
{
|
|
Invalidate(GetKeyPhysicalBounds(pressedKey, fx, fy)); // Only invalidate area under the currently pressed key.
|
|
this.Update();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Cursor = Cursors.Hand;
|
|
}
|
|
}
|
|
}
|
|
protected override void OnMouseMove(MouseEventArgs e)
|
|
{
|
|
if (ProcessMouseAction)
|
|
{
|
|
PerformMoveAction(e.Location);
|
|
_LastTouchAction = null;
|
|
}
|
|
base.OnMouseMove(e);
|
|
}
|
|
|
|
|
|
protected override void OnMouseLeave(EventArgs e)
|
|
{
|
|
KeyboardLayout currentLayout = Keyboard.CurrentLayout;
|
|
Rectangle rectKeys = KeysRectangle;
|
|
|
|
float fx = (float)rectKeys.Width / currentLayout.LogicalWidth;
|
|
float fy = (float)rectKeys.Height / currentLayout.LogicalHeight;
|
|
|
|
if (_PressedKey != HideKeyboardKey)
|
|
Invalidate(CloseButtonRectangle);
|
|
else if (_PressedKey != null)
|
|
{
|
|
Invalidate(GetKeyPhysicalBounds(_PressedKey, fx, fy)); // Only invalidate area under the currently pressed key.
|
|
this.Update();
|
|
}
|
|
|
|
CancelRepeatKey();
|
|
}
|
|
|
|
private bool _TouchEnabled = true;
|
|
/// <summary>
|
|
/// Indicates whether touch support is enabled when provided by hardware.
|
|
/// </summary>
|
|
[DefaultValue(true), Category("Behavior"), Description("Indicates whether touch support is enabled when provided by hardware.")]
|
|
public bool TouchEnabled
|
|
{
|
|
get { return _TouchEnabled; }
|
|
set
|
|
{
|
|
if (value != _TouchEnabled)
|
|
{
|
|
bool oldValue = _TouchEnabled;
|
|
_TouchEnabled = value;
|
|
OnTouchEnabledChanged(oldValue, value);
|
|
}
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// Called when TouchEnabled property has changed.
|
|
/// </summary>
|
|
/// <param name="oldValue">Old property value</param>
|
|
/// <param name="newValue">New property value</param>
|
|
protected virtual void OnTouchEnabledChanged(bool oldValue, bool newValue)
|
|
{
|
|
//OnPropertyChanged(new PropertyChangedEventArgs("TouchEnabled"));
|
|
}
|
|
|
|
protected override void OnHandleCreated(EventArgs e)
|
|
{
|
|
if (TouchHandler.IsTouchEnabled)
|
|
{
|
|
_TouchHandler = new TouchHandler(this, eTouchHandlerType.Touch);
|
|
_TouchHandler.TouchDown += new EventHandler<TouchEventArgs>(TouchHandlerTouchDown);
|
|
_TouchHandler.TouchUp += new EventHandler<TouchEventArgs>(TouchHandlerTouchUp);
|
|
// Don't need touch move action for keyboard handling
|
|
//_TouchHandler.TouchMove += new EventHandler<TouchEventArgs>(TouchHandlerTouchMove);
|
|
}
|
|
base.OnHandleCreated(e);
|
|
}
|
|
|
|
private DateTime? _LastTouchAction = null;
|
|
//void TouchHandlerTouchMove(object sender, TouchEventArgs e)
|
|
//{
|
|
// PerformMoveAction(e.Location);
|
|
// _LastTouchAction = DateTime.Now;
|
|
//}
|
|
void TouchHandlerTouchUp(object sender, TouchEventArgs e)
|
|
{
|
|
if (_TouchEnabled)
|
|
{
|
|
// Process keybaord hiding through the MouseUp event instead of touch event since if closed using touch the mouse up will occur on control
|
|
// which is under keyboard after keyboard is hidden which is not desired
|
|
if (IsTopBarVisible && CloseButtonRectangle.Contains(e.Location))
|
|
{
|
|
_LastTouchAction = null;
|
|
return;
|
|
}
|
|
PerformUpAction(e.Location);
|
|
_LastTouchAction = DateTime.Now;
|
|
}
|
|
}
|
|
void TouchHandlerTouchDown(object sender, TouchEventArgs e)
|
|
{
|
|
if (_TouchEnabled)
|
|
{
|
|
PerformDownAction(e.Location);
|
|
_LastTouchAction = DateTime.Now;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the Key at given location. Location expected is in control coordinates.
|
|
/// </summary>
|
|
/// <param name="location">Location is control coordinates.</param>
|
|
/// <returns>Key if there is a key at given location or null/nothing if no key exists at given location.</returns>
|
|
public Key HitTest(Point location)
|
|
{
|
|
KeyboardLayout currentLayout = Keyboard.CurrentLayout;
|
|
Rectangle rectKeys = KeysRectangle;
|
|
|
|
float fx = (float)rectKeys.Width / currentLayout.LogicalWidth;
|
|
float fy = (float)rectKeys.Height / currentLayout.LogicalHeight;
|
|
|
|
Key key = null;
|
|
if (KeysRectangle.Contains(location))
|
|
key = currentLayout.KeyHitTest((int)((location.X - rectKeys.Left) / fx), (int)((location.Y - rectKeys.Top) / fy));
|
|
|
|
return key;
|
|
}
|
|
|
|
private void PerformDownAction(Point location)
|
|
{
|
|
if (_PressedKey != null) // This can happen because multi-touch can send multiple down messages
|
|
{
|
|
PerformUpAction(location);
|
|
}
|
|
|
|
if (IsTopBarVisible && CloseButtonRectangle.Contains(location))
|
|
{
|
|
_PressedKey = HideKeyboardKey;
|
|
Invalidate(CloseButtonRectangle);
|
|
}
|
|
else
|
|
{
|
|
KeyboardLayout currentLayout = Keyboard.CurrentLayout;
|
|
Rectangle rectKeys = KeysRectangle;
|
|
|
|
float fx = (float)rectKeys.Width / currentLayout.LogicalWidth;
|
|
float fy = (float)rectKeys.Height / currentLayout.LogicalHeight;
|
|
|
|
Key key = HitTest(location);
|
|
|
|
if (key != null)
|
|
{
|
|
TimeSpan ts = DateTime.Now.Subtract(_LastMouseUpEvent);
|
|
if (_LastKeyUp != null && key.Caption == _LastKeyUp.Caption && ts < TimeSpan.FromMilliseconds(SystemInformation.DoubleClickTime) && key.ChangeToLayoutEx >= 0)
|
|
{
|
|
Keyboard.CurrentLayoutIndex = key.ChangeToLayoutEx;
|
|
_LastMouseUpEvent = DateTime.MinValue;
|
|
Invalidate();
|
|
}
|
|
else
|
|
{
|
|
_PressedKey = key;
|
|
|
|
if (_PressedKey.Info != null && _PressedKey.Info != "")
|
|
{
|
|
StartRepeatKey(key);
|
|
SendKeysToTarget(key.Info);
|
|
}
|
|
}
|
|
Invalidate(GetKeyPhysicalBounds(key, fx, fy)); // Only invalidate area under the currently pressed key.
|
|
}
|
|
}
|
|
this.Update();
|
|
}
|
|
|
|
private int TouchMouseInputFilterDelay = 2000;
|
|
private bool ProcessMouseAction
|
|
{
|
|
get
|
|
{
|
|
return _LastTouchAction == null ||
|
|
(_LastTouchAction != null && DateTime.Now.Subtract(_LastTouchAction.Value).TotalMilliseconds > TouchMouseInputFilterDelay);
|
|
}
|
|
}
|
|
protected override void OnMouseDown(MouseEventArgs e)
|
|
{
|
|
if (ProcessMouseAction)
|
|
{
|
|
PerformDownAction(e.Location);
|
|
_LastTouchAction = null;
|
|
}
|
|
base.OnMouseDown(e);
|
|
}
|
|
|
|
private DateTime _LastMouseUpEvent = DateTime.MinValue;
|
|
private Key _LastKeyUp = null;
|
|
|
|
private void PerformUpAction(Point location)
|
|
{
|
|
if (IsTopBarVisible && CloseButtonRectangle.Contains(location))
|
|
{
|
|
if (_PressedKey == HideKeyboardKey)
|
|
{
|
|
HideKeyboard();
|
|
_PressedKey = null;
|
|
}
|
|
CancelRepeatKey();
|
|
}
|
|
else
|
|
{
|
|
Key pressedKey = _PressedKey;
|
|
CancelRepeatKey();
|
|
if (pressedKey != null)
|
|
{
|
|
KeyboardLayout currentLayout = Keyboard.CurrentLayout;
|
|
Rectangle rectKeys = KeysRectangle;
|
|
|
|
float fx = (float)rectKeys.Width / currentLayout.LogicalWidth;
|
|
float fy = (float)rectKeys.Height / currentLayout.LogicalHeight;
|
|
|
|
_LastMouseUpEvent = DateTime.Now;
|
|
_LastKeyUp = pressedKey;
|
|
|
|
if (pressedKey.ChangeToLayout >= 0)
|
|
{
|
|
Keyboard.CurrentLayoutIndex = pressedKey.ChangeToLayout;
|
|
Invalidate(); // Ok to invalidate entire control because we need to draw a whole new layout.
|
|
}
|
|
else if (pressedKey.ChangeToLayout == KeyboardLayout.PreviousLayout)
|
|
{
|
|
Keyboard.ChangeToPreviousLayout();
|
|
Invalidate(); // Ok to invalidate entire control because we need to draw a whole new layout.
|
|
}
|
|
else
|
|
{
|
|
Invalidate(GetKeyPhysicalBounds(pressedKey, fx, fy)); // Only invalidate area under the currently pressed key.
|
|
}
|
|
}
|
|
}
|
|
this.Update();
|
|
}
|
|
protected override void OnMouseUp(MouseEventArgs e)
|
|
{
|
|
if (ProcessMouseAction)
|
|
{
|
|
PerformUpAction(e.Location);
|
|
base.OnMouseUp(e);
|
|
}
|
|
_LastTouchAction = null;
|
|
}
|
|
|
|
private Rectangle GetKeyPhysicalBounds(Key key, float scaleFactorX, float scaleFactorY)
|
|
{
|
|
Rectangle r = key.Bounds;
|
|
|
|
r.X = (int)(r.X * scaleFactorX) + Border;
|
|
r.Y = (int)(r.Y * scaleFactorY) + Border + TopBarVisibleHeight;
|
|
r.Width = (int)(r.Width * scaleFactorX);
|
|
r.Height = (int)(r.Height * scaleFactorY);
|
|
|
|
return r;
|
|
}
|
|
|
|
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
if (disposing)
|
|
{
|
|
if (_DefaultColorTable != null)
|
|
_DefaultColorTable.Dispose();
|
|
if (_RepeatTimer != null)
|
|
_RepeatTimer.Dispose();
|
|
}
|
|
|
|
base.Dispose(disposing);
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Attaches the Keyboard to the specified control. The keyboard will automatically appear when the control receives input focus.
|
|
/// </summary>
|
|
/// <param name="control">The control to which the Keyboard will be attached.</param>
|
|
public void AttachTo(Control control)
|
|
{
|
|
control.GotFocus += new EventHandler(control_GotFocus);
|
|
control.LostFocus += new EventHandler(control_LostFocus);
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Detaches the Keyboard from the specified control.
|
|
/// </summary>
|
|
/// <param name="control">The control from which the Keyboard will be detached.</param>
|
|
public void DetachFrom(Control control)
|
|
{
|
|
control.GotFocus -= new EventHandler(control_GotFocus);
|
|
control.LostFocus -= new EventHandler(control_LostFocus);
|
|
}
|
|
|
|
public void UnlockCapsLock()
|
|
{
|
|
if (Control.IsKeyLocked(Keys.CapsLock))
|
|
{
|
|
OSVERSIONINFO info=new OSVERSIONINFO();
|
|
info.dwOSVersionInfoSize= System.Runtime.InteropServices.Marshal.SizeOf(typeof(OSVERSIONINFO));
|
|
GetVersionEx(ref info);
|
|
byte[] keys= new byte[255];
|
|
if(info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) // Win95/98
|
|
{
|
|
keys[VK_CAPITAL] = 1;
|
|
SetKeyboardState(keys);
|
|
}
|
|
else if (info.dwPlatformId == VER_PLATFORM_WIN32_NT)
|
|
{
|
|
// Simulate Key Press
|
|
keybd_event(VK_CAPITAL, 0X45, KEYEVENTF_EXTENDEDKEY | 0, 0);
|
|
// Simulate Key Release
|
|
keybd_event(VK_CAPITAL, 0x45, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void control_GotFocus(object sender, EventArgs e)
|
|
{
|
|
Show();
|
|
}
|
|
|
|
|
|
void control_LostFocus(object sender, EventArgs e)
|
|
{
|
|
HideKeyboard();
|
|
}
|
|
|
|
private void HideKeyboard()
|
|
{
|
|
CancelEventArgs ce = new CancelEventArgs();
|
|
OnKeyboardClosing(ce);
|
|
if (ce.Cancel) return;
|
|
|
|
this.Hide();
|
|
|
|
OnKeyboardClosed(EventArgs.Empty);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Occurs before the keyboard is closed and allows canceling of keyboard closing.
|
|
/// </summary>
|
|
[Description("Occurs before the keyboard is closed and allows canceling of keyboard closing.")]
|
|
public event CancelEventHandler KeyboardClosing;
|
|
/// <summary>
|
|
/// Raises KeyboardClosing event.
|
|
/// </summary>
|
|
/// <param name="e">Provides event arguments.</param>
|
|
protected virtual void OnKeyboardClosing(CancelEventArgs e)
|
|
{
|
|
CancelEventHandler handler = KeyboardClosing;
|
|
if (handler != null)
|
|
handler(this, e);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Occurs after keyboard is closed.
|
|
/// </summary>
|
|
[Description("Occurs after keyboard is closed.")]
|
|
public event EventHandler KeyboardClosed;
|
|
/// <summary>
|
|
/// Raises KeyboardClosed event.
|
|
/// </summary>
|
|
/// <param name="e">Provides event arguments.</param>
|
|
protected virtual void OnKeyboardClosed(EventArgs e)
|
|
{
|
|
EventHandler handler = KeyboardClosed;
|
|
if (handler != null)
|
|
handler(this, e);
|
|
}
|
|
/// <summary>
|
|
/// Occurs before the key pressed on keyboard is sent to target control and allows cancellation of the message
|
|
/// </summary>
|
|
[Description("Occurs before the key pressed on keyboard is sent to target control and allows cancellation of the message.")]
|
|
public event KeyCancelEventHandler SendingKey;
|
|
/// <summary>
|
|
/// Raises SendingKey event.
|
|
/// </summary>
|
|
/// <param name="e">Provides event arguments.</param>
|
|
protected virtual void OnSendingKey(KeyboardKeyCancelEventArgs e)
|
|
{
|
|
KeyCancelEventHandler handler = SendingKey;
|
|
if (handler != null)
|
|
handler(this, e);
|
|
}
|
|
/// <summary>
|
|
/// Occurs after the key is sent to target control.
|
|
/// </summary>
|
|
[Description("Occurs after the key is sent to target control.")]
|
|
public event KeyEventHandler KeySent;
|
|
/// <summary>
|
|
/// Raises KeySent event.
|
|
/// </summary>
|
|
/// <param name="e">Provides event arguments.</param>
|
|
protected virtual void OnKeySent(KeyboardKeyEventArgs e)
|
|
{
|
|
KeyEventHandler handler = KeySent;
|
|
if (handler != null)
|
|
handler(this, e);
|
|
}
|
|
|
|
protected override void OnTextChanged(EventArgs e)
|
|
{
|
|
this.Invalidate();
|
|
base.OnTextChanged(e);
|
|
}
|
|
|
|
#region Windows API
|
|
// Declare Type for API call:
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
internal struct OSVERSIONINFO
|
|
{
|
|
public int dwOSVersionInfoSize;
|
|
public int dwMajorVersion;
|
|
public int dwMinorVersion;
|
|
public int dwBuildNumber;
|
|
public int dwPlatformId;
|
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
|
|
public string szCSDVersion;
|
|
}
|
|
// Constant declarations:
|
|
const int VK_CAPITAL = 0x14;
|
|
const int KEYEVENTF_EXTENDEDKEY = 1;
|
|
const int KEYEVENTF_KEYUP = 2;
|
|
const int VER_PLATFORM_WIN32_NT = 2;
|
|
const int VER_PLATFORM_WIN32_WINDOWS = 1;
|
|
|
|
|
|
// API declarations:
|
|
[DllImport("kernel32.Dll")]
|
|
private static extern short GetVersionEx(ref OSVERSIONINFO o);
|
|
|
|
[DllImport("user32.dll")]
|
|
static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo);
|
|
|
|
[DllImport("user32.dll")]
|
|
static extern bool SetKeyboardState(byte[] lpKeyState);
|
|
#endregion
|
|
}
|
|
|
|
/// <summary>
|
|
/// Defines delegate for key-related events.
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
public delegate void KeyEventHandler(object sender, KeyboardKeyEventArgs e);
|
|
/// <summary>
|
|
/// Provides data for key related events.
|
|
/// </summary>
|
|
public class KeyboardKeyEventArgs : EventArgs
|
|
{
|
|
/// <summary>
|
|
/// Gets the key being pressed. The format of this information
|
|
/// must confirm to the description for the SendKeys.Send function. For more details, see:
|
|
/// http://msdn.microsoft.com/en-us/library/system.windows.forms.sendkeys.aspx
|
|
/// </summary>
|
|
public readonly string Key;
|
|
/// <summary>
|
|
/// Initializes a new instance of the KeyboardKeyEventArgs class.
|
|
/// </summary>
|
|
/// <param name="key"></param>
|
|
public KeyboardKeyEventArgs(string key)
|
|
{
|
|
Key = key;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Defines delegate for key-related events.
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
public delegate void KeyCancelEventHandler(object sender, KeyboardKeyCancelEventArgs e);
|
|
/// <summary>
|
|
/// Provides data for key related events.
|
|
/// </summary>
|
|
public class KeyboardKeyCancelEventArgs : CancelEventArgs
|
|
{
|
|
/// <summary>
|
|
/// Gets the key being pressed. The format of this information
|
|
/// must confirm to the description for the SendKeys.Send function. For more details, see:
|
|
/// http://msdn.microsoft.com/en-us/library/system.windows.forms.sendkeys.aspx
|
|
/// </summary>
|
|
public readonly string Key;
|
|
/// <summary>
|
|
/// Initializes a new instance of the KeyCancelEventArgs class.
|
|
/// </summary>
|
|
/// <param name="key"></param>
|
|
public KeyboardKeyCancelEventArgs(string key)
|
|
{
|
|
Key = key;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
}
|