My final decision is this. This creates a numerical panel with a frame and - yes - it is activated if the border is pressed or changed, but pressing the buttons does not steal focus from the target shape / control. Just using CreateParams did not work for me - instead, you needed the WMMouseActivate message.
I combined it with a routine that discovered that it was hosting the key for the OS, not just targeted control. Please note that the code below assumes some simple support for the ancestor form to set the default size and position. Thank you for 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.