virtualtreeview programmatically moving the selection bar (responding to down/up key) - delphi-7

How to move the selection in the treeview, akin to dbgrid.next/dbgrid.previous?
I'm trying to move the selection bar in the VST when down/up key is pressed in a TEdit control.
This I have tried, works, but seems too verbose to me for a simple task:
procedure TfrmUserManager.edtTaskFilterKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
var
Node: PVirtualNode;
begin
if not Assigned(vstTasks.FocusedNode) then
Node:= vstTasks.GetFirst
else
Node:= vstTasks.FocusedNode;
if key = VK_DOWN then
vstTasks.FocusedNode:= vstTasks.GetNextVisible(Node)
else if key = VK_UP then
vstTasks.FocusedNode:= vstTasks.GetPreviousVisible(Node);
vstTasks.Selected[vstTasks.FocusedNode] := True;
if key in [VK_DOWN,VK_UP] then key := 0;
end;
Can this be simplified? TIA.

Related

Set position of InfoTip

My TListView control has ShowHints enabled and handles the OnInfoTip event. The message in the popup InfoTip box is set in the OnInfoTip handler. However, the position of the popup InfoTip box is relative to the position of the mouse when hovering over an item in the list. There doesn't appear to be a way to customise the position.
Is it possible set the position of the hint popup, for example in a specific area of the TListView or even elsewhere on the form outside the bounds of the TListView control? Ideally, I'd like to display the hint popup in such a way to minimise (or eliminate) obscuring any other item in the TListView.
First you have to expose the CMHintShow of the TListView as following:
type
TListView = class(Vcl.ComCtrls.TListView)
private
FPos: TPoint;
protected
procedure CMHintShow(var Message: TCMHintShow); message CM_HINTSHOW;
published
property MyPos: TPoint read FPos write FPos;
end;
TfrmMain = class(TForm)
...
ListView1: TListView;
Then at the OnInfoTip event you set the desired Position. At my example, I get the coords of the TopLeft Corner of a ScrollBox (sbxFilter - which is located under the TlistView) and pass the Coords to the TListView property MyPos.
procedure TfrmMain.ListView1InfoTip(Sender: TObject; Item: TListItem; var InfoTip: string);
var
p: TPoint;
begin
InfoTip := 'Test';
p := sbxFilter.ClientToScreen(point(0, 0));
ListView1.MyPos := p;
end;
{ TListView }
procedure TListView.CMHintShow(var Message: TCMHintShow);
begin
inherited;
Message.HintInfo.HintPos := FPos;
end;
It is possible to display the hint, that you define in the OnInfoTip() event in e.g. a StatusPanel of a StatusBar (at the bottom of the form).
For example:
procedure TForm1.ListView1InfoTip(Sender: TObject; Item: TListItem;
var InfoTip: string);
begin
InfoTip := '';
StatusBar1.Panels[0].Text := 'ListView Infotip, Item '+IntToStr(Item.Index);
end;

Lazarus find control under cursor

I am using the following code from this posting.
Code from Checked Answer
I need to get the Control (Label.Caption) under the mouse cursor from one of several TLabel and it worked fine when the Label was on the Main From. I put the Labels on a Panel on the Main form and now this only finds the Panel. I only want this to work on a select few of the Labels of the many that are on the Panel.
I tried changing the Z-Order for the Labels as "Bring To Front" but it made no difference, still got the Panel. How can I again find a Label under the cursor now that they are on the Panel?
Lazarus does not appear to have FindVCLWindow or ObjectAtPoint.
procedure TForm1.Button1Click(Sender: TObject);
var
ctrl : TControl;
point : TPoint;
begin
point := Mouse.CursorPos; // Mouse pos at screen
Dec(point.X, Left); // Adjust for window.
Dec(point.Y, Top);
Dec(point.Y, GetSystemMetrics(SM_CYCAPTION)); // Adjust to client area.
ctrl := ControlAtPos(point, True, True, True);
// I added the following
tStr:=ctrl.Name; // DEBUG: This now shows "Panel2"
aStr:=(ctrl as TLabel).Caption; // This used to work
end;
Try:
procedure TForm1.Button1Click(Sender: TObject);
var
ctrl: TControl;
pt: TPoint;
begin
pt := ScreenToClient(Mouse.CursorPos);
ctrl := ControlAtPos(pt, [capfRecursive, capfAllowWinControls]);
if Assigned(ctrl) then
Caption := ctrl.Name
else
Caption := Format('%d, %d', [pt.x, pt.y]);
end;

how do I intercept was pressed the F1 key on my main window?

How to do that when I'm in the main window of my system by pressing F1 will display the help muetre in pdf format. how do I intercept was pressed the F1 key on my main window?
I use Delphi XE2
Thanks for the help!
Use the Application.OnHelpCommand event, which you can either assign in code:
interface
type
TForm1 = class(TForm)
// IDE generated code
private
procedure AppOnHelp(Command: Word; Data: Integer;
var CallHelp: Boolean);
end;
implementation
procedure TForm1.FormCreate(Sender: TObject);
begin
Application.OnHelp := AppOnHelp;
end;
Or assign by using a TApplicationEvents component and creating a handler for the OnHelp event in the Object Inspector's Events tab.
You can set CallHelp to false to prevent the normal help processing, and launch your own help file via ShellExecute.
procedure TForm1.AppOnHelp(Command: Word; Data: Integer;
var CallHelp: Boolean);
begin
CallHelp := False;
// Launch your own help here
end;

Move form without border style

how do I move a borderless form? I tried looking on the internet, but nothing. Thanks a lot.
You can drag a form using any contained control, including itself.
Using the following example, you can move a form by clicking on its canvas and dragging. You could do the same with a panel on the form by putting the same code in the panel's MouseDown event, which would let you create your own pseudo caption bar.
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
const
SC_DRAGMOVE = $F012;
begin
if Button = mbLeft then
begin
ReleaseCapture;
Perform(WM_SYSCOMMAND, SC_DRAGMOVE, 0);
end;
end;
If you mean dragging the window by the mouse, you can override WM_NCHITTEST message handling and return HTCAPTION for the drag region. The below will drag the window within the upper 30 pixels for insance:
type
TForm1 = class(TForm)
private
protected
procedure WMNCHitTest(var Message: TWMNCHitTest); message WM_NCHITTEST;
end;
..
procedure TForm1.WMNCHitTest(var Message: TWMNCHitTest);
var
Pt: TPoint;
begin
Pt := ScreenToClient(SmallPointToPoint(Message.Pos));
if Pt.Y < 30 then
Message.Result := HTCAPTION
else
inherited;
end;

Delphi XE and Trapping Arrow Key with OnKeyDown

I want my form to handle the arrow keys, and I can do it -- as long as there is no button on the form. Why is this?
Key messages are processed by the controls themselves who receives these messages, that's why when you're on a button the form is not receiving the message. So normally you would have to subclass these controls, but the VCL is kind enough to ask the parenting form what to do if the form is interested:
type
TForm1 = class(TForm)
..
private
procedure DialogKey(var Msg: TWMKey); message CM_DIALOGKEY;
..
procedure TForm1.DialogKey(var Msg: TWMKey);
begin
if not (Msg.CharCode in [VK_DOWN, VK_UP, VK_RIGHT, VK_LEFT]) then
inherited;
end;
François editing: to answer the OP original question, you need to call onKeyDown somehow so that his event code would work (feel free to edit; was too long for a comment).
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
private
{ Private declarations }
procedure DialogKey(var Msg: TWMKey); message CM_DIALOGKEY;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.DialogKey(var Msg: TWMKey);
begin
case Msg.CharCode of
VK_DOWN, VK_UP, VK_RIGHT, VK_LEFT:
if Assigned(onKeyDown) then
onKeyDown(Self, Msg.CharCode, KeyDataToShiftState(Msg.KeyData));
else
inherited
end;
end;
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
case Key of
VK_DOWN: Top := Top + 5;
VK_UP: Top := Top - 5;
VK_LEFT: Left := Left - 5;
VK_RIGHT: Left := Left + 5;
end;
end;
Arrow keys are used to navigate between buttons on a form. This is standard Windows behaviour. Although you can disable this standard behaviour you should think twice before going against the platform standard. Arrow keys are meant for navigation.
If you want to get the full low down on how a key press finds its way through the message loop I recommend reading A Key's Odyssey. If you want to intercept the key press before it becomes a navigation key, you need to do so in IsKeyMsg or earlier. For example, Sertac's answer gives one such possibility.
Only the object that has the focus can receive a keyboard event.
To let the form have access to the arrow keys event,
declare a MsgHandler in the public part of the form.
In the form create constructor, assign the Application.OnMessage to this MsgHandler.
The code below intercepts the arrow keys only if they are coming from a TButton descendant. More controls can be added as needed.
procedure TForm1.FormCreate(Sender: TObject);
begin
Application.OnMessage := Self.MsgHandler;
end;
procedure TForm1.MsgHandler(var Msg: TMsg; var Handled: Boolean);
var
ActiveControl: TWinControl;
key : word;
begin
if (Msg.message = WM_KEYDOWN) then
begin
ActiveControl := Screen.ActiveControl;
// if the active control inherits from TButton, intercept the key.
// add other controls as fit your needs
if not ActiveControl.InheritsFrom(TButton)
then Exit;
key := Msg.wParam;
Handled := true;
case Key of // intercept the wanted keys
VK_DOWN : ; // doStuff
VK_UP : ; // doStuff
VK_LEFT : ; // doStuff
VK_RIGHT : ; // doStuff
else Handled := false;
end;
end;
end;
Because they are preempted to deal with setting the focus on the next available WinControl.
(I'm pretty sure that if you put an Edit instead of a Button you see the same thing).
If you want to handle them yourself, you can provide the Application with an OnMessage event that will filter those before they are processed and handle them yourself there.
var
KBHook: HHook; {this intercepts keyboard input}
implementation
{$R *.dfm}
function KeyboardHookProc(Code: Integer; WordParam: Word; LongParam: LongInt): LongInt; stdcall;
begin
case WordParam of
vk_Space: ShowMessage ('space') ;
vk_Right:ShowMessage ('rgt') ;
vk_Left:ShowMessage ('lft') ;
vk_Up: ShowMessage ('up') ;
vk_Down: ShowMessage ('down') ;
end; {case}
end;
procedure TForm4.FormCreate(Sender: TObject);
begin
KBHook:=SetWindowsHookEx(WH_KEYBOARD,#KeyboardHookProc,HInstance,GetCurrentThreadId());
end;
This code will work even when a control is focused (buttons , listboxes), so be careful some controls may loose their keyboard events (Read David haffernans answer) .
keyboard events with Focused controls
eg: If you are having textbox in your app and want to recive text(if focused) also , then
add an applicationevent1
procedure TForm4.ApplicationEvents1Message(var Msg: tagMSG;var Handled: Boolean);
begin
if Msg.message = WM_KEYFIRST then
KBHook:=SetWindowsHookEx(WH_KEYBOARD,#KeyboardHookProc,HInstance,GetCurrentThreadId());
end;
add the following code at the bottom of the function KeyboardHookProc
UnhookWindowsHookEx(KBHook);
and remove
KBHook:=SetWindowsHookEx(WH_KEYBOARD,#KeyboardHookProc, HInstance,
GetCurrentThreadId());
from oncreate event.

Resources