Как использовать сообщения фокуса окна для формы экранной клавиатуры Delphi
Мне нужна встроенная экранная цифровая клавиатура в моем приложении. По разным причинам я не могу использовать программное обеспечение TMS или другие коммерческие предложения компонентов. Я очень доволен решением на основе кнопок, показанным ниже, но я еще не вижу, как решить проблему переключения фокуса, когда нажатие кнопки активирует форму клавиатуры, и я теряю сфокусированный контроль, в который я хотел символы. Мое решение работает, если я сохраняю кнопки клавиатуры в целевой форме, но я бы как независимое от формы решение. Есть ли способ отключить активацию кнопки или узнать, откуда пришел фокус, чтобы я мог использовать что-то вроде осыпи.Свойство activecontrol :=?? положить обратно?
3 ответов
Я не знаю, как создать окно с рамкой, которая непростительна, когда вы нажимаете на нее, поэтому следующая без границы. И, как упоминал Андреас, используйте кнопки 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;
для этой задачи на клавиатуре можно использовать кнопку TSpeedButton. TSpeedButton не стали фокус. Но форма знает. И это уродливо, даже если вы возвращаете фокус к своей основной форме, фокус мерцает между двумя формами. Поэтому я бы попытался создать форму без фокуса.
флаг с именем WS_EX_NOACTIVATE можно использовать для создания окна (формы), которое не становится окном переднего плана, когда пользователь щелкает его. Кроме того, система не выводит это окно на передний план когда пользователь сворачивает или закрывает окно.
создать без активации форма, переопределить CreateParams способ как:
procedure TMainForm.CreateParams(var Params: TCreateParams) ;
//const WS_EX_NOACTIVATE = 00000;
begin
inherited;
Params.ExStyle := Params.ExStyle + WS_EX_NOACTIVATE;
end;
когда Delphi создает форму, метод Create вызывает функцию CreateWindowEx API для создания фактического окна.
перед выполнением CreateWindowEx вызывается метод CreateParams - CreateParams позволяет изменить стиль окна по умолчанию при его создании в соответствии с вашими конкретными потребностями.
мое окончательное решение таково. Это создает цифровую панель с границей и-да-она активируется, если граница нажата или изменена, но нажатие кнопок не крадет фокус из целевой формы / элемента управления. Просто использование 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.