mGNCS(miniStudio新控件集)提供nscCreateModalDialogFromID
函数用于从资源模板创建模式对话框,但是对于miniStudio生成的对话框模板,使用nscCreateModalDialogFromID
函数创建模式对话框是无效的,下面是nscCreateModalDialogFromID
的实例代码:
int ncsCreateModalDialogFromID (HPACKAGE package, Uint32 dlgId,
HWND owner, HICON hIcon, HMENU hMenu,
NCS_EVENT_HANDLER_INFO* handlers, NCS_EVENT_CONNECT_INFO* connects)
{
int ret = 0;
// ncsCreateMainWindowIndirectFromID根据模板中的class名字来创建对应的实例,
// 而miniStudio生成的模板class属性是mainwnd,
// 所以这里ncsCreateMainWindowIndirectFromID返回的对象是'mMainWnd',
// 'mMainWnd'不是'mDialogBox'的子类,所以不能转为'mDialogBox',所以这里的返回值为NULL.
mDialogBox * dialog = SAFE_CAST(mDialogBox,
ncsCreateMainWindowIndirectFromID(package, dlgId, owner, hIcon, hMenu, handlers, connects, 0));
// dialog 为 NULL,所以 这里不会执行doModal方法,就直接返回了,对话框的表现就像一个普通窗口一样,不是模式的。
if (dialog) {
ret = _c(dialog)->doModal(dialog, TRUE);
MainWindowThreadCleanup(dialog->hwnd);
}
return ret;
}
这应该算是mGNCS
的一个bug。要解决这个问题,首先要让miniStuio生成的对话框模板有正确的class属性。
Class 属性
minigui提供的miniStudio (1.2.1)还没有做完善,所以在miniStudio中只能生成class类型为mainwnd
的标准窗口,因为如下图没有办法在gui界面中修改Class属性
如果想生成 一个对话框类型(dialogbox)的模板,该怎么办呢?经过多次尝试,找到解决办法,就是直接修改资源模板的xml文件。如下图打开模板对应的xml文件,将窗口的class修改为DialogBox
,这样创建的窗口实例的类型就为mDialogBox
。
再次用ministudio打开资源模板。会发现界面中Class
属性神奇的消失了
请注意,这里OK,CANCEL按钮的ID不能随便定义,要用minigui预定义的IDOK
和IDCANCEL
,否则对话框无法正确处理消息
修改 nscCreateModalDialogFromID
好了,现在我们的对话框资源模板的类型已经正确了。那么是不是可以直接用mGNCS(miniStudio新控件集)提供nscCreateModalDialogFromID
函数创建对话框呢?
你可以试试,运行的时候肯定会抛出异常。为什么呢?
再来分析ncsCreateMainWindowIndirectFromID
的源码,参见代码中本文作者添加的中文注释
mMainWnd *ncsCreateMainWindowIndirectFromID (HPACKAGE package, Uint32 wndId,
HWND owner, HICON hIcon, HMENU hMenu,
NCS_EVENT_HANDLER_INFO* handlers,
NCS_EVENT_CONNECT_INFO *connects,
DWORD user_data)
{
int uitype;
Uint32 id;
mMainWnd *mainWnd;
NCSRM_WINHEADER *header;
NCS_MNWND_TEMPLATE tmpl;
create_notify_t create_notify;
if (package == HPACKAGE_NULL)
return NULL;
uitype = get_ui_data(package, wndId, (void*)&header, &id);
if (uitype == UIDATA_ERR) {
printf ("Error=>ResManager: ID(0x%0x) isn't valid main window id. \n", wndId);
return NULL;
}
if(connects)
{
INIT_CREATE_NOTIFY(&create_notify, cb_on_create_child);
create_notify.connect_head = new_connect_node_array(connects);
}
construct_wnd_template (package, header, (NCS_WND_TEMPLATE*)&tmpl,
handlers, (NCS_CREATE_NOTIFY_INFO*)(connects?&create_notify:NULL), TRUE);
tmpl.user_data = user_data;
tmpl.hIcon = hIcon;
tmpl.hMenu = hMenu;
// Class 为mainwnd则调用ncsCreateMainWindowIndirect创建主窗口(MainWindow)
// 否则 调用ncsCreateWindowIndirect创建普通窗口
if(ncsIsChildClass(tmpl.class_name, NCSCTRL_MAINWND))
mainWnd = (mMainWnd*)ncsCreateMainWindowIndirect (&tmpl, owner);
else
mainWnd = (mMainWnd*)ncsCreateWindowIndirect((NCS_WND_TEMPLATE*)&tmpl, owner);
deconstruct_wnd_template(&tmpl);
//insert children
if(connects)
{
if(mainWnd)
connect_events(create_notify.connect_head, connects);
free_connect_nodes(create_notify.connect_head);
}
del_list(package, id);
if (mainWnd == NULL) {
printf ("RESMANAGER>Error: According to ID(0x%0x), create main window or dialog failure. \n", wndId);
return NULL;
}
return mainWnd;
}
从上面的代码分析可以看出,当模板的Class属性为mainwnd
时创建的窗口类型是MainWindow
,而其他类型时返回的是普通窗口实例。问题就出在这里。
再回头看ncsCreateModalDialogFromID
的代码
if (dialog) {
ret = _c(dialog)->doModal(dialog, TRUE);
MainWindowThreadCleanup(dialog->hwnd);
}
对话框关闭后调用MainWindowThreadCleanup
从函数名就可以看出,这个函数是针对MainWindow
的,对于一个普通窗口对象是不能执行MainWindowThreadCleanup
的。程序的运行异常就来自于这里。
mGNCS(miniStudio新控件集)中提供的调用例程都是调用ncsCreateMainWindowIndirect
来创建窗口实例,所以能正常执行。
如下图是mGNCS
手册中的例程片段
所以显然不能用ncsCreateModalDialogFromID
来创建我们手工修改过模板Class属性的对话框了。但既然知道了原因,我们就可以参照ncsCreateModalDialogFromID
的代码自己写一个myCreateModalDialogFromID
函数。
// 根据模板ID(dlgId)创建模式对话框
// 点击IDOK按钮,返回1,否则返回0
int myCreateModalDialogFromID(HPACKAGE package, Uint32 dlgId,
HWND owner, HICON hIcon, HMENU hMenu,
NCS_EVENT_HANDLER_INFO* handlers, NCS_EVENT_CONNECT_INFO* connects)
{
int ret = 0;
mDialogBox * dialog = SAFE_CAST(mDialogBox,
ncsCreateMainWindowIndirectFromID(package, dlgId, owner, hIcon, hMenu, handlers, connects, 0));
if (dialog) {
mWidget* wok = ncsGetChildObj(dialog->hwnd,IDOK);
mWidget* wcancel = ncsGetChildObj(dialog->hwnd,IDCANCEL);
// 检查窗口对象中是否有IDOK,IDCANCEL按钮,如果没有输出警告
if(!wok && !wcancel){
perror("WARNING:not define OK or CANCEL button\n");
}
// 检查IDOK,IDCANCEL是否为按钮对象,如果不是则输出警告
if(wok && ! INSTANCEOF(wok,mButton)){
perror("WARNING:IDOK is not a button\n");
}
if(wcancel && ! INSTANCEOF(wcancel,mButton)){
perror("WARNING:IDCANCEL is not a button\n");
}
ret = _c(dialog)->doModal(dialog, TRUE);
// 这里判断是否为MainWindow,如果是MainWindow才执行MainWindowThreadCleanup
if(IsMainWindow(dialog->hwnd)){
MainWindowThreadCleanup(dialog->hwnd);
}
}
return ret;
}