Solved

Set focus to a MenuItem

Posted on 2010-09-09
7
1,369 Views
Last Modified: 2013-12-04
I have a Popupmenu with several items on it. I want to set focus or select one of these menu items. Maybe it could work with some sort of SendMessage and the Handle of the MenuItem.
0
Comment
Question by:dMa
  • 4
  • 2
7 Comments
 
LVL 33

Expert Comment

by:pgnatyuk
Comment Utility
I think you can use SendInput/keyb_event.
http://msdn.microsoft.com/en-us/library/ms646310(VS.85).asp
Here is an example in Delphi:
http://www.swissdelphicenter.ch/torry/showcode.php?id=220

>> I want to set focus or select one of these menu items.
Probably, I do not understand the question. If you create this popup menu in your program, it receives the focus when you call TrackPopupMenu function. Select an item means close the menu. TrackPopupMenu returns the index of the selected item. Or WM_COMMAND message with the item is received in the parent window.
If you are going to handle a popup menu in another application, try to use SendInput or keyb_event.
0
 
LVL 25

Expert Comment

by:epasquier
Comment Utility
After playing with the HiliteMenuItem function called this way :
HiliteMenuItem( pm1.WindowHandle, pm1.Handle,1,MF_BYPOSITION Or MF_HILITE)
I found out that it just doesn't work. It returns true but fails to highligh anything, at least in my Vista.

Besides, even if it did, it would only until the mouse moved again just a bit

I tried also to send keys to the popup menu handle
 PostKeyExHWND( pm1.WindowHandle, VK_DOWN );
(PostKeyExHWND is a tool function I use to simulate keys stroke, basically it sets the keyboard state and send DOWN/UP messages for that key. See code below if you want to try yourself)
But again it just does nothing...

The only thing that works, and that was in fact my first attempt, is to move the mouse position over the item you want :

procedure TForm1.pm1Popup(Sender: TObject);
begin
  With popMenu1 do SetCursorPos(PopupPoint.X+10, PopupPoint.Y+60);
// you would have to calculate Y offset (here 60) to select the proper item
end;
but for that code to work, you have to make the PopupPoint property of TPopupMenu public (it is protected, for an unknown reason...). You can do this by adding this code BEFORE the form type definition

type
   TPopupMenu = class(Menus.TPopupMenu)
    public
       property PopupPoint; // just augment this property accessibility
    end;

   TForm1 = class(TForm)
...
// Code for PostKeyExHWND : does not work in this case, 

// but it does in so many other situations that it's worth posting



procedure PostKeyExHWND(hWindow: HWnd; key: Word; const shift: TShiftState=[]; specialkey: Boolean=False);

{************************************************************

 * Procedure PostKeyEx

 *

 * Parameters:

 *  hWindow: target window to be send the keystroke

 *  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.

 *           If this parameter is true, bit 24 of the lparam for

 *           the posted WM_KEY* messages will be set.

 * Description:

 *  This procedure sets up Windows key state array to correctly

 *  reflect the requested pattern of modifier keys and then posts

 *  a WM_KEYDOWN/WM_KEYUP message pair to the target window. Then

 *  Application.ProcessMessages is called to process the messages

 *  before the keyboard state is restored.

 * Error Conditions:

 *  May fail due to lack of memory for the two key state buffers.

 *  Will raise an exception in this case.

 * NOTE:

 *  Setting the keyboard state will not work across applications

 *  running in different memory spaces on Win32 unless AttachThreadInput

 *  is used to connect to the target thread first.

 *Created: 02/21/96 16:39:00 by P. Below

 ************************************************************}

type

  TBuffers = array [0..1] of TKeyboardState;

var

  pKeyBuffers: ^TBuffers;

  lParam: LongInt;



  procedure check(b:Boolean);

  begin

   if Not b

    then ShowMessage(IntToStr(GetLastError));

  end;

begin

  (* check if the target window exists *)

  if IsWindow(hWindow) then

  begin

    (* set local variables to default values *)

    pKeyBuffers := nil;

    lParam := MakeLong(0, MapVirtualKey(key, 0));



    (* modify lparam if special key requested *)

    if specialkey then

      lParam := lParam or $1000000;



    (* allocate space for the key state buffers *)

    New(pKeyBuffers);

    try

      (* Fill buffer 1 with current state so we can later restore it.

         Null out buffer 0 to get a "no key pressed" state. *)

      GetKeyboardState(pKeyBuffers^[1]);

      FillChar(pKeyBuffers^[0], SizeOf(TKeyboardState), 0);



      (* set the requested modifier keys to "down" state in the buffer*)

      if ssShift in shift then

        pKeyBuffers^[0][VK_SHIFT] := $80;

      if ssAlt in shift then

      begin

        (* Alt needs special treatment since a bit in lparam needs also be set *)

        pKeyBuffers^[0][VK_MENU] := $80;

        lParam := lParam or $20000000;

      end;

      if ssCtrl in shift then

        pKeyBuffers^[0][VK_CONTROL] := $80;

      if ssLeft in shift then

        pKeyBuffers^[0][VK_LBUTTON] := $80;

      if ssRight in shift then

        pKeyBuffers^[0][VK_RBUTTON] := $80;

      if ssMiddle in shift then

        pKeyBuffers^[0][VK_MBUTTON] := $80;



      (* make out new key state array the active key state map *)

      SetKeyboardState(pKeyBuffers^[0]);

      (* post the key messages *)

      if ssAlt in Shift then

      begin

        check(PostMessage(hWindow, WM_SYSKEYDOWN, key, lParam));

        check(PostMessage(hWindow, WM_SYSKEYUP, key, lParam or $C0000000));

      end

      else

      begin

        check(PostMessage(hWindow, WM_KEYDOWN, key, lParam));

        check(PostMessage(hWindow, WM_KEYUP, key, lParam or $C0000000));

      end;

      (* process the messages *)

      Application.ProcessMessages;



      (* restore the old key state map *)

      SetKeyboardState(pKeyBuffers^[1]);

    finally

      (* free the memory for the key state buffers *)

      if pKeyBuffers <> nil then

        Dispose(pKeyBuffers);

    end; { If }

  end;

end; { PostKeyEx }

Open in new window

0
 
LVL 2

Author Comment

by:dMa
Comment Utility
I don't want to move the cursor.

It's better I describe the practical problem.

My app is hidden to tray icon. I display a popup menu after the user presses a (user defined) hotkey. This means the menu popups in another application, at the point where the cursor is (e.g. in notepad). My problem is that if I assign a shortcut to a Menuitem, e.g. Alt + A it does not work, because after pressing Alt the Popupmenu disappears. I thought if I could set focus to any of the menuitems first, the shortcuts may start to work.
0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 25

Accepted Solution

by:
epasquier earned 250 total points
Comment Utility
hum. that popup menu is capricious... I just can't get anything to programmatically select items, set focus on the popup window etc...
what about system-wide hotkeys for each sub menu ?
unit Main;



interface



uses

  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

  Dialogs, abfComponents, StdCtrls, ComCtrls;



type

  TMainForm = class(TForm)

    HotKey: THotKey;

    btnRegister: TButton;

    procedure FormCreate(Sender: TObject);

    procedure FormDestroy(Sender: TObject);

    procedure btnRegisterClick(Sender: TObject);

  private

    { Déclarations privées }

     Procedure WMHotkey( Var msg: TWMHotkey ) ; message WM_HOTKEY;

  public

    { Déclarations publiques }

  end;



var

  MainForm: TMainForm;



implementation



{$R *.dfm}



Const

 HOT_KEY_FUNCTION_1 = 1; // 1 = message identifier for this hotkey function



//In the main forms OnCreate

//handler assign the hotkey:

procedure TMainForm.FormCreate(Sender: TObject);

begin

 If not RegisterHotkey (Handle, HOT_KEY_FUNCTION_1, MOD_CONTROL , VK_F3)

  Then ShowMessage('Unable to assign Ctrl-F3 as hotkey.') ;

end;



//In the main form OnDestroy event remove the handler:

procedure TMainForm.FormDestroy(Sender: TObject);

begin

 UnRegisterHotkey( Handle, HOT_KEY_FUNCTION_1 ) ;

end;



// that button allow modification of the default hotkey using 

// a THotKey edit component

procedure TMainForm.btnRegisterClick(Sender: TObject);

Var

 Modif:Integer;

begin

 UnRegisterHotkey( Handle, HOT_KEY_FUNCTION_1 ) ;

 Modif:=0;

 if hkShift in HotKey.Modifiers then Modif:=Modif Or MOD_SHIFT;

 if hkCtrl in HotKey.Modifiers then Modif:=Modif Or MOD_CONTROL;

 if hkAlt in HotKey.Modifiers then Modif:=Modif Or MOD_ALT;

 if hkExt in HotKey.Modifiers then Modif:=Modif Or MOD_WIN;

 RegisterHotkey (Handle, 1, Modif , HotKey.HotKey And $1FFF );

end;



Procedure TMainForm.WMHotkey( Var msg: TWMHotkey ) ;

Begin

 Case msg.hotkey of

  HOT_KEY_FUNCTION_1:

  begin

// The HOT_KEY_FUNCTION_1 is the application identifier of this hotkey

// used in (un)RegisterHotKey functions

// Here you do what you want in response to the hot key #1=Ctrl-F3



  end;

End;



end.

Open in new window

0
 
LVL 2

Author Comment

by:dMa
Comment Utility
System wide hotkeys could be a solution, but  a very dirty one. At least it's an idea!
0
 
LVL 2

Author Comment

by:dMa
Comment Utility
p.s.

I've also tried using Ctrl + A instead of Alt + A, but that didn't work.. The menu does not disappear, but pressing Ctrl + A does nothing. The strange thing is that pressing the underlined hotkey works.  (e.g if an item has the caption named copy and c is underlined, then pressing the c key acutally works and the menu item is selected).
0
 
LVL 2

Author Closing Comment

by:dMa
Comment Utility
The solution for the problem seems not possible, but a work around idea was presented.
0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Since upgrading to Office 2013 or higher installing the Smart Indenter addin will fail. This article will explain how to install it so it will work regardless of the Office version installed.
A short article about problems I had with the new location API and permissions in Marshmallow
Viewers will learn how to properly install Eclipse with the necessary JDK, and will take a look at an introductory Java program. Download Eclipse installation zip file: Extract files from zip file: Download and install JDK 8: Open Eclipse and …
In this seventh video of the Xpdf series, we discuss and demonstrate the PDFfonts utility, which lists all the fonts used in a PDF file. It does this via a command line interface, making it suitable for use in programs, scripts, batch files — any pl…

744 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now