Redrain duilib中委托代理存在的问题

在Redrain duilib中,委托模式将事件发送与事件处理进行了解耦,并预定义了六个事件处理函数的原型,具体如下(对应源文件UIDelegate.h):

typedef bool (*FunVoid)(void* pParam,LPARAM lParam,WPARAM wParam);
typedef bool (*FunTEvent)(TEventUI* pTEventUI,LPARAM lParam,WPARAM wParam);
typedef bool (*FunTNotify)(TNotifyUI* pTNotifyUI,LPARAM lParam,WPARAM wParam);
typedef bool (T::*CMFunVoid)(void* pParam,P lParam,WPARAM wParam);
typedef bool (T::*CMFunTEvent)(TEventUI* pTEventUI,P lParam,WPARAM wParam);
typedef bool (T::*CMFunTNotify)(TNotifyUI* pTNotifyUI,P lParam,WPARAM wParam);

如果利用如下代码给pCtrl控件的OnNotify添加一个委托函数:

pCtrl->OnNotify += MakeDelegate<CTestWnd, CTestWnd, LPARAM>(this, &CTestWnd::OnTest);

其中CTestWnd::OnTest的定义如下:

bool CTestWnd::OnTest(void *pParam, LPARAM lParam, WPARAM wParam)
{
    return true;
}

分析下起处理流程:

1、用户操作导致pCtrl发送某个事件;

2、调用CPaintManagerUI::MessageHandler;

3、在CPaintManagerUI::MessageHandler函数内部调用pMsg->pSender->OnNotify(pMsg)。这里的pMsg->pSender是上面所说的pCtrl;

4、OnNotify是pCtrl的一个成员变量,对应的类是CEventSource,该类对()进行了操作符重载,第3步中pMsg->pSender->OnNotify(pMsg),实际调用的是:

	bool CEventSource::operator() (TNotifyUI* pTNotifyUI) 
	{
		for( int i = 0; i < m_aDelegates.GetSize(); i++ ) {
			CDelegateBase* pObject = m_aDelegates.GetAt(i);

			if( pObject && !pObject->Invoke(pTNotifyUI,pObject->GetLParam(),pObject->GetWParam()) ) return false;
		}
		return true;
	}

而不是bool CEventSource::operator() (void* param) 和bool CEventSource::operator() (TEventUI* pTEventUI)。因为pMsg的类型是TNotifyUI。

5、第4部中调用Invoke函数如下:

virtual bool Invoke(TNotifyUI* pTNotifyUI,LPARAM lParam = NULL,WPARAM wParam = NULL)
{
    O* pObject = (O*) GetObj();
    if(pObject && GetNotifyTypeName().IsEmpty())
        return (pObject->*m_pCMFunTNotify)(pTNotifyUI,(P)GetLParam(),GetWParam());
    else if(pObject && pTNotifyUI && pTNotifyUI->sType == GetNotifyTypeName())
        return (pObject->*m_pCMFunTNotify)(pTNotifyUI,(P)GetLParam(),GetWParam());
        
    return true;
};

他会调用pObject->*m_pCMFunTNotify。

问题来了,最开始我们调用

pCtrl->OnNotify += MakeDelegate<CTestWnd, CTestWnd, LPARAM>(this, &CTestWnd::OnTest);

MakeDelegate根据传递的参数以及CTestWnd::OnTest的函数原型,通过构成函数

CDelegate(O* pObj, CMFunVoid pCMFunVoid,P lParam = NULL,WPARAM wParam = NULL)
 : CDelegateBase(pObj, *(FunVoid*)&pCMFunVoid,(LPARAM)lParam,wParam)
 , m_pCMFunVoid(pCMFunVoid)
 ,m_pCMFunTEvent(NULL)
 ,m_pCMFunTNotify(NULL)
 {}

得到一个委托对象,并添加到pCtrl->OnNotify中。很显然,构造的这个委托对象的m_pCMFunTNotify为NULL,而在第5步中却调用了m_pCMFunTNotify,进而导致崩溃。

从上面的分析看,

typedef bool (*FunVoid)(void* pParam,LPARAM lParam,WPARAM wParam);
typedef bool (*FunTEvent)(TEventUI* pTEventUI,LPARAM lParam,WPARAM wParam);
typedef bool (T::*CMFunVoid)(void* pParam,P lParam,WPARAM wParam);
typedef bool (T::*CMFunTEvent)(TEventUI* pTEventUI,P lParam,WPARAM wParam);

是不能使用的,除非对相关代码进行进一步修改。

另外,对菜单项不要使用委托模式。如果使用了,在菜单项对应的函数中弹出对话框时,会出现异常情况。

时间: 2024-10-12 12:56:37

Redrain duilib中委托代理存在的问题的相关文章

Redrain duilib中的名字空间问题

在Redrain duilib中的个别文件使用了using namespace UiLib;语句,那么引用lib库之后,工程自然就使用了UiLib命名空间,如果在工程中同时使用了UiLib库和ATL或者MFC,使用一些对象时会出现命名冲突的情况.应该将uilib库中的using namespace UiLib;去掉,用namespace UiLib {}取代,这样才能给用户更多的自主权.

Duilib中的消息泵和虚拟窗口

Duilib中的消息泵和虚拟窗口 一.消息泵的结构 CNotifyPump类是构建Duilib消息泵的根父类,要使用消息泵机制的窗口类应该从该类继承.在继承关系的基础上,通过DUI_DECLARE_MESSAGE_MAP.DUI_BEGIN_MESSAGE_MAP.DUI_END_MESSAGE_MAP.DUI_ON_MSGTYPE.DUI_ON_MSGTYPE_CTRNAME.DUI_ON_CLICK_CTRNAME.DUI_ON_SELECTCHANGED_CTRNAME.DUI_ON_KI

Duilib中为RichEdit\Edit控件添加自定义右键菜单

前言 Duilib中的RichEdit控件在使用中发现,基本上对复制.粘贴.剪切等快捷方式都是支持的,不过唯一缺点是没有右键菜单,感觉不够好,于是就想着加上右键菜单. 右键菜单基本思路是,在RichEdit的消息处理函数中对鼠标的右键消息处理,发送一个自定义的Notify消息出来,主窗口中受到这个消息后弹出自己的右键菜单. 实现方法 第一步:把鼠标右键消息转发出来 MessageHandler中修改原有代码 bool bWasHandled = true; if( (uMsg >= WM_MOU

Duilib中Webbrowser事件完善,使其支持判断页面加载完毕

在多iframe的页面中,需要结合DISPID_DOCUMENTCOMPLETE和DISPID_BEFORENAVIGATE2两个事件判断页面是否加载完毕,而duilib中没有提供对DISPID_DOCUMENTCOMPLETE的支持. 要支持DISPID_DOCUMENTCOMPLETE,需要修改UIWebBrowser.cpp.UIWebBrowser.h和WebBrowserEventHandler.h三个文件,修改之后的文件见附件.

duilib中加入自己定义控件之后怎么可以在xml文件里配置使用

加入自己定义控件可能有两种不同的情况: 1.  在duilib库中加入的自己定义控件. 2.  在我们的应用程序中自己重写了一个控件. 以下開始解说不同的情况下怎么才干支持在xml文件配置控件: 1.  库中情况 假如自己定义的控件是CGifUI类. 库中情况相对是比較简单的,仅仅需在分析xml文件时候将控件创建出来即可了,所以我找到的函数是CControlUI* CDialogBuilder::_Parse(CMarkupNode* pRoot, CControlUI* pParent, CP

duilib中添加自定义控件之后怎么能够在xml文件中配置使用

添加自定义控件可能有两种不同的情况: 1.  在duilib库中添加的自定义控件. 2.  在我们的应用程序中自己重写了一个控件. 下面开始讲解不同的情况下怎么才能支持在xml文件配置控件: 1.  库中情况 假如自定义的控件是CGifUI类. 库中情况相对是比较简单的,只需在分析xml文件时候将控件创建出来就行了,所以我找到的函数是CControlUI* CDialogBuilder::_Parse(CMarkupNode* pRoot, CControlUI* pParent, CPaint

duilib中CListUI控件消息处理的方法与技巧

CListUI控件是duilib中比较常用.也是比较遇到问题的控件,使用CListUI可以简单的实现类似各类软件管家软件列表的样式与功能.但是CListUI使用时经常有小伙伴经常为遇到的各种小问题而头疼,下面我们重点总结一下: 常见问题: 1.如何隐藏列表头:List控件属性里面添加header="hidden"即可 2.如何调整列表文字对齐方式: List控件属性里面修改itemalign即可(List控件中列表项的属性配置都是在List属性列表进行的,只不过在普通控件属性的基础上添

分享个Duilib中基于wke的浏览器控件

概述 wke是基于谷歌chrome浏览器源代码的裁剪版本,大小仅仅只有10M左右,无需依赖其他的扩展库(跟CEF的一大堆大约40M的DLL来比简直爽呆了),就可以在本地使用谷歌内核快速加载网页.网上也有基于wke在Duilib 上扩展的控件代码,其实wke头文件挺清楚的了,接口一目了然,特别是JS与C++交互的函数更是容易看懂,也没什么难的,你也可以做到的. 代码 毕竟是裁剪库,有的功能还是没有接口来处理的(比如网页加载前.页面跳转.菜单消息--),头文件代码: #ifndef __UIWKEW

Duilib中带有权重的灵活控件排列实现(一)

在开发播放器软件过程中,因为窗口的大小是可变的,为了让控制栏部分的控件(播放,上一集,下一集,全屏,字幕等)适应窗口的尺寸的变化而显示隐藏,产品经理会定义一系列的规则,好让在任何时候都最核心的功能提供给用户使用. 先列一下产品经理给予的需求: 两边往中间缩,保证左侧LOGO和右侧X最优先显示. 顶部隐藏优先级:搜索栏,换肤,意见反馈,播放记录,最小化,最大化 底部隐藏优先级:全屏,画质增强,无痕,打开文件,播放顺序,音量条 在处理这个需求过程中,前人也尝试了一些方法,比较通过全float绝对布局