【VS开发】动态添加的ActiveX控件如何响应事件
http://blog.csdn.net/xiaoqiqixiao/article/details/574542
今天在csdn上看到一朋友问如何响应动态添加的控件的事件,搜索资料,发现对于一般的应用来说,使用ON_EVENT_RANGE就足以满足需要了,不过,还是希望能够更灵活的实现动态的响应,所以又经过对MFC源码一番探查,发现还是可以的,而且应该也不是很复杂的,下面分别来说说这两种方法
例程为Tdax,对话框程序,使用了Microsoft UpDown Control 6.0控件作为控件,添加了MouseDown事件
第一种方法挺简单的:
1.
缺省的映射定义为
BEGIN_EVENTSINK_MAP(CTdaxDlg, CDialog)
//{{AFX_EVENTSINK_MAP(CTdaxDlg)
ON_EVENT(CTdaxDlg, IDC_UPDOWN1, -605 /* MouseDown */, OnMouseDownUpdown1, VTS_I2 VTS_I2 VTS_I4 VTS_I4)
//}}AFX_EVENTSINK_MAP
END_EVENTSINK_MAP()
现改为
BEGIN_EVENTSINK_MAP(CTdaxDlg, CDialog)
//{{AFX_EVENTSINK_MAP(CTdaxDlg)
//}}AFX_EVENTSINK_MAP
ON_EVENT_RANGE(CTdaxDlg, IDC_UPDOWN1, IDC_UPDOWN1+10, -605 /* MouseDown */, OnMouseDownUpdown1, VTS_I4 VTS_I2 VTS_I2 VTS_I4 VTS_I4)
END_EVENTSINK_MAP()
就是说,可以映射至少11个控件(请注意,这里用了至少,也就是说可以少于11个,甚至于1个也无所谓)
a.这里建议把ON_EVENT_RANGE从
//{{AFX_EVENTSINK_MAP(CTdaxDlg)
ON_EVENT_RANGE(CTdaxDlg, IDC_UPDOWN1, IDC_UPDOWN1+10, -605 /* MouseDown */, OnMouseDownUpdown1, VTS_I4 VTS_I2 VTS_I2 VTS_I4 VTS_I4)
//}}AFX_EVENTSINK_MAP
对中拖出来,见上面例子,不拖出来不会对程序产生影响,但可能会影响后续的IDE操作,试试便知。
b.最后的参数类型中要在前面加上VTS_I4,即从VTS_I2 VTS_I2 VTS_I4 VTS_I4变为VTS_I4 VTS_I2 VTS_I2 VTS_I4 VTS_I4,当然事件处理函数也将做相应调整,下面马上讲到。
2.
修改框架建立的事件处理函数
void OnMouseDownUpdown1(short Button, short Shift, long x, long y);
改为
BOOL OnMouseDownUpdown1(UINT nID, short Button, short Shift, long x, long y);
返回类型为void,应该也无所谓的,不过msdn上说是BOOL,咱就BOOL吧,但UINT nID这个参数是一定要加上的
3.做一些于本案无关的工作
a.在对话框编辑器中删掉原来的ID为IDC_UPDOWN1的控件
b.加一个成员变量CObList m_oaUpdowns;
c.在OnInitDialog中,加上
UINT nID = IDC_UPDOWN1;
CRect rect(10, 10, 20, 30);
for(int i=0; i<11; i++){
CUpDown* pupdown = new CUpDown;
rect.OffsetRect(0, 20);
pupdown->Create(NULL, WS_CHILD | WS_VISIBLE, rect, this, nID + i);
m_oaUpdowns.Add(pupdown);
}
d.在OnDestroy中,加上
for(int i=0; i<m_oaUpdowns.GetSize(); i++){
delete m_oaUpdowns[i];
}
e.在OnMouseDownUpdown1中,加上
CString str;
str.Format("%d", nID);
MessageBox(str);
用来指示是否收到事件
4.编译一下,可以发现,绝对OK
下面,我们来看更多灵活的方法:
观察MFC宏
BEGIN_EVENTSINK_MAP,END_EVENTSINK_MAP和ON_EVENT,可以发现MFC将控件ID,事件ID和事件处理函数等等信息都放在AFX_EVENTSINKMAP_ENTRY这个结构,每个映射一个这种结构,所有的结构组成一数组放在AFX_EVENTSINKMAP结构中,本来以为动态修改这两个结构信息,不就可以实现动态映射控件事件了,可惜发现MFC定义了
const AFX_EVENTSINKMAP_ENTRY theClass::_eventsinkEntries[] =
{
...
}
可恶的const,只好另想它途了。
继续观察MFC源码,发现它中间会有一个
BOOL CCmdTarget::OnEvent(UINT idCtrl, AFX_EVENT* pEvent,
AFX_CMDHANDLERINFO* pHandlerInfo)
{
...
}
函数中有如下代码
VARIANT var;
AfxVariantInit(&var);
DISPPARAMS dispparams;
dispparams.rgvarg = NULL;
if (bRange)
{
memcpy(&dispparams, pEvent->m_pDispParams, sizeof(DISPPARAMS));
dispparams.rgvarg = new VARIANT[++dispparams.cArgs];
memcpy(dispparams.rgvarg, pEvent->m_pDispParams->rgvarg,
sizeof(VARIANT) * (dispparams.cArgs-1));
VARIANT* pvarID = &dispparams.rgvarg[dispparams.cArgs-1];
V_VT(pvarID) = VT_I4;
V_I4(pvarID) = idCtrl;
}
hResult = CallMemberFunc(&pEntry->dispEntry, DISPATCH_METHOD, &var,
(bRange ? &dispparams : pEvent->m_pDispParams), &uArgError);
ASSERT(FAILED(hResult) || (V_VT(&var) == VT_BOOL));
bHandled = V_BOOL(&var);
if (bRange)
delete [] dispparams.rgvarg;
break;
好了,切入口找到了,就用这个CallMemberFunc了,唯一的问题就是重新组织这个函数所需的参数了。
开始:
1.为避免干扰,干脆注释掉OnInitDialog上一例中添加的代码,再加入如下代码:
UINT nID = 10004;
CRect rect(10, 10, 20, 30);
for(int i=0; i<11; i++){
CUpDown* pupdown = new CUpDown;
rect.OffsetRect(0, 20);
pupdown->Create(NULL, WS_CHILD | WS_VISIBLE, rect, this, nID + i);
m_oaUpdowns.Add(pupdown);
}
其实就改了一个nID,之所以改nID,是为了表示这个ID是可以你自己动态确定的,当然这里省事,将这些ID连在了一起,其实分开也是没问题的。
2.注释掉事件映射
BEGIN_EVENTSINK_MAP(CTdaxDlg, CDialog)
//{{AFX_EVENTSINK_MAP(CTdaxDlg)
//}}AFX_EVENTSINK_MAP
// ON_EVENT_RANGE(CTdaxDlg, IDC_UPDOWN1, IDC_UPDOWN1+10, -605 /* MouseDown */, OnMouseDownUpdown1, VTS_I4 VTS_I2 VTS_I2 VTS_I4 VTS_I4)
END_EVENTSINK_MAP()
3.重载对话框的OnCmdMsg,加入代码如下:
if(nCode == CN_EVENT && nID >= 10004 && nID <= 10014){
AFX_EVENT* pEvent = (AFX_EVENT*)pExtra;
if(pEvent != NULL && pEvent->m_dispid == -605){//根据dispid来判断是否正确的事件
AFX_DISPMAP_ENTRY e;
e.lpszName = _T("");
e.lDispID = -605; //dispID,事件的dispID
e.vt = VT_BOOL; //返回类型
e.pfn = (AFX_PMSG)OnMouseDownUpdown1;//事件处理函数
e.pfnSet = (AFX_PMSG)0;
e.nPropOffset = 0;
e.flags = afxDispCustom;
//下面是参数类型,如果事件处理函数不加控件ID的话,应该改为VTS_I2 VTS_I2 VTS_I4 VTS_I4
e.lpszParams = VTS_I4 VTS_I2 VTS_I2 VTS_I4 VTS_I4;
UINT uArgError = (UINT)-1; // no error yet
VARIANT var;
AfxVariantInit(&var);
//如果需要每个控件的ID信息,应该为框架生成的事件处理函数的参数最前面加上1个UINT nID参数,代码如下:
DISPPARAMS dispparams;
dispparams.rgvarg = NULL;
memcpy(&dispparams, pEvent->m_pDispParams, sizeof(DISPPARAMS));
dispparams.rgvarg = new VARIANT[++dispparams.cArgs];
memcpy(dispparams.rgvarg, pEvent->m_pDispParams->rgvarg,
sizeof(VARIANT) * (dispparams.cArgs-1));
VARIANT* pvarID = &dispparams.rgvarg[dispparams.cArgs-1];
pvarID->vt = VT_I4;
pvarID->intVal = nID;
CallMemberFunc(&e, DISPATCH_METHOD, &var,
&dispparams, &uArgError);
delete []dispparams.rgvarg;
//如果不需要控件的ID信息,代码如下,不过函数就不用加上第1个参数(控件ID)了:
/* CallMemberFunc(&e, DISPATCH_METHOD, &var,
(pEvent->m_pDispParams), &uArgError);*/
}
return TRUE;
}
return CDialog::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
4.编译运行,OK了。
例程为Tdax,对话框程序,使用了Microsoft UpDown Control 6.0控件作为控件,添加了MouseDown事件
第一种方法挺简单的:
1.
缺省的映射定义为
BEGIN_EVENTSINK_MAP(CTdaxDlg, CDialog)
//{{AFX_EVENTSINK_MAP(CTdaxDlg)
ON_EVENT(CTdaxDlg, IDC_UPDOWN1, -605 /* MouseDown */, OnMouseDownUpdown1, VTS_I2 VTS_I2 VTS_I4 VTS_I4)
//}}AFX_EVENTSINK_MAP
END_EVENTSINK_MAP()
现改为
BEGIN_EVENTSINK_MAP(CTdaxDlg, CDialog)
//{{AFX_EVENTSINK_MAP(CTdaxDlg)
//}}AFX_EVENTSINK_MAP
ON_EVENT_RANGE(CTdaxDlg, IDC_UPDOWN1, IDC_UPDOWN1+10, -605 /* MouseDown */, OnMouseDownUpdown1, VTS_I4 VTS_I2 VTS_I2 VTS_I4 VTS_I4)
END_EVENTSINK_MAP()
就是说,可以映射至少11个控件(请注意,这里用了至少,也就是说可以少于11个,甚至于1个也无所谓)
a.这里建议把ON_EVENT_RANGE从
//{{AFX_EVENTSINK_MAP(CTdaxDlg)
ON_EVENT_RANGE(CTdaxDlg, IDC_UPDOWN1, IDC_UPDOWN1+10, -605 /* MouseDown */, OnMouseDownUpdown1, VTS_I4 VTS_I2 VTS_I2 VTS_I4 VTS_I4)
//}}AFX_EVENTSINK_MAP
对中拖出来,见上面例子,不拖出来不会对程序产生影响,但可能会影响后续的IDE操作,试试便知。
b.最后的参数类型中要在前面加上VTS_I4,即从VTS_I2 VTS_I2 VTS_I4 VTS_I4变为VTS_I4 VTS_I2 VTS_I2 VTS_I4 VTS_I4,当然事件处理函数也将做相应调整,下面马上讲到。
2.
修改框架建立的事件处理函数
void OnMouseDownUpdown1(short Button, short Shift, long x, long y);
改为
BOOL OnMouseDownUpdown1(UINT nID, short Button, short Shift, long x, long y);
返回类型为void,应该也无所谓的,不过msdn上说是BOOL,咱就BOOL吧,但UINT nID这个参数是一定要加上的
3.做一些于本案无关的工作
a.在对话框编辑器中删掉原来的ID为IDC_UPDOWN1的控件
b.加一个成员变量CObList m_oaUpdowns;
c.在OnInitDialog中,加上
UINT nID = IDC_UPDOWN1;
CRect rect(10, 10, 20, 30);
for(int i=0; i<11; i++){
CUpDown* pupdown = new CUpDown;
rect.OffsetRect(0, 20);
pupdown->Create(NULL, WS_CHILD | WS_VISIBLE, rect, this, nID + i);
m_oaUpdowns.Add(pupdown);
}
d.在OnDestroy中,加上
for(int i=0; i<m_oaUpdowns.GetSize(); i++){
delete m_oaUpdowns[i];
}
e.在OnMouseDownUpdown1中,加上
CString str;
str.Format("%d", nID);
MessageBox(str);
用来指示是否收到事件
4.编译一下,可以发现,绝对OK
下面,我们来看更多灵活的方法:
观察MFC宏
BEGIN_EVENTSINK_MAP,END_EVENTSINK_MAP和ON_EVENT,可以发现MFC将控件ID,事件ID和事件处理函数等等信息都放在AFX_EVENTSINKMAP_ENTRY这个结构,每个映射一个这种结构,所有的结构组成一数组放在AFX_EVENTSINKMAP结构中,本来以为动态修改这两个结构信息,不就可以实现动态映射控件事件了,可惜发现MFC定义了
const AFX_EVENTSINKMAP_ENTRY theClass::_eventsinkEntries[] =
{
...
}
可恶的const,只好另想它途了。
继续观察MFC源码,发现它中间会有一个
BOOL CCmdTarget::OnEvent(UINT idCtrl, AFX_EVENT* pEvent,
AFX_CMDHANDLERINFO* pHandlerInfo)
{
...
}
函数中有如下代码
VARIANT var;
AfxVariantInit(&var);
DISPPARAMS dispparams;
dispparams.rgvarg = NULL;
if (bRange)
{
memcpy(&dispparams, pEvent->m_pDispParams, sizeof(DISPPARAMS));
dispparams.rgvarg = new VARIANT[++dispparams.cArgs];
memcpy(dispparams.rgvarg, pEvent->m_pDispParams->rgvarg,
sizeof(VARIANT) * (dispparams.cArgs-1));
VARIANT* pvarID = &dispparams.rgvarg[dispparams.cArgs-1];
V_VT(pvarID) = VT_I4;
V_I4(pvarID) = idCtrl;
}
hResult = CallMemberFunc(&pEntry->dispEntry, DISPATCH_METHOD, &var,
(bRange ? &dispparams : pEvent->m_pDispParams), &uArgError);
ASSERT(FAILED(hResult) || (V_VT(&var) == VT_BOOL));
bHandled = V_BOOL(&var);
if (bRange)
delete [] dispparams.rgvarg;
break;
好了,切入口找到了,就用这个CallMemberFunc了,唯一的问题就是重新组织这个函数所需的参数了。
开始:
1.为避免干扰,干脆注释掉OnInitDialog上一例中添加的代码,再加入如下代码:
UINT nID = 10004;
CRect rect(10, 10, 20, 30);
for(int i=0; i<11; i++){
CUpDown* pupdown = new CUpDown;
rect.OffsetRect(0, 20);
pupdown->Create(NULL, WS_CHILD | WS_VISIBLE, rect, this, nID + i);
m_oaUpdowns.Add(pupdown);
}
其实就改了一个nID,之所以改nID,是为了表示这个ID是可以你自己动态确定的,当然这里省事,将这些ID连在了一起,其实分开也是没问题的。
2.注释掉事件映射
BEGIN_EVENTSINK_MAP(CTdaxDlg, CDialog)
//{{AFX_EVENTSINK_MAP(CTdaxDlg)
//}}AFX_EVENTSINK_MAP
// ON_EVENT_RANGE(CTdaxDlg, IDC_UPDOWN1, IDC_UPDOWN1+10, -605 /* MouseDown */, OnMouseDownUpdown1, VTS_I4 VTS_I2 VTS_I2 VTS_I4 VTS_I4)
END_EVENTSINK_MAP()
3.重载对话框的OnCmdMsg,加入代码如下:
if(nCode == CN_EVENT && nID >= 10004 && nID <= 10014){
AFX_EVENT* pEvent = (AFX_EVENT*)pExtra;
if(pEvent != NULL && pEvent->m_dispid == -605){//根据dispid来判断是否正确的事件
AFX_DISPMAP_ENTRY e;
e.lpszName = _T("");
e.lDispID = -605; //dispID,事件的dispID
e.vt = VT_BOOL; //返回类型
e.pfn = (AFX_PMSG)OnMouseDownUpdown1;//事件处理函数
e.pfnSet = (AFX_PMSG)0;
e.nPropOffset = 0;
e.flags = afxDispCustom;
//下面是参数类型,如果事件处理函数不加控件ID的话,应该改为VTS_I2 VTS_I2 VTS_I4 VTS_I4
e.lpszParams = VTS_I4 VTS_I2 VTS_I2 VTS_I4 VTS_I4;
UINT uArgError = (UINT)-1; // no error yet
VARIANT var;
AfxVariantInit(&var);
//如果需要每个控件的ID信息,应该为框架生成的事件处理函数的参数最前面加上1个UINT nID参数,代码如下:
DISPPARAMS dispparams;
dispparams.rgvarg = NULL;
memcpy(&dispparams, pEvent->m_pDispParams, sizeof(DISPPARAMS));
dispparams.rgvarg = new VARIANT[++dispparams.cArgs];
memcpy(dispparams.rgvarg, pEvent->m_pDispParams->rgvarg,
sizeof(VARIANT) * (dispparams.cArgs-1));
VARIANT* pvarID = &dispparams.rgvarg[dispparams.cArgs-1];
pvarID->vt = VT_I4;
pvarID->intVal = nID;
CallMemberFunc(&e, DISPATCH_METHOD, &var,
&dispparams, &uArgError);
delete []dispparams.rgvarg;
//如果不需要控件的ID信息,代码如下,不过函数就不用加上第1个参数(控件ID)了:
/* CallMemberFunc(&e, DISPATCH_METHOD, &var,
(pEvent->m_pDispParams), &uArgError);*/
}
return TRUE;
}
return CDialog::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
4.编译运行,OK了。
【VS开发】动态添加的ActiveX控件如何响应事件的更多相关文章
- {VS2010C#}{WinForm}{ActiveX}VS2010C#开发基于WinForm的ActiveX控件
在VS2010中使用C#开发基于WinForm的ActiveX控件 常见的一些ActiveX大部分是使用VB.Delphi.C++开发,使用C#开发ActiveX要解决下面三个问题: 使.NET组件可 ...
- 让动态创建的ActiveX控件响应Windows消息
当我们通过 CWnd::CreateControl() 动态创建 ActiveX 控件时, Windows 消息并不会被发送给我 们的由 CWnd 派生得控件类.例如,即使我们为 WM_KIL ...
- C#构架之基础学习----动态添加窗体和 控件
仿照窗体应用程序编写: 任务一:生成一个Form类的窗体对象frm using System.Windows.Forms; //using指令使用Form对象创建所需的命名空间 //如 ...
- 【VS开发】windows注册ActiveX控件
ActiveX控件是一个动态链接库,是作为基于COM服务器进行操作的,并且可以嵌入在包容器宿主应用程序中,ActiveX控件的前身就是OLE控件.由于ActiveX控件与开发平台无关,因此,在一种编程 ...
- C#开发COM+组件和ActiveX控件
using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices ...
- vs2012开发基于MFC的ActiveX控件
1.新建工程 2.一直点击下一步,直到出现一下界面,注意红色标注选项,点击完成. 3.进入工程的属性界面,设置工程属性 4.添加对话框资源及其他控件,添加对话框类, 5.设置对话框属性 6.设置Dia ...
- js动态加载activeX控件在IE11与低版本IE中的差异
由于IE11更加遵循W3C规范,所以IE11与低版本IE在加载activeX时有差别. 1.IE11中动态加载activeX的顺序 var objectTag = document.createEle ...
- ActiveX控件的Events事件
http://labview360.com/article/info.asp?TID=10152&FID=165 Active X函式库 对使用LabVIEW作为开发环境的开发人员来说,如果能 ...
- Delphi不注册COM直接使用ActiveX控件并绑定事件
文笔不行,直接上源码: 主窗口: unit Unit1; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System ...
随机推荐
- React 服务器端渲染流程
其实我们在访问客户端渲染的页面时,请求到的只是一个 html 空壳,里面引入了一个 js 文件,所有的内容都是通过 js 进行插入的,正是因为页面是由 js 渲染出来的,所以会带来如下几个问题: 1. ...
- Acwing-278-数字组合(背包)
链接: https://www.acwing.com/problem/content/280/ 题意: 给定N个正整数A1,A2,-,AN,从中选出若干个数,使它们的和为M,求有多少种选择方案. 思路 ...
- Oracle数据库查询优化方案
1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引.2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引 ...
- 什么是CSS 表单?
㈠输入框(input) 样式 ⑴使用 width 属性来设置输入框的宽度 示例:css部分:input { width: 100%; } html部分:<for ...
- 学习笔记:python3,代码。小例子习作
http://www.cnblogs.com/qq21270/p/7634025.html 学习笔记:python3,一些基本语句(一些基础语法的代码,被挪到这里了) 日期和时间操作 http://b ...
- unittest详解(五) 引入装饰器@classmethod
我们知道setUp()和setDown()的作用是在每条测试用例执行前准备测试环境以及用例测试结束后恢复测试环境,如果我们执行的测试类下所有测试用例的环境准备和环境复原的操作都是一样的,那么我们就没必 ...
- Selenium 对元素的判断(expected_conditions)(转载)
我们在用webdriver去操作元素时,先要判断这个元素是否存在,存在才去操作,否则就会报错. selenium的expected_conditions模块提供了一些判断方法 场景 Expected ...
- JavaWeb-SpringBoot_使用H2数据库实现用户注册登录
使用Gradle编译项目 传送门 前端资源同:使用MySQL数据库实现用户管理_demo 传送门 H2:SpringBoot内置持久化数据库 使用H2数据库实现用户注册登录 用户可以在index.h ...
- 微信小程序_(视图)简单的swiper容器
swiper容器效果 官方文档:传送门 swiper容器可实现简单的轮播图效果 结构程序 Page({ /** * 页面的初始数据 */ data: { }, /** * 生命周期函数--监听页面加载 ...
- SpringBoot整合kafka(安装)
项目路径:https://github.com/zhaopeng01/springboot-study/tree/master/study_14 序言 Kafka 是一种高吞吐的分布式发布订阅消息系统 ...