Code Repo    |     RSS
MD's Technical Sharing



Thursday, November 6, 2008

Floating popup menu on a Smartphone

As there is no touchscreen on a smartphone and therefore no such thing as "tap and hold", the .NET compact framework for WM6 standard doesn't support the ContextMenu property for textbox controls. Simulate this behaviour by displaying a popup menu whenever the user presses ENTER when the textbox is having focus. This is possible in C++ by using TrackPopupMenu API.


Method 1: Retrieve all the sub-menus under the left soft key menu of the application (or in general, associated with the MainMenu property of the application) and display as pop-up menu on the top left corner of the form:


IntPtr parentWindow = GetActiveWindow()
IntPtr hMenuWnd
= SHFindMenuBar(parentWindow);
IntPtr hMainMenu
= (IntPtr)SendMessage(hMenuWnd, SHCMBM_GETMENU, 0, 0);
IntPtr hMenu
= GetSubMenu(hMainMenu, 0);
TrackPopupMenu(hMenu, TPM_LEFTALIGN, 0, 0, parentWindow, IntPtr.Zero);

[DllImport("coredll.dll", EntryPoint = "TrackPopupMenuEx", SetLastError = true)]
private static extern int TrackPopupMenu(IntPtr hMenu, int wFlags, int x, int y, IntPtr hwnd, IntPtr lprc);

[DllImport("coredll.dll", SetLastError = true)]
public static extern IntPtr GetActiveWindow();

[DllImport("aygshell.dll", SetLastError=true)]
public static extern IntPtr SHFindMenuBar(IntPtr hWindow);

[DllImport("coredll.dll", SetLastError = true)]
public static extern int SendMessage(IntPtr hWnd, int msg, int wParam, int lParam);

[DllImport("coredll.dll", SetLastError = true)]
public static extern IntPtr GetSubMenu(IntPtr hMenu, int nPos);


The OnClick event of the menu items can be declared the usual way:


this.menuItem3.Click += new System.EventHandler(this.menuItem3_Click);
private void menuItem3_Click(object sender, EventArgs e)
{
}


and menuItem3_Click will be called when the associated menu item is clicked, regardless of whether it comes from the pop-up menu or from the main menu.


This approach has some limitations:


(1) It can only display a sub-menu (via GetSubMenu) but cannot display the entire menu. Removing the GetSubMenu line and passing hMainMenu to TrackPopupMenu immediately causes TrackPopupMenu to return 0 and GetLastError returns ERROR_INVALID_HANDLE. How to display the entire menu?


(2) The menu can only be displayed as pop-up if it is associated with the MainMenu property of the form (otherwise SHFindMenuBar would not work). How to display the pop-up menu without first associating it with the form? One approach is to use reflection to retrieve the private properties of the MainMenu data type, which hopefully contains the IntPtr handle.


Method 2: Create our own menu and pass the handle to TrackPopupMenu:


IntPtr hMenu = CreatePopupMenu();
int index = 100;
AppendMenu(hMenu, MF_STRING, index, "Item1");index++;
AppendMenu(hMenu, MF_STRING, index, "Item2");index++;
AppendMenu(hMenu, MF_STRING, index, "Item3");index++;


It works this way (the popup menu is displayed) but handling the click event is difficult. To trace which item is clicked we must use:


TrackPopupMenu(hMenu, TPM_LEFTALIGN | TPM_RETURNCMD, 0, 0, parentWindow, IntPtr.Zero);


which make the call to TrackPopupMenu a blocking call and will return the index of the selected menu item.


Reference:

http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=3556499&SiteID=1

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.