mingui: 基于miniStudio1.2.1创建模式对话框(DialogBox)

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预定义的IDOKIDCANCEL,否则对话框无法正确处理消息
这里写图片描述

修改 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;
}

猜你喜欢

转载自blog.csdn.net/10km/article/details/80939641