ObjectARX如何监控实体双击事件(利用钩子函数或者反应器)

 在ObjectARX开发中,常常要监控鼠标的双击事件,比如,往一个实体中写了扩展数据,然后你希望用户双击这个实体的时候进行特殊处理。怎么办呢?
 ObjectARX至少提供了两种方法:钩子函数和反应器。

1 实现方法1——钩子函数

1.1 钩子函数的作用

 在ObjectARX中,可以在CAD的窗口消息循环中注册一个钩子函数。注册钩子函数的函数原型为:

BOOL acedRegisterFilterWinMsg( const AcedFilterWinMsgFn pfn);

 其中,AcedFilterWinMsgFn 的类型为:

 BOOL (* AcedFilterWinMsgFn)(MSG*);

 acedRegisterFilterWinMsg中形参pfn指向的函数就是钩子函数,它可以更改传递进入的消息值。如果pfn返回FALSE,这个消息将继续传递给其他钩子函数和AutoCAD(假设其他钩子函数没有终止该消息的处理);如果返回TRUE,则消息不再传递。
 在CAD的窗口消息循环中,所有窗口消息都会进入到钩子函数,你可以在钩子函数中对特定消息进行处理。

1.2 利用钩子函数监控双击事件

 在加载arx时会调用OnLoadApp,在卸载时会调用OnUnloadApp。因此,我们可以在OnLoadApp注册钩子函数,在OnUnloadApp删掉注册的钩子函数,注册与反注册的代码如下:

void OnLoadApp()
{
	//..........省略
	//注册一个钩子函数
	//If the function returns TRUE, the message will not be passed to other hook functions or AutoCAD. The message is terminated. 
	acedRegisterFilterWinMsg(FilterEntDBClick);
}

void OnUnloadApp()
{
	//..........省略
	//remove the hook function.
	acedRemoveFilterWinMsg(FilterEntDBClick);
}

 上述钩子函数具体实现为

bool FilterEntDBClick(MSG *pMsg)
{
	if (pMsg->message == WM_LBUTTONDBLCLK)
	{
		ads_name ss;
		if (RTNORM != acedSSGet(_T("I"), NULL, NULL, NULL, ss))
			return false;

		ads_name ent;
		int32_t lLen;
		acedSSLength(ss, &lLen);
		acedSSName(ss, lLen-1, ent);
		acedSSFree(ss);
		AcDbObjectId idObj;
		if (Acad::eOk == acdbGetObjectId(idObj, ent))
		{
			AcDbEntity* pEnt = NULL;
			if (Acad::eOk == acdbOpenObject(pEnt, idObj))
			{
				CString sDxfName = pEnt->isA()->dxfName();
				acutPrintf(_T("\n\t当前双击实体的dxf名称:%s"), sDxfName);
				CString sClassName = pEnt->isA()->name();
				acutPrintf(_T("\n\t当前双击实体的类名称:%s"), sClassName);
				pEnt->close();
			}
		}
	}
	//消息继续传递
	return false;
}

 当双击某个实体的时候,这个钩子函数会获取实体的对象类型、类名称等信息。关于这个函数还有几点说明:
 1.对象的图元类型、类名称信息存在在AcRxClass类对象中,通过IsA()方法,获取对象的AcRxClass类对象。然后进一步获取对象类型、类名称等信息。
 2.双击某个对象的时候,可能出现一种情况,就是已经选择了多个对象;所以,在钩子函数中,我们应当获取当前选择集的最后一个对象,这才是双击的对象,这是通过acedSSName方法来完成的。
 最后效果如下:
在这里插入图片描述

2 实现方法2——反应器

2.1 反应器的作用

 反应器实际上就是给ObjectARX提供了捕获AutoCAD特定事件的接口,例如,通过文档反应器捕获到创建新文档、打开图形文档、关闭图形文档的事件,通过数据库反应器捕获到添加新实体、修改实体、删除实体的事件,就可以针对这些事件做一些特定的处理。
 AutoCAD提供了多种类型的反应器,供ObjectARX处理特定的事件,包括:
 1. 编辑器反应器:AcEditorReactor
 2. 文档反应器:AcApDocManagerReactor
 3. 数据库反应器:AcDbDatabaseReactor
 4. 对象反应器:AcDbObject
 每个反应器的用法都有差异,监控实体双击事件,我们需要使用编辑器反应器。

2.2 利用编辑器反应器监控双击事件

 以下反应器实例监控对象的双击事件,并且当对象是组中成员的时候,在命令行输出组的名称。
 从AcEditorReactor派生出一个新类CDBClickReactor,头文件为:

#pragma once

class CDBClickReactor : public AcEditorReactor
{
public:
	CDBClickReactor() {};
	~CDBClickReactor() {};
	virtual void beginDoubleClick(const AcGePoint3d& clickPoint);
};

 该类的实现为:

#include "stdafx.h"
#include "CDBClickReactor.h"
#include <dbgroup.h>

void CDBClickReactor::beginDoubleClick(const AcGePoint3d& clickPoint)
{
	//获取预选择集
	ads_name ss;
	if (RTNORM != acedSSGet(_T("I"), NULL, NULL, NULL, ss))
		return;

	ads_name ent;
	acedSSName(ss, 0, ent);
	acedSSFree(ss);
	AcDbObjectId objId;
	acdbGetObjectId(objId, ent);

	AcDbEntity* pEnt = NULL;
	if (Acad::eOk == acdbOpenObject(pEnt, objId))
	{
		const AcDbVoidPtrArray *pReactors;
		void *pSomething;
		AcDbObjectReactor *pObjReactor;
		AcDbObjectId persObjId;
		AcDbGroup *pGroup;

		pReactors = pEnt->reactors();
		if (pReactors != NULL && pReactors->length() > 0) 
		{
			for (int i = 0; i < pReactors->length(); i++) 
			{
				pSomething = pReactors->at(i);
				// Is it a persistent reactor?
				if (acdbIsPersistentReactor(pSomething)) 
				{
					persObjId = acdbPersistentReactorObjectId(pSomething);

					// 如果是组,就打开,输出组名称
					if ((Acad::eOk == acdbOpenObject(pGroup, persObjId, AcDb::kForRead)))
					{
						acutPrintf(_T("\n\t当前组的名称为:%s\n"), pGroup->name());
						pGroup->close();
						break;
					}
				}
			}
		}
	}
}

 关于类的实现,着重说明以下几点:
 1.用户双击对象以后,会进入到CDBClickReactor::beginDoubleClick,我们为了演示方便,针对用户双击组对象的情况,做了特殊处理,输出组的名称;其他情况没有特殊处理。
 2.用户双击组时,实际上双击的是组中的某个成员实体,但是这个实体添加了一个永久反应器,这个永久反应器关联的对象才是组对象。上述例程用到的acdbPersistentReactorObjectId,返回的就是永久反应器关联的实体ID。
 除了上述实现文件以外,需要声明全局变量

CDBClickReactor* g_ReactorDBClick = NULL;

 在加载arx时,往CAD编辑器加载这个反应器

void OnLoadApp()
{
	//.........省略
	g_ReactorDBClick = new CDBClickReactor();
	acedEditor->addReactor(g_ReactorDBClick);
}

 在卸载arx时,需要删掉这个反应器

void OnUnloadApp()
{
	//.........省略
	acedEditor->removeReactor(g_ReactorDBClick);
	delete g_ReactorDBClick;
}

 实现效果如下:
在这里插入图片描述
 以上,就是利用编辑器反应器监控对象双击的简单示例。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_Santiago

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值