I need a built-in on screen numeric keypad in my Application. For various reasons I cannot use the TMS Software or other commercial component offerings. I\'m very happy with
My final solution is as follows. This creates a numeric pad with a border and - yes- it does activate if the border is clicked or resized, but clicking the buttons does not steal focus from the target form / control. Simply using CreateParams did not work for me - it seemed to need the WMMouseActivate message instead.
I've combined it with a routine that I found that posts the key to the OS, not just the focused control. Note that code below assumes some simple support from an ancestor form for setting a default size and position. Thanks for all your help.
unit UArtScreenKeyboardForm;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
UArtBaseForm, Buttons,
StdCtrls;
type
TArtScreenKeyboardForm = class(TArtBaseForm)
procedure FormShow(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormCreate(Sender: TObject);
procedure WMMouseActivate(var Message: TWMMouseActivate); message WM_MOUSEACTIVATE;
procedure FormResize(Sender: TObject);
private
{ Private declarations }
procedure DoOnbuttonClick(ASender: TObject);
procedure DrawButtons;
protected
procedure SetDefaultSizeAndPosition; override;
public
{ Public declarations }
end;
procedure ArtScreenKeyboardForm_Show;
procedure ArtScreenKeyboardForm_Hide;
implementation
{$R *.DFM}
uses
UArtLibrary;
type
TButtonKind = (
bk0,
bk1,
bk2,
bk3,
bk4,
bk5,
bk6,
bk7,
bk8,
bk9,
bkPlus,
bkMinus,
bkDel,
bkDiv,
bkMul,
bkEquals,
bkDecPt,
bkEnter );
const
ButtonCaptions : array[TButtonKind] of string = (
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'+',
'-',
'Back',
'/',
'*',
'=',
'.',
'Enter' );
ScanCodes : array[TButtonKind] of cardinal = (
Ord( '0' ),
Ord( '1' ),
Ord( '2' ),
Ord( '3' ),
Ord( '4' ),
Ord( '5' ),
Ord( '6' ),
Ord( '7' ),
Ord( '8' ),
Ord( '9' ),
VK_ADD,
VK_SUBTRACT,
8, {BACKSPACE}
VK_DIVIDE,
VK_MULTIPLY,
Ord( '=' ),
Ord( '.' ),
VK_RETURN );
var
ArtScreenKeyboardForm: TArtScreenKeyboardForm = nil;
procedure PostKeyEx32(key: Word; const shift: TShiftState; specialkey: Boolean) ;
{
Parameters :
* key : virtual keycode of the key to send. For printable keys this is simply the ANSI code (Ord(character)) .
* shift : state of the modifier keys. This is a set, so you can set several of these keys (shift, control, alt, mouse buttons) in tandem. The TShiftState type is declared in the Classes Unit.
* specialkey: normally this should be False. Set it to True to specify a key on the numeric keypad, for example.
Description:
Uses keybd_event to manufacture a series of key events matching the passed parameters. The events go to the control with focus. Note that for characters key is always the upper-case version of the character. Sending without any modifier keys will result in a lower-case character, sending it with [ ssShift ] will result in an upper-case character!
}
type
TShiftKeyInfo = record
shift: Byte ;
vkey: Byte ;
end;
ByteSet = set of 0..7 ;
const
shiftkeys: array [1..3] of TShiftKeyInfo =
((shift: Ord(ssCtrl) ; vkey: VK_CONTROL),
(shift: Ord(ssShift) ; vkey: VK_SHIFT),
(shift: Ord(ssAlt) ; vkey: VK_MENU)) ;
var
flag: DWORD;
bShift: ByteSet absolute shift;
j: Integer;
begin
for j := 1 to 3 do
begin
if shiftkeys[j].shift in bShift then
keybd_event(shiftkeys[j].vkey, MapVirtualKey(shiftkeys[j].vkey, 0), 0, 0) ;
end;
if specialkey then
flag := KEYEVENTF_EXTENDEDKEY
else
flag := 0;
keybd_event(key, MapvirtualKey(key, 0), flag, 0) ;
flag := flag or KEYEVENTF_KEYUP;
keybd_event(key, MapvirtualKey(key, 0), flag, 0) ;
for j := 3 downto 1 do
begin
if shiftkeys[j].shift in bShift then
keybd_event(shiftkeys[j].vkey, MapVirtualKey(shiftkeys[j].vkey, 0), KEYEVENTF_KEYUP, 0) ;
end;
end;
procedure TArtScreenKeyboardForm.DoOnbuttonClick(ASender: TObject);
var
Btn : TSpeedButton;
Kind : TButtonKind;
begin
Btn := ASender as TSpeedButton;
Kind := TButtonKind(StrToIntDef( Copy( Btn.Name, 4, MaxStrLen ), 0 ));
PostKeyEx32( ScanCodes[Kind], [], False );
// As suggested also:
//PostMessage(GetFocus, WM_KEYDOWN, Ord('A'), 0 );
// PostMessage(GetFocus, WM_KEYDOWN, VK_NUMPAD1, MakeLong(0, MapVirtualKey(VK_NUMPAD1, 0)));
end;
procedure TArtScreenKeyboardForm.WMMouseActivate(var Message: TWMMouseActivate);
begin
Message.Result := MA_NOACTIVATE;
end;
procedure ArtScreenKeyboardForm_Show;
begin
If ArtScreenKeyboardForm = nil then
begin
ArtScreenKeyboardForm := TArtScreenKeyboardForm.Create( Application );
ArtScreenKeyboardForm.Show;
end;
Application.ProcessMessages;
end;
procedure ArtScreenKeyboardForm_Hide;
begin
If ArtScreenKeyboardForm <> nil then
begin
ArtScreenKeyboardForm.Free;
ArtScreenKeyboardForm := nil;
end;
end;
procedure TArtScreenKeyboardForm.FormShow(Sender: TObject);
begin
DrawButtons;
end;
procedure TArtScreenKeyboardForm.SetDefaultSizeAndPosition;
begin
inherited;
Width := 300;
PlaceControl( Self, cpWorkAreaTopLeft );
end;
procedure TArtScreenKeyboardForm.FormClose(Sender: TObject;
var Action: TCloseAction);
begin
Action := caFree;
ArtScreenKeyboardForm := nil;
end;
procedure TArtScreenKeyboardForm.FormCreate(Sender: TObject);
begin
Constraints.MinWidth := 200;
Constraints.MinHeight := (120 * 5) div 4;
end;
procedure TArtScreenKeyboardForm.DrawButtons;
procedure AddButton( ATop, ALeft, AWidth, AHeight : integer; AKind : TButtonKind );
function WidthPix( AValue : integer ) : integer;
begin
Result := AValue * (ClientWidth div 4);
end;
function HeightPix( AValue : integer ) : integer;
begin
Result := AValue * (ClientHeight div 5);
end;
var
Button : TSpeedButton;
begin
Button := TSpeedButton.Create( Self );
Button.Parent := Self;
Button.Left := WidthPix( ALeft );
Button.Top := HeightPix( ATop );
Button.Width := WidthPix( AWidth );
Button.Height := HeightPix( AHeight );
Button.Visible := True;
Button.Name := Format( 'btn%d', [Ord( AKind )] );
Button.Caption := ButtonCaptions[ AKind ];
button.OnClick := DoOnbuttonClick;
end;
var
I : integer;
begin
Height := (Width * 5) div 4;
ApplyScreenIconTitleFontToFont( Font );
Font.Size := Font.Size + ((Height-250) div 30);
Font.Style := Font.Style + [fsBold];
Font.Color := clGray;
For I := ComponentCount-1 downto 0 do
If Components[I] is TSpeedButton then
Components[I].Free;
Addbutton( 0, 0, 1, 1, bkDel );
Addbutton( 0, 1, 1, 1, bkEquals );
Addbutton( 0, 2, 1, 1, bkDiv );
Addbutton( 0, 3, 1, 1, bkMul );
Addbutton( 1, 0, 1, 1, bk7 );
Addbutton( 1, 1, 1, 1, bk8 );
Addbutton( 1, 2, 1, 1, bk9 );
Addbutton( 1, 3, 1, 1, bkMinus );
Addbutton( 2, 0, 1, 1, bk4 );
Addbutton( 2, 1, 1, 1, bk5 );
Addbutton( 2, 2, 1, 1, bk6 );
Addbutton( 2, 3, 1, 1, bkPlus );
Addbutton( 3, 0, 1, 1, bk1 );
Addbutton( 3, 1, 1, 1, bk2 );
Addbutton( 3, 2, 1, 1, bk3 );
Addbutton( 3, 3, 1, 2, bkEnter );
Addbutton( 4, 0, 2, 1, bk0 );
Addbutton( 4, 2, 1, 1, bkDecPt );
end;
procedure TArtScreenKeyboardForm.FormResize(Sender: TObject);
begin
DrawButtons;
end;
initialization
finalization
FreeAndNil( ArtScreenKeyboardForm );
end.