2011-05-06 100 views
8

我需要一个内置的屏幕数字键盘在我的应用程序。由于各种原因,我不能使用TMS Software或其他商业组件产品。我对下面显示的基于按钮的解决方案感到非常满意,但是我还不知道如何解决焦点切换问题,点击按钮激活键盘窗体,我失去了我想要人物的焦点控制。我的解决方案如果将键盘按钮保留在目标表单中,但我希望独立于表单的解决方案。有没有一种方法来禁用按钮激活或知道焦点来自哪里,以便我可以使用像Scree.ActiveControl:= ??把它放回去?如何使用窗口焦点消息的Delphi屏幕上的键盘形式

enter image description here

+0

要获取具有键盘焦点的窗口的句柄,请尝试使用[GetFocus](http://msdn.microsoft.com/zh-cn/library/ms646294%28v=vs.85%29.aspx ),但是如何创建不可聚焦的窗口我现在不能告诉你,但你可以获得灵感,例如从TRxCalcEdit,它有这样的弹出数字键盘。 – 2011-05-06 12:32:38

回答

2

我不知道如何创建窗口的框架,它是不可聚焦的,当你点击它,所以下面的一个没有边框。正如Andreas所说,使用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; 

下面是如何显示的键盘窗口

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

恕我直言,它不可能创建框架窗口的框架点击不聚焦窗口,从用户的角度来看,这将是相当混乱。 – 2011-05-06 13:35:35

+0

谢谢,你是对的 - 鼠标消息是至关重要的,CreateParams不是这样。布莱恩。 – 2011-05-07 15:56:38

5

可以使用TSpeedButton在您的键盘此任务。 TSpeedButton没有对焦点进行加固。但形式确实如此。而且这很丑陋,即使你将注意力集中在主要形式上,焦点也会在两种形式之间闪烁。所以我会尝试创建一个没有焦点的表单。

一个名为WS_EX_NOACTIVATE的标志可以用来创建一个窗口(窗体),当用户单击它时它不会成​​为前景窗口。此外,当用户最小化或关闭前景窗口时,系统不会将此窗口置于前台。

要创建非激活的形式,覆盖的CreateParams方法:

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

当德尔福创建一个表单,创建方法调用是CreateWindowEx API函数来创建实际的窗口。

在执行CreateWindowEx之前,将调用CreateParams方法 - CreateParams允许您在创建窗口时根据特定需要更改窗口的默认样式。

+0

但是窗户呢。 – 2011-05-06 11:13:26

+0

好的,对。我增加了一个更好的答案... – Andreas 2011-05-06 11:38:10

+0

仍然可以专注窗口。即使你将包括WM_MOUSEACTIVATE处理你的按钮将获得焦点。 – 2011-05-06 12:16:29

0

我的最终解决方案如下。这会创建一个带有边框的数字键盘,并且 - 如果边框被单击或调整大小,它会激活,但单击按钮不会从目标表单/控件中窃取焦点。简单地使用CreateParams并不适合我 - 它似乎需要WMMouseActivate消息。

我已经将它与一个例程相结合,我发现将操作系统的关键字发布到操作系统,而不仅仅是集中控制。请注意,下面的代码假定来自祖先形式的一些简单支持来设置默认大小和位置。感谢你的帮助。

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.