tuoyue 发表于 2004-10-22 17:05:00

[原创]ObjectARX中右键(快捷)菜单的实现方法

<A href="http://www.863s.com/list.asp?unid=126" target="_blank" >http://www.863s.com/list.asp?unid=126</A>

qqwwwee 发表于 2004-11-22 15:50:00

<CENTER class=aTitle>[原创]ObjectARX中右键(快捷)菜单的实现方法</CENTER>
<TABLE width="100%">
<TBODY>
<TR>
<TD width=600></TD>
<TD>作者:3STech <BR></TD></TR></TBODY></TABLE>          网上看到好像网友问到在AutoCAD窗口中实现右键菜单的方法,觉得这种技术很实用,在许多开发应用中都能用得到,所以就写了这篇文章,以供大家参考。由于本人水平有限,不对之处敬请原谅。
<P align=left>  据本人理解,右键菜单,也叫快捷菜单,在Windows编程中叫上下文(context)菜单。ObjectARX本身提供了一套处理上下文菜单的机制。<SPAN class=H1Overlay>在ObjectARX类库中有一个名为AcEdUIContext的类,此类负责在ObjectARX应用中的上下文菜单中添加自己的菜单项,而原菜单项不会被破坏,这也是此种方法的优点之一。用AcEdUIContext类添加菜单时,菜单项的数目没有限制,但必须是文本菜单。菜单可以层迭,但不允许使用键盘加速键,不能够在状态行显示快捷菜单命令状态提示。此类可以处理三种情况下的上下文菜单:一个默认上下文菜单,二是实体对象上下文菜单,三是命令执行时上下文菜单。虽然菜单出现的时机不同,但方法基本相同,它们之间主要的不同是所用的加载和卸载函数不同。下面加以详细介绍。


<P align=left>  在AcEdUIContext为中包含了三个重要的成员函数,他们分别是:


<P align=left>                       (1) AutoCAD系统获取快捷菜单句柄函数<BR>  virtual void * getMenuContext(const AcRxClass * unnamed,const AcDbObjectIdArray&amp; unnamed) = 0;<BR>  其中,第一个参数unnamed 是当前所选择的实体的对象句柄,第二个参数unnamed是所选实体的实体ID数组。这两个参数只有在实体对象上下文菜单中有效。


<P align=left>                       (2) 菜单项命令事件响应函数<BR>  virtual void onCommand(Adesk::UInt32 unnamed) = 0;<BR>  其中,unnamed是相应菜单项的菜单ID。此函数在用户选择执行快捷菜单中的某个菜单项时被调用。


<P align=left>  (3) 菜单更新函数<BR>  virtual void OnUpdateMenu();<BR>  AutoCAD在快捷菜单弹出之前调用此函数。相当于MFC中的菜单更新事件,我们可以在这个函数中改变菜单项的检查状态或使能菜单项等。


<P align=left>  其实,我们利用ObjectARX实现上下文菜单要做的工作主要是重载并填写这几个AcEdUIContext成员函数,其操作方法如下:


<P align=left>  首先,我们从AcEdUIContext类派生一个自己的类,名字就叫CDefaultContextMenu吧,当然,你可以按自己的喜好起名字了:-)。然后,在派生的类中重载以上三个函数。


<BLOCKQUOTE>
<P align=left>class CDefaultContextMenu: public AcEdUIContext<BR>{<BR>public:<BR>                       CDefaultContextMenu();<BR>                       ~CDefaultContextMenu();<BR><BR>                       // 如下重载以下三个函数<BR>                       virtual void* getMenuContext(const AcRxClass *pClass, const AcDbObjectIdArray&amp; ids) ;<BR>                       virtual void onCommand(Adesk::UInt32 cmdIndex);<BR>                       virtual void OnUpdateMenu();<BR><BR>private:<BR>                       CMenu *m_pDemoMenu; // 用来增加菜单项的MFC菜单对象,使用它是为了加载我们在VC中增加的菜单资源。<BR>                       HMENU m_hDemoMenu;       // 菜单项所对应的句柄,这才是我们真正要加载的的菜单项,它是m_pDemoMenu中的一项。<BR>};

</BLOCKQUOTE>
<P align=left>  接下来我们需要做的是:(1)在构造函数中加载菜单资源;(2)在getMenuContext函数中添加显示我们自己菜单项的代码;(3)在onCommand函数中处理命令执行代码;(4)在OnUpdateMenu中修改菜单项的显示状态(此步可选可不选);(5)在析构函数中卸载资源。


<P align=left>  <FONT color=#000080>--在构造函数中加载菜单资源</FONT><BR><FONT color=#808080>  acDocManager-&gt;pushResourceHandle(_hdllInstance); // 切换当前使用的资源,千万不要忘记加上吆!_hdllInstance是模块<BR>                                                                                                                                                                                                                                                                                                                                                                                                                               //实例指针,通过extern引用到使用的位置就可以了。<BR>                       m_pDemoMenu= new CMenu;                                                                                                                                                                                                       // 创建一个菜单对象<BR>                       m_pDemoMenu-&gt;LoadMenu(IDR_DEMO_DEFAULT_MENU);                       // 使用创建的菜单对象加载在资源编辑器中编辑好的资源<BR>                       acDocManager-&gt;popResourceHandle();                                                                                                               // 再把资源切换回来吧!</FONT>


<P align=left>                       <FONT color=#000080>--在getMenuContext函数中添加显示我们自己菜单项的代码</FONT><BR>        <FONT color=#808080>               m_hDemoMenu= m_pDemoMenu-&gt;GetSubMenu(0)-&gt;GetSafeHmenu(); // 这里我们就显示已经加载的菜单(m_pDemoMenu)中的第一个子菜单吧!<BR>                       return &amp;m_hDemoMenu; // 返回子菜单对象的句柄</FONT>


<P align=left><FONT color=#808080>                       </FONT><FONT color=#000080>--在onCommand函数中处理命令执行代码<BR></FONT><FONT color=#808080>                       acDocManager-&gt;pushResourceHandle(_hdllInstance);                                                                                               // 切换当前使用的资源<BR><BR>                       CString strMenuTitle, strPrompt;<BR>                       m_pMenu-&gt;GetMenuString(cmdIndex,strMenuTitle,MF_BYCOMMAND);       // 获取一所选菜单项的文本标题<BR>                       strPrompt.Format("\n您已经选取了菜单:%s\n",strMenuTitle);<BR>                       acutPrintf(strPrompt);                                                                                                                                                                                                                                                                                                               // 我们的例子显示哪一个菜单项被选择<BR>                       acedPostCommandPrompt();                                                                                                                                                                                                                                                                                               // 显示命令提示<BR><BR>                       acDocManager-&gt;popResourceHandle();                                                                                                                                                                                                               // 将资源切换回来</FONT>


<P align=left>                       <FONT color=#000080>--在OnUpdateMenu中修改菜单项的显示状态<BR>                       </FONT><FONT color=#808080>m_pDemoMenu-&gt;EnableMenuItem(IDR_DEMO_DEFAULT_MENU_ITEM1,MF_GRAYED);                                                                                                                       // 使菜单变灰<BR>                       m_pDemoMenu-&gt;EnableMenuItem(IDR_DEMO_DEFAULT_MENU_ITEM2,MF_ENABLED);                                                                                                               // 使能菜单项<BR>                       m_pDemoMenu-&gt;CheckMenuItem(IDR_DEMO_DEFAULT_MENU_ITEM3, MF_BYCOMMAND|MF_CHECKED);       // 复选菜单项 </FONT>


<P align=left><FONT color=#808080>                       </FONT><FONT color=#000080>--在析构函数中卸载资源<BR>                       </FONT><FONT color=#808080>if (m_pMenu) delete m_pMenu;       // 不释放的话麻烦可大呀!!!</FONT><BR>       


<P align=left>  以上用默认上下文菜单为例对AcEdUIContext类的使用方法作了阐述,至于其他两种基本是与默认上下文菜单相同。只不过在实体对象上下文菜单中的getMenuContext函数中可以对所选的实体进行响应,因为我们可以通过getMenuContext函数的参数获取实体对象。


<P align=left>  有了以上的准备工作,我们就可以按照菜单显示时机加载不同种类的菜单了。


<P align=left>  <FONT color=#000080>--</FONT><FONT color=#000080>首先,要行声明一个全局的上下文菜单对象,如下:</FONT><BR>  <FONT color=#808080>CDefaultContextMenu *gpDefDemoCM; // 默认上下文菜单<BR>                       CEntityContextMenu *gpEntDemoCM;       // 实体对象上下文菜单<BR>                       CCmdContextMenu *gpCmdDemoCM;                               // 命令时上下文菜单</FONT>


<P align=left>                       <FONT color=#000080>--</FONT><FONT color=#000080>然后,在初始化ARX应用时创建并加载上下文菜单对象。</FONT><BR><FONT color=#808080>  gpDefDemoCM = new CDefaultContextMenu; // 创建默认上下文菜单<BR>                       gpEntDemoCM = new CEntityContextMenu;       // 创建实体对象上下文菜单<BR>                       gpCmdDemoCM = new CCmdContextMenu;                               // 创建命令时上下文菜单</FONT><BR><BR>                       <FONT color=#808080>acDocManager-&gt;pushResourceHandle(_hdllInstance);                       // 切换当前使用的资源<BR><BR>                       acedAddDefaultContextMenu(</FONT><FONT color=#808080>gpDefDemoCM</FONT><FONT color=#808080>, pAppID);                                                                                                                               // 向AutoCAD应用中添加</FONT><FONT color=#808080>默认上下文菜单</FONT>


<P align=left><FONT color=#808080>  acedAddObjectContextMenu(AcDbEntity::desc(), gpEntDemoCM , pAppID); // </FONT><FONT color=#808080>        向AutoCAD应用中添加实体对象上下文菜单<BR><BR></FONT><FONT color=#808080>                       // </FONT><FONT color=#808080>向AutoCAD应用中添加</FONT><FONT color=#808080>命令时上下文菜单<BR>                       </FONT><FONT color=#808080>// myCmd是一个命令函数。第一个参数是命令组名,第二个参数是全局命令名,<BR>                       // 第三个参数是本地命令名,第四个参数是命令模式,第5和6个参数就不用说了,大家应该明白了。:-0<BR>                       acedRegCmds-&gt;addCommand("MyGrp", "MyDemo", "MyDemo", ACRX_CMD_MODAL, &amp;myCmd, </FONT><FONT color=#808080>gpCmdDemoCM </FONT><FONT color=#808080>); <BR><BR>                       acDocManager-&gt;popResourceHandle(); // 切换回资源<BR><BR>  </FONT>说明:pAppID是acrxEntryPoint(AcRx::AppMsgCode msg, void* pkt)中是第二个参数。acedAddObjectContextMenu中的第一个参数根据实体的不同而不同,如对于线实体则为AcDbLine::desc()等。<FONT color=#808080><BR><BR>                       </FONT><FONT color=#000080>--</FONT><FONT color=#000080>最后,在卸载ARX应用时,将加载的上下文菜单对象移除,并释放内存空间。<BR> </FONT><FONT color=#808080> HINSTANCE hInst = AfxGetResourceHandle(); // 保证资源正确<BR>                       AfxSetResourceHandle(_hdllInstance);<BR><BR>                       acedRemoveDefaultContextMenu(gpDefDemoCM </FONT><FONT color=#808080>); // 移除默认上下文菜单<BR>                       acedRemoveObjectContextMenu(AcDbEntity::desc(), </FONT><FONT color=#808080>gpEntDemoCM </FONT><FONT color=#808080>); // 移除实体对象上下文菜单<BR>                       acedRegCmds-&gt;removeGroup("MyGrp</FONT><FONT color=#808080>"); // 移除命令组"MyGrp"<BR><BR>                       delete gpDefDemoCM;<BR>                       delete gpEntDemoCM;<BR>                       delete gpCmdDemoCM;<BR><BR>                       AfxSetResourceHandle(hInst);</FONT>


<P align=left><BR><FONT color=#808080>       </FONT>       到此为止,我们的所有代码都已经编写完毕了,不过你别忘记了创建菜单资源吆!不知道你是否已经掌握了呢?如有问题的话欢迎在本站(<A href="http://863s.com/" target="_blank" >http://www.863s.com</A>)发帖询问。如果有什么意见或建议呢,也希望您给予批评指正。

</SPAN>

freejustin 发表于 2005-4-25 17:37:00

请问,有人依照本例实现功能?我在右键时只多出一条分割线,不知道问题出在那儿?


恳请大家指点

wangws2005 发表于 2005-5-3 11:50:00

试试看!

树袋熊 发表于 2005-5-9 00:31:00

太晚了,天亮再试
页: [1]
查看完整版本: [原创]ObjectARX中右键(快捷)菜单的实现方法