第六章--PW菜单介绍


6、菜单

PW开发能够实现向ProjectWise Explorer中添加新菜单的功能,比如我们打算向Document(文件)菜单下面添加一个新的一级子菜单,再比如我们打算向Document\New菜单下面添加一个新的二级子菜单,再或者向其他菜单下添加更深层次的子菜单。

在这些新菜单下,我们可以实现自己想要的功能,来达到拓展ProjectWise Explorer的目的。通过添加新菜单来添加新功能是我们最常见的开发PW的方式,在这一章我们将给大家介绍如何添加新菜单并且关联与之对应的功能。

6.1、添加静态菜单

在Pw中添加新菜单时我们可以添加静态菜单,何谓静态菜单?简而言之,静态菜单是通过菜单编辑器(Menu Edit)事先创建完成(需要一个dll提供菜单的响应函数),然后保存为以.mrr为后缀的文件,最后将该.mrr文件和包含菜单响应函数的dll一起放到ProjectWise Explorer的运行目录下。

接下来我们按照上面图中的流程,分三步学习如何在ProjectWise上添加一个静态菜单。

步骤一:首先我们按照1.1章节介绍的内容新建一个叫做StudyPwStaticMenu的工程,然后添加头文件及其路径、静态库及其路径、改变生成dll的路径和添加入口函数。由于我们的静态菜单还将要调用该动态库中的某个响应函数,所以还需创建一个响应函数,内容如下:

extern "C" void StaticMenu()
{
	AfxMessageBox(L"静态菜单添加成功!");
}

为了让外部程序能够调用该响应函数,我们应该在项目上午一个.def文件中添加该函数的输出表示,内容如下:

EXPORTS
    ; Explicit exports can go here
	CustomInitialize
	StaticMenu

接着我们使用Custom Module Manager X86工具将我们生成的dll加入到ProjectWise Explorer中。

步骤二:打开一个叫做Menu Editor的编辑工具,初始界面如下图所示:

使用该软件首先点击工具栏中的“New”按钮新建一个.mrr文件(点击后可能不会有任何明显的反馈,但是必须点击一下“New”),然后在左侧空白处右键弹出工具菜单,选择“Add”,就会在空白处出现一个主菜单,这时我们在右侧Command Type下拉框出选择“PullDown Menu”,选中后其他下拉框就会被激活。我们在Unique Item Identifier处填入一个唯一的标识符,在Display Name处填入一个显示的菜单名字。如果是主菜单,填入这三项就完成了。

添加完主菜单后,右键左侧我们新建的主菜单“一步一步学习PW”,选择”Add”功能,在该主菜单下面新建一个一级子菜单,在右侧Command Type下拉框中选择“Generic Command”,选中后其他选项就会被激活,此时我们在Unique Item Identifier处填写唯一的标识符,在Display Name处填写显示的菜单名字,在DLL Name处填写我们刚刚创建的动态库的名字,在Function Name处填写该动态库中我们应该调用的响应函数的名字,在Reguired Mask处勾选Valid Login表示只有当用户有效登录后才显示出来该菜单。

步骤三:设计完成后,点击工具栏中的Save,将该mrr文件保存到ProjectWise安装目录下即可。如果我们第一步创建的dll没有保存到安装目录下,也需要手动保存到。

此时我们运行ProjectWise Explorer软件,在启动之前会弹出下图的对话框,表示我们创建的StudyPwStaticMenu.dll成功的嵌入到了Explorer中,点击“OK”继续

软件启动并成功登录某个数据源后,在主菜单栏我们会发现我们创建的”一步一步学习PW”主菜单,在该菜单下有一个“静态菜单”的一级子菜单,点击该子菜单,会弹出如下我们所要它弹出的内容,至此,一个Pw静态菜单添加成功。

6.2、添加动态菜单

在Pw中添加新菜单时我们可以添加动态菜单,何谓动态菜单?相对静态菜单需要通过Menu Editor事先设计好菜单结构,然后在与dll中的某个响应函数关联的操作,动态菜单不管是菜单结构的设计还是与响应函数的关联都是在dll中完成的。

相对静态菜单,添加动态菜单时,我们首先需要知道要在哪个主菜单下面进行添加,换句话说就是,动态菜单只能是某个主菜单下的子菜单,不能通过使用动态菜单的方式添加主菜单,此时还需要我们判断具体该子菜单是在指定主菜单下的哪个位置,是一个一级子菜单还是某个一级子菜单的二级子菜单,或者更深层次的子菜单;接下来是构造我们想要添加的菜单信息,包括菜单唯一标识符、菜单名称、菜单提示和菜单的响应函数等;最后我们需要完善菜单响应函数的内容,在其中实现自己的业务逻辑。

接下来的例子中,我们将在Document(文档)主菜单下,添加一个新的一级子菜单,该子菜单的名字为“Advanced”,接着在新建的一级子菜单下面创建两个二级子菜单,分别为“Set Final Status”和“Remove Final Status”,表示对文档进行设置最终状态和移除最终状态。下面我们详细介绍一下具体内容。

步骤一:首先我们按照1.1章节的介绍的内容新建一个叫做StudyPwDynamicMenu的工程,然后添加头文件及其路径、静态库及其路径、改变生成dll的路径和添加入口函数CustomInitialize的基本内容。

extern "C" LONG WINAPI CustomInitialize
	(
	ULONG   ulMask,     // i Application Mask
	LPVOID  lpReserved  // i Reserved (must be NULL)
	)
{
	static BOOL s_bLoaded = FALSE;

	if (s_bLoaded)
		return IDOK;
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	MessageBox(NULL, L"我们自己创建PW Dynamic Menu DLL项目已经嵌入进了ProjectWise Explorer客户端", L"一步一步学习PW开发", MB_OK | MB_ICONINFORMATION );
	s_bLoaded = TRUE;
	return IDOK;
}	

步骤二:我们在StudyPwDynamicMenu.h文件中,给类CStudyPwDynamicMenuApp添加一个public函数,函数原型为:VOID InitMenu(AAPROC_EXECCMD lpCallback),它接受一个回调 函数,返回值是VOID。添加一个Protected函数,函数原型为:BOOL InitMenu(AAPROC_EXECCMD lpCallback),它接受一个回调函数,返回值是BOOL。然后在函数InitMenu()的实现中添加调用函数InitAdvancedMenu()的方法,内容如下:

VOID CStudyPwDynamicMenuApp::InitMenu( AAPROC_EXECCMD lpCallback )
{
	InitAdvancedMenu(lpCallback);
}

在函数InitAdvancedMenu()的实现中添加创建Document(文件)下面一级子菜单和二级子菜单的代码,内容如下:

BOOL CStudyPwDynamicMenuApp::InitAdvancedMenu

                (

                AAPROC_EXECCMD lpCallback

                )

{

                AADMSMENUITEM aaMenuItem;

                aaMenuItem.ulMask = AADMSMIF_FLAGS | AADMSMIF_CMDTYPE;

                aaMenuItem.uiFlags     = AAMIF_SEPARATOR;

                aaMenuItem.uiCmdType   = AAMENU_DOCUMENT;

                aaApi_AddCustomMenuItem(0, AADMSMI_LAST, &aaMenuItem);

 

 

                aaMenuItem.ulMask = AADMSMIF_FLAGS | AADMSMIF_NAME | AADMSMIF_CMDTYPE;

                aaMenuItem.uiFlags     = AAMIF_POPUP;

                aaMenuItem.uiCmdType   = AAMENU_DOCUMENT;

                aaMenuItem.lptstrName   = L"Advanced";

                ULONG hAdvancedMenu;

                hAdvancedMenu = aaApi_AddCustomMenuItem(0, AADMSMI_LAST, &aaMenuItem);

 

                if (!hAdvancedMenu)

                {

                                return FALSE;

                }

 

                m_uiSetFinalCmdId = aaApi_ReserveCommandIdByType(AAMENU_DOCUMENT);

 

                aaMenuItem.ulMask = AADMSMIF_FLAGS | AADMSMIF_NAME | AADMSMIF_CMDTYPE | AADMSMIF_PROMPT |

                                AADMSMIF_CMDID | AADMSMIF_REQUIRED | AADMSMIF_CALLBACK |

                                AADMSMIF_REQUIREDS;

                aaMenuItem.uiFlags     = AAMIF_COMMAND;

                aaMenuItem.uiCmdType   = AAMENU_DOCUMENT;

                aaMenuItem.lptstrName   = L"Set Final Status";

                aaMenuItem.uiCommandId = m_uiSetFinalCmdId;

                aaMenuItem.ulRequiredMask = AAMF_SEL_NONFINAL;

                aaMenuItem.ulRequiredSMask = AAMSF_ANY_DOCUMENT;

                aaMenuItem.fpExecute = lpCallback;

                aaMenuItem.ushParamType = AACMDPT_DOCUMENT;

 

                if (!aaApi_AddCustomMenuItem(hAdvancedMenu, AADMSMI_LAST, &aaMenuItem))

                {

                                return FALSE;

                }

 

                m_uiRemoveFinalCmdId = aaApi_ReserveCommandIdByType(AAMENU_DOCUMENT);

                aaMenuItem.lptstrName   = L"Remove Final Status";;

                aaMenuItem.uiCommandId = m_uiRemoveFinalCmdId;

                aaMenuItem.ulRequiredMask = AAMF_SEL_FINAL;

 

                if (!aaApi_AddCustomMenuItem(hAdvancedMenu, AADMSMI_LAST, &aaMenuItem))

                {

                                return FALSE;

                }

 

                return TRUE;

}

在这部分代码中,我们主要介绍两个函数:获取指定类型可用标识符的函数aaApi_ReserveCommandIdByType()和往指定位置添加菜单的函数aaApi_AddCustomMenuItem()。调用函数aaApi_ReserveCommandIdByType()时,它的唯一参数是一个主菜单的类型,返回值是该类型主菜单下唯一且有效的子菜单标识符;调用函数aaApi_AddCustomMenuItem()时,它第一第二个参数表示我们将要添加的菜单的位置,第三个参数表示我们将要添加的菜单的内容。详细函数介绍参考帮助文档。

步骤三:我们在StudyPwDynamicMenu.h文件中,给类CStudyPwDynamicMenuApp添加一个public函数,函数原型为:LONG ExecuteCommand(LPAACMDPARAM lpCommand)。该函数的参数记录菜单操作对象的信息,返回值是一个LONG类型,根据处理的结果返回相应数值。该函数的实现我们一般这样写:

LONG CStudyPwDynamicMenuApp::ExecuteCommand

                (

                LPAACMDPARAM lpCommand

                )

{

                LONG lItems;

                HDOCCMDPARAM hDocParam;

                LPAADOC_ITEM lpDocItem;

                if (!lpCommand)

                {

                                return AAERR_PARAMETER;

                }

                if (lpCommand->lCommandType != AAMENU_DOCUMENT ||

                                lpCommand->ushParamType != AACMDPT_DOCUMENT)

                {

                                return AAAPI_EXECCMD_DO_DEFAULT;

                }

                hDocParam = *((HDOCCMDPARAM*)lpCommand->lpParam);

 

                lItems = aaApi_GetDocCmdParamCount(AADOCCMDPT_DOCUMENT1, hDocParam);

                lpDocItem = (LPAADOC_ITEM)aaApi_GetDocCmdParamElements(AADOCCMDPT_DOCUMENT1, hDocParam);

 

                if (!lpDocItem)

                {

                                return AAAPI_EXECCMD_DO_DEFAULT;

                }

                if (lpCommand->lCommandId == (LONG)m_uiSetFinalCmdId)

                {

                                aaApi_ExecuteDocumentCommand(IDMD_SET_FINAL, lItems, lpDocItem);

                                return 0;

                }

                if (lpCommand->lCommandId == (LONG)m_uiRemoveFinalCmdId)

                {

                                aaApi_ExecuteDocumentCommand(IDMD_REMOVE_FINAL, lItems, lpDocItem);

                       return 0;

                }

                if (lItems != 1)

                {

                                return AAAPI_EXECCMD_DO_DEFAULT;

                }

                return 0;

}

在这部分代码中,我们利用函数aaApi_GetDocCmdParamCount()和函数aaApi_GetDocCmdParamElements()从参数lpCommand中获取菜单操作对象的详细信息,然后利用菜单的唯一标识符m_uiSetFinalCmdId和m_uiRemoveFinalCmdId,对不同的菜单执行不同的操作。

关于添加动态菜单的详细代码案例,可参考PW SDK中的mymenu功能例子,里面有详细的代码流程。