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

前端 未结 3 1939
感情败类
感情败类 2021-01-02 10:22

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

相关标签:
3条回答
  • 2021-01-02 10:47

    You can use a TSpeedButton for this task on your Keypad. TSpeedButton does not steel the Focus. But the Form does. And this is ugly, even if you give the focus back to your main form, the focus flickers between the two forms. So I would try to create a form without focus.

    A flag named WS_EX_NOACTIVATE can be used to create a window (form) that does not become the foreground window when the user clicks it. Also, the system does not bring this window to the foreground when the user minimizes or closes the foreground window.

    To create a non activatable 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 a form, the Create method calls the CreateWindowEx API function to create the actual window.

    Before executing the CreateWindowEx, the CreateParams method is called - CreateParams allows you to change the default style of a window when it is created to suit your particular needs.

    0 讨论(0)
  • 2021-01-02 10:47

    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.
    
    0 讨论(0)
  • 2021-01-02 11:13

    I don't know how to create window with the frame which is unfocusable when you click it, so the following one is without border. 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 keypad 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;
    
    0 讨论(0)
提交回复
热议问题