Context menu support in CTreeView
Irvin Waldman -- Irvin_Waldman@crow.bmc.com
Thursday, October 17, 1996
Environment: VC++ 4.2, Win 95
Q: Context menu support in CTreeView
I have wired context menu support in a CTreeView derived class. I
then provided an override for NM_RCLICK in order to display the pop-up
menu. How do I obtain the right-clicked item since it seems that an
item is only selected if a left-click is performed? Also, how do I
obtain the coordinates e.g. CPoint, of the right-clicked item in order
to positioned the pop menu appropriately?
Thanks in advanced.
Irvin M. Waldman
BMC Software, Inc.
SachinX Keskar -- SachinX_Keskar@ccm.jf.intel.com
Friday, October 18, 1996
Text item:
Probably you might try the HitTest member function of the CTreeCtrl class.
Sachin
Environment: VC++ 4.2, Win 95
Q: Context menu support in CTreeView
I have wired context menu support in a CTreeView derived class. I
then provided an override for NM_RCLICK in order to display the pop-up
menu. How do I obtain the right-clicked item since it seems that an
item is only selected if a left-click is performed? Also, how do I
obtain the coordinates e.g. CPoint, of the right-clicked item in order
to positioned the pop menu appropriately?
Thanks in advanced.
Irvin M. Waldman
BMC Software, Inc.
Text item: External Message Header
The following mail header is for administrative use
and may be ignored unless there are problems.
***IF THERE ARE PROBLEMS SAVE THESE HEADERS***.
Reply-To: mfc-l@netcom.com
Precedence: bulk
Errors-To: owner-mfc-l@majordomo.netcom.com
Sender: owner-mfc-l@majordomo.netcom.com
Content-Description: cc:Mail note part
Randy Taylor -- randy_taylor@ebt.com
Friday, October 18, 1996
[Mini-digest: 9 responses]
Get the HTREEITEM of the item that was right clicked on
by calling HitTest()
Select an item by calling SelectItem().
randy_taylor@ebt.com
----------
> From: Irvin Waldman
> To: mfc-l@netcom.com
> Subject: Context menu support in CTreeView
> Date: Thursday, October 17, 1996 9:29 AM
>
> Environment: VC++ 4.2, Win 95
>
> Q: Context menu support in CTreeView
>
> I have wired context menu support in a CTreeView derived class. I
> then provided an override for NM_RCLICK in order to display the
pop-up
> menu. How do I obtain the right-clicked item since it seems that an
> item is only selected if a left-click is performed? Also, how do I
> obtain the coordinates e.g. CPoint, of the right-clicked item in
order
> to positioned the pop menu appropriately?
>
> Thanks in advanced.
>
> Irvin M. Waldman
> BMC Software, Inc.
-----From: "GoroKhM1"
Hi Irvin,
You wrote:
>How do I obtain the right-clicked item ...
The following code works fine in a Tree View:
class MyTree : public CTreeView
{
...
CTreeCtrl* m_pTree;
HTREEITEM m_hHitItem;
...
}
int MyTree::OnCreate(...)
{
...
m_pTree = &GetTreeCtrl();
}
void MyTree::OnRightClick(...)
{
...
CPoint cpClick(::GetMessagePos());
ScreenToClient(&cpClick);
UINT nFlags;
m_hHitItem = m_pTree->HitTest(cpClick, &nFlags);
...
}
If your are working with a Tree Control in a dialog box change
ScreenToClient(&cpClick);
to
m_pTree->ScreenToClient(&cpClick);
MarkG
-----From: Jim Barry
Here's how I do it:
void CMyDialog::OnRclickType(NMHDR* pNMHDR, LRESULT* pResult)
{
CTreeCtrl& tree = m_wndTree;
CPoint pt;
GetCursorPos(&pt);
tree.ScreenToClient(&pt);
UINT flags;
HTREEITEM hItem = tree.HitTest(pt, &flags);
if ((NULL == hItem) || !(TVHT_ONITEM & flags))
return;
// Select the item now, because the tree control doesn't
tree.SelectItem(hItem);
// Show the context menu
CMenu menu;
VERIFY(menu.LoadMenu(IDM_TREE));
GetCursorPos(&pt);
menu.GetSubMenu(0)->TrackPopupMenu(TPM_RIGHTBUTTON, pt.x, pt.y, this);
*pResult = 0;
}
Note that the SelectItem() call allows you to get the correct tree item
by calling CTreeCtrl::GetSelectedItem() in the handlers for the context
menu commands.
Hope this helps.
Jim Barry
Interactive Learning Productions
Newcastle upon Tyne, UK
http://www.ilp.com
-----From: Uma Shankar
Provide the override for WM_RBUTTONDOWN and you can obtain the item handle like
this
void CNavigationTreeView::OnRButtonDown(UINT nFlags, CPoint point)
{
// CNavigationTreeView is derived from CTreeView
// TODO: Add your message handler code here and/or call default
CTreeCtrl& pTree = GetTreeCtrl();
HITEM hItem = pTree.HitTest(point,NULL);
CTreeView::OnRButtonDown(nFlags,point);
}
-----From: David Little
Here is how I did it:
void CCourtCostTree::OnContextMenu(CWnd* pWnd, CPoint point)
{
CMenu hmenu;
CMenu* hmenuTrackPopup;
if ((hmenu.LoadMenu(IDR_POPUPMENU)) == NULL)
return;
hmenuTrackPopup = hmenu.GetSubMenu(0);
hmenuTrackPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON,
point.x, point.y, pWnd, NULL);
hmenu.DestroyMenu();
}
Hope this helps....
-----From: "Quang Ngo"
You should respond to WM_CONTEXTMENU rather than NM_RLICK. Under MFC,
when the user clicks Shift-F10 the message WM_CONTEXTMENU is sent to
the parent window of the focused control.
In your case, what you might want to do is intercept WM_CONTEXTMENU then
post it to the focused control.
ON_WM_CONTEXTMENU()
void CParentWnd::OnContextMenu(CWnd* pWnd, CPoint point)
{
pWnd->PostMessage(WM_CONTEXTMENU, (WPARAM)pWnd->m_hWnd,
MAKELPARAM(point.x, point.y));
}
Then, in your code for the tree view, do:
void CMyTree::OnContextMenu(CWnd* pWnd, CPoint ptScreen)
{
UINT flags;
HTREEITEM hHitItem;
/*---------------------------------------------------------------------
* Convert point
*-------------------------------------------------------------------*/
CPoint ptClient = ptScreen;
ScreenToClient(&ptClient);
/*---------------------------------------------------------------------
* Check if cursor hits any item
*-------------------------------------------------------------------*/
hHitItem = HitTest(ptClient, &flags);
if (hHitItem == NULL)
{
return;
}
/*---------------------------------------------------------------------
* Build the context menu based on the hit item
*-------------------------------------------------------------------*/
CMenu menu;
if (!BuildMyContextMenu(menu, hHitItem))
return;
/*---------------------------------------------------------------------
* Hilight the hit item
*-------------------------------------------------------------------*/
SetItemState(hHitItem, TVIS_DROPHILITED, TVIS_DROPHILITED);
/*---------------------------------------------------------------------
* Show context menu
* Note: Pass AfxGetMainWnd() to get the fly-by messages in the status
* bar.
*-------------------------------------------------------------------*/
menu.TrackPopupMenu(
TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON,
ptScreen.x,
ptScreen.y,
m_bOwnPopupMenus ? this : AfxGetMainWnd(),
NULL);
/*---------------------------------------------------------------------
* Unhilight the hit item
*-------------------------------------------------------------------*/
SetItemState(hHitItem, ~TVIS_DROPHILITED, TVIS_DROPHILITED);
/*---------------------------------------------------------------------
* Destroy the menu
*-------------------------------------------------------------------*/
menu.DestroyMenu();
}
NOTE: Under Windows NT 4.0 you don't need to PostMessage WM_CONTEXTMENU
from the parent
window and the focused control will still get it. However, under Windows
95 and
Windows NT 3.5x you need to PostMessage it. If you double-click the right
button
you'll get WM_CONTEXTMENU and won't with a single click. Moreover, a
CTreeCtrl
control won't get WM_RBUTTONUP when user single right-click.
-Quang
-----From: IT GmbH <100031.1400@CompuServe.COM>
>> How do I obtain the right-clicked item since it seems that an item is only
selected if a left-click is performed?
You can use GetTreeCtrl().GetDropHighlightItem to determine the right-clicked
item.
>> Also, how do I obtain the coordinates e.g. CPoint, of the right-clicked item
in order to positioned the pop menu appropriately?
Use ::GetCursorPos()
[Moderator's note: Actually, this is a bad idea because the cursor may have
already moved. As discussed previously in this list, you should use
GetMessagePos() instead.]
-----From: "Ferguson, Jeff"
Hello Irvin,
First, call CTreeView::GetTreeCtrl() to get a reference to the view's
underlying tree control. You can then call CTreeCtrl::HitTest(), which
will return an HTREEITEM found at the supplied CPoint. In your case, this
will be the point at which the right mouse button was clicked.
You should be able to use GetMessagePos() to find out what the mouse
coordinates of the message were.
There are two forms of CTreeCtrl::HitTest(). See Books Online for more
information.
Regards,
-- Jeff Ferguson [JeffF@twgi.com]
The Weidt Group, Inc. [http://www.twgi.com]
-----From: "Peter Friis"
Here is the NM_RCLICK handler from my CTreeCtrl-derived class:
void TreeCtrl::OnRclick(NMHDR*,LRESULT*){
CPoint p(GetMessagePos()); ScreenToClient(&p);
UINT flags;
HTREEITEM h=HitTest(p,&flags);
if(h) VERIFY(SelectItem(h)); // do this to select the right-clicked item
doPopup(h,p,flags); // creates & tracks the context menu
}
I think this answers both your questions. Hope it works for you.
_______________________________________________________________
Peter Friis, Software Engineer peterf@wallchart.com
Wallchart International Ltd http://www.wallchart.com
+44 (1403) 275321 Fax: +44 (1403) 275322
_______________________________________________________________
Dominique Letrйsor -- isambert@mail.telepac.pt
Saturday, October 19, 1996
[Mini-digest: 2 responses]
Irvin Waldman wrote:
>
> Environment: VC++ 4.2, Win 95
>
> Q: Context menu support in CTreeView
>
> I have wired context menu support in a CTreeView derived class. I
> then provided an override for NM_RCLICK in order to display the pop-up
> menu. How do I obtain the right-clicked item since it seems that an
> item is only selected if a left-click is performed? Also, how do I
> obtain the coordinates e.g. CPoint, of the right-clicked item in order
> to positioned the pop menu appropriately?
>
> Thanks in advanced.
>
> Irvin M. Waldman
> BMC Software, Inc.
In your OnRclick derivation, you can use the TreeCtrl HitTest member
function. As to get the coordinate of the mouse click, the API function
::GetCurPos () will do it. Here is an example:
UINT flags = TVHT_ONITEM;
CPoint point;
HTREEITEM hItem;
GetCursorPos (&point);
ScreenToClient (&point);
if ((hItem = GetTreeCtrl ().HitTest(point, &flags)) != NULL)
{
}
-----From: "Michael A. Harris"
I believe that I have done what you are asking in an app that I am
currently working on. I have included the code that I used to do the
job. Hope it helps.
The following code stores the CPoint of the right click position in a
private member variable of the view.
//////////////////////////////////////////////////////////////////////////////////////////////////////////
void CSummaryView::OnRButtonDown(UINT nFlags, CPoint point)
{
// Store the current CPoint for which the user right clicked in a
private
// member variable (MousePos) for future use.
MousePos = point;
CTreeView::OnRButtonDown(nFlags, point);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
The following code demonstates how I obtain the right clicked item in a
CTreeCtrl irregardless of which item is currently selected:
//////////////////////////////////////////////////////////////////////////////////////////////////////////
void CSummaryView::OnRclick(NMHDR* pNMHDR, LRESULT* pResult)
{
CMenu m_Menu;
HTREEITEM m_TreeItem;
UINT pInt = UINT(TVHT_ONITEM);
int m_Image,m_SelectedImage;
// Test if the point at which the user right clicked is on a valid tree
item.
m_TreeItem = GetTreeCtrl().HitTest(MousePos,&pInt);
// If m_TreeItem is NULL then the click was not on a valid tree item so
ignore.
if ((m_TreeItem != NULL))
{
// Get the image(s) associated with the right clicked tree item.
if (GetTreeCtrl().GetItemImage(m_TreeItem, m_Image, m_SelectedImage));
{
// Store the right clicked tree item in a private member variable for
// future use.
m_RClickedNode = m_TreeItem;
ClientToScreen(&MousePos);
// Determine which type of node the user right clicked on and load the
// appropriate popup menu.
if (m_Image <= NODE1_IMG)
{
VERIFY(m_Menu.LoadMenu(IDR_NODE1MENU));
CMenu* pPopup = m_Menu.GetSubMenu(0);
ASSERT(pPopup != NULL);
pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON,
MousePos.x, MousePos.y, this);
}
else if (m_Image <= NODE2_IMG)
{
VERIFY(m_Menu.LoadMenu(IDR_NODE2MENU));
CMenu* pPopup = m_Menu.GetSubMenu(0);
ASSERT(pPopup != NULL);
pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON,
MousePos.x, MousePos.y, this);
}
else
{
// Item selected is not one which we are interested in so
// do something else.
}
}
}
*pResult = 0;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
Have a good day,
Michael A. Harris
Jim Barry -- Jim.Barry@ilp.com
Tuesday, October 22, 1996
[Mini-digest: 3 responses]
[Moderator's note: Two of the responses use GetCursorPos(), which
I'm pretty sure is a bad idea because the cursor can change position
after the message has been put into the queue. You should use
GetMessagePos() whenever you need to know where the cursor was when
the message came through. This has been discussed here several
times in the past and should probably be mentioned in the FAQ.]
>Environment: VC++ 4.2, Win 95
>
>Q: Context menu support in CTreeView
>
>I have wired context menu support in a CTreeView derived class. I
>then provided an override for NM_RCLICK in order to display the pop-up
>menu. How do I obtain the right-clicked item since it seems that an
>item is only selected if a left-click is performed? Also, how do I
>obtain the coordinates e.g. CPoint, of the right-clicked item in order
>to positioned the pop menu appropriately?
In my previous posting, I had not considered the WM_CONTEXTMENU message,
which a child window's DefWindowProc sends to the parent window following
a right button up-click. If the menu key on a Microsoft Natural Keyboard
is pressed, a WM_CONTEXTMENU message is sent with lParam set to -1. Here
is how my code now looks, if anyone's interested.
void CMyDialog::OnRclickTree(NMHDR* pNMHDR, LRESULT* pResult)
{
// Do a hit test
CTreeCtrl& tree = m_wndTree;
CPoint pt;
GetCursorPos(&pt);
tree.ScreenToClient(&pt);
UINT flags;
HTREEITEM hItem = tree.HitTest(pt, &flags);
if ((NULL == hItem) || !(TVHT_ONITEM & flags))
return;
// Select the item now, coz the tree control won't do it
tree.SelectItem(hItem);
*pResult = 0;
}
void CMyDialog::OnContextMenu(CWnd* pWnd, CPoint point)
{
CTreeCtrl& tree = m_wndTree;
if (pWnd == &tree)
{
HTREEITEM hItem;
// Is click inside tree control?
CPoint pt(point); tree.ScreenToClient(&pt);
CRect rect; tree.GetClientRect(rect);
if (rect.PtInRect(pt))
{
// Inside control - hit test for item
UINT flags;
hItem = tree.HitTest(pt, &flags);
if ((NULL == hItem) || !(TVHT_ONITEM & flags))
return;
}
else
{
// Outside control - choose selected item
hItem = tree.GetSelectedItem();
if (NULL == hItem)
return;
// Set menu coords to be at centre of selected item
tree.GetItemRect(hItem, rect, TRUE);
point = rect.CenterPoint();
tree.ClientToScreen(&point);
}
ASSERT(hItem);
// Show the context menu
CMenu menu;
VERIFY(menu.LoadMenu(IDM_TREE));
menu.GetSubMenu(0)->TrackPopupMenu(TPM_RIGHTBUTTON,
point.x, point.y, this);
}
}
Works OK on Win32s too.
Jim Barry
Interactive Learning Productions
Newcastle upon Tyne, UK
http://www.ilp.com
-----From: "Dan Maher"
A better way to do this might be to use the GetMessagePos() function since
it retrieves the mouse position at the time the message was sent, which is
not necessarily the same as when you process the message.
void CMyTreeView::OnRClick( NMHDR* pNMHDR, LRESULT* pResult) {
*pResult = 0;
UINT dwPos = GetMessagePos();
CPoint pointScreen;
// GetMessagePos returns mouse position in SCREEN coordinates
pointScreen.x = LOWORD (dwPos);
pointScreen.y = HIWORD (dwPos);
}
dM
-----From: "Thomas Schall"
Hi Irvin,
I just had the same problem. There is no great deal about solving the
problem.
My class CRSYDBBrowserView is derived from CTreeView. Just follow the code
provided.
My comment at the beginning of OnRClick() is only estimated! and not
prooved.
Hope thid helps.
void CRSYDBBrowserView::OnContextMenu(CWnd* pWnd, CPoint point)
{
// Context menu has been avtivated
HTREEITEM hItem;
CMenu DBContextMenu;
CPoint localPoint = point;
CTreeCtrl& tree = GetTreeCtrl();
ScreenToClient(&localPoint);
if((hItem = tree.HitTest(localPoint)) == NULL)
return;
//pFrame = (CRSYMainWnd *)AfxGetApp()->m_pMainWnd;
// Further version will provide user defined commands
static char *szDBItemCmds[6] = {
"leaves only", "nodes only", "depth", "reverse", "DLI", "STR" };
DBContextMenu.CreatePopupMenu();
// Add menu items
for(int i=0; i<6; i++)
{
DBContextMenu.AppendMenu(MF_STRING, 0, szDBItemCmds[i]);
}
// Show popup menu
DBContextMenu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x,
point.y,
this);
TRACE("*** popup DB menu over item %s\n", tree.GetItemText(hItem));
}
void CRSYDBBrowserView::OnRClick(NMHDR* pNMHDR, LRESULT* pResult)
{
CPoint point;
// We need this explicit redirection, because otherwise the popup menu
only
// will be drawn clicking twice the right mouse. Maybe this is due to the
// reflection of the NM_RCLICK message.
// Get current cursor position
GetCursorPos(LPPOINT(&point));
// Redirect right click to member function OnContextMenu()
OnContextMenu(this, point);
*pResult = 0;
}
Feel free to remail, if any problems occur.
Thomas
------------------------
Dipl.-Ing. Thomas Schall
ICA-CSV EMail: schall@csv.ica.uni-stuttgart.de
Universitaet Stuttgart
Pfaffenwaldring 27 Tel. : +49 (0) 711 / 685 3756
70569 Stuttgart, Germany Fax : +49 (0) 711 / 685 3758
| Вернуться в корень Архива
|