How to use window focus messages for a Delphi on-screen keyboard

I need a built-in numeric keypad on the screen in the application. For various reasons, I cannot use TMS Software or other commercial offers. I am very pleased with the button-based solution given below, but I don’t see how to solve the problem of switching focus when pressing a button activates the shape of the keyboard and I lose the focused control that I need the characters in. My solution works if I keep the keyboard buttons in the target form, but I would like to get a solution independent of the form. Is there a way to turn off button activation or find out where the focus came from so I can use something like Scree.ActiveControl: = ?? get him back?

enter image description here

+8
forms delphi keyboard focus
source share
3 answers

I do not know how to create a window with a frame that does not focus when you click on it, so the next one is without a frame. And, as Andreas mentioned, use TSpeedButtons.

type TKeypadForm = class(TForm) SpeedButton1: TSpeedButton; procedure SpeedButton1Click(Sender: TObject); private procedure CreateParams(var Params: TCreateParams); override; procedure WMMouseActivate(var Message: TWMMouseActivate); message WM_MOUSEACTIVATE; end; procedure TKeypadForm.CreateParams(var Params: TCreateParams); begin inherited CreateParams(Params); Params.Style := WS_POPUP or WS_THICKFRAME; end; procedure TKeypadForm.WMMouseActivate(var Message: TWMMouseActivate); begin Message.Result := MA_NOACTIVATE; end; procedure TKeypadForm.SpeedButton1Click(Sender: TObject); begin PostMessage(GetFocus, WM_KEYDOWN, VK_NUMPAD1, MakeLong(0, MapVirtualKey(VK_NUMPAD1, 0))); end; 

And here's how to show the keyboard window

 procedure TForm18.Edit1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin case Key of VK_RETURN: ShowWindow(KeypadForm.Handle, SW_SHOWNOACTIVATE); VK_ESCAPE: ShowWindow(KeypadForm.Handle, SW_HIDE); end; end; 
+2
source share

You can use TSpeedButton for this task on the keyboard. TSpeedButton does not put Focus. But the form does. And it's ugly, even if you give focus to your main form, the focus flickers between the two forms. So I would try to create a form without focus.

The WS_EX_NOACTIVATE flag can be used to create a window (form) that does not become the front window when the user clicks on it. In addition, the system does not bring this window to the forefront when the user minimizes or closes the foreground window.

To create a non-activated form, override the CreateParams method as:

 procedure TMainForm.CreateParams(var Params: TCreateParams) ; //const WS_EX_NOACTIVATE = $8000000; begin inherited; Params.ExStyle := Params.ExStyle + WS_EX_NOACTIVATE; end; 

When Delphi creates the form, the Create method calls the CreateWindowEx API function to create the actual window.

Before executing CreateWindowEx, the CreateParams method is called - CreateParams allows you to change the default style for the window when it is created according to your specific needs.

+5
source share

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. 
0
source share

All Articles