delphi重写事件

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了delphi重写事件相关的知识,希望对你有一定的参考价值。

我现在用到一个自动搜索sql数据库服务器的组件,然后前台的程序当执行完搜索功能以后需要弹出一个框说明一共发现多少服务器。于是我找到组件里的搜索完成事件RefreshComplete,在这个事件里多加了一句代码,
loginFrm.tray1.ShowBalloonHint('搜索完成','共发现 '+inttostr(self.Items.Count)+' 台数据库服务器! ',bitInfo,10);

可是这样以后再次使用这个组件的时候,这句代码就又得重新在这个pas改过来.
所以我想这样实现功能,在RefreshComplete事件里加入一个函数接口,内容为空,然后在其他窗体类需要使用这个组件的时候,重写这个函数接口,实现搜索完成的提示功能。如果说不清楚,请留下qq,给我远程一下,谢谢
怪我没说清楚
界面只调用RefreshList过程,RefreshComplete是由Searcher的终止事件执行的,所以界面不能调用RefreshComplete吧..

然后控件里是这样写的
procedure RefreshList;
begin
Searcher.OnTerminate := RefreshComplete;
end;

procedure RefreshComplete (Sender :TObject);
begin
..........
end;

把RefreshList改成函数,加个返回值也是不行的啊,RefreshComplete 事件执行代码的时间是同步的呀。

Event实际上就是一个Property,只不过它的数据类型比较特殊,是一个函数指针。
看看这个组件的源代码里哪里引用了这个事件的所代表的函数指针。一般是在某个消息的处理过程里面,有这样的代码
if Assigned(FRefreshComplete) then FRefreshComplete(...)
找到了这个过程就好办了。在本类中把它设置成虚方法。

重新建一个新的组件,以“自动搜索sql数据库服务器的组件”为基类。然后对前面说的这个过程进行重载就可以了。先继承基类的方法,然后再加上你的代码。如你的:
loginFrm.tray1.ShowBalloonHint('搜索完成','共发现 '+inttostr(self.Items.Count)+' 台数据库服务器! ',bitInfo,10);

以后你就用你自己建的这个新的组件就可以了。

这就是面向对象的威力所在啊。继承,重载!

如果说的不够明白的话,我可以做个例子给你看。

unit Unit2;

interface

uses Windows, Messages, Classes;

type
TAncestor = class(TComponent)
private
FOnRefreshComplete: TNotifyEvent;
procedure SetOnRefreshComplete(const Value: TNotifyEvent);
protected
procedure DoRefreshComplete;virtual;
published
property OnRefreshComplete: TNotifyEvent read FOnRefreshComplete write SetOnRefreshComplete;
end;

TOffSpring = class(TAncestor)
protected
procedure DoRefreshComplete;override;
published
property OnRefreshComplete;
end;

implementation

TAncestor

procedure TAncestor.DoRefreshComplete;
begin
if Assigned(FOnRefreshComplete) then
FOnRefreshComplete(Self);
end;

procedure TAncestor.SetOnRefreshComplete(const Value: TNotifyEvent);
begin
FOnRefreshComplete := Value;
end;

TOffSpring

procedure TOffSpring.DoRefreshComplete;
begin
inherited;
ShowMessage('put my own codes here');
end;

end.
参考技术A 你所提的这个需求,不需要用到重载之类的技术,不然有点”高射炮打蚊子”的感觉.简单地将业务功能与界面功能分离即可。

Searcher.OnTerminate := RefreshComplete_ui; //改一下

//界面函数,根据你的需要进行调整
procedure RefreshComplete_ui()
var
cnt:integer;
begin
cnt:= RefreshComplete(); //调函数

//在不同窗体进行计算时,用不同的方式处理就行了.
loginFrm.tray1.ShowBalloonHint('搜索完成','共发现 '+inttostr(cnt)+' 台数据库服务器! ',bitInfo,10);
end;

//业务函数,无论你的界面怎么改,这里都不用改了
function RefreshComplete_business():integer;
begin
//...
result:=XXX;
end;

------------------------------------------------------------

用函数,返回你的搜索结果,在应用程序里处理你的界面.

这样可将你的业务功能与界面分离,便于系统的升级与复用.

//业务函数,无论你的界面怎么改,这里都不用改了
function RefreshComplete():integer;
begin
//...
result:=XXX;
end;

//界面函数
procedure ShowInfo()
var
cnt:integer;
begin
cnt:= RefreshComplete(); //调函数

//在不同窗体进行计算时,用不同的方式处理就行了.
loginFrm.tray1.ShowBalloonHint('搜索完成','共发现 '+inttostr(cnt)+' 台数据库服务器! ',bitInfo,10);
end;

qt源码解析1--事件循环原理(重写事件函数,事件过滤器等)


首先看我上篇博客准备好环境:

​​qt源码解析0--源码获取与环境准备​​


现在进入主题。

先说答案:

  • 事件event默认状态是不确定的,我们可以在处理函数里调用setAccept()或者ignore()函数来改变这个状态
  • 安装事件过滤器时,eventFilter(receiver, event))函数返回true,则目标对象的event()函数得不到执行了。
  • 重写事件函数event()时,函数返回true,而且事件是接收状态,则事件不会继续往父对象传递了,否则会传递给父对象。 注:有些特定事件是否传递给父对象不依赖于返回值,而是依赖与这个接收者类型,以及属性。比如鼠标移动事件 QEvent::MouseMove if (w-
• >isWindow() || w->testAttribute(Qt::WA_NoMousePropagation))
break;

因为qt源码的代码是这也写的:

eventAccepted = (w == receiver ? mouse : &me)->isAccepted();
if (res && eventAccepted)
break;
w = w->parentWidget(); //不断寻找父窗口对象继续传递事件

源码详情,见我下面的图,就清楚了。
 


qt事件循环和处理过程(我跟踪源码得到的)如下图所示:

qt源码解析1--事件循环原理(重写事件函数,事件过滤器等)_事件循环

 

threadData:记录自己线程上的事件派发器(eventDispatcher)、事件队列、对象列表等信息。是独一份的,每个派发器,派发对象它们都有它的指针

QApplication::exec()
QGuiApplication::exec();
QCoreApplication::exec();
QEventLoop eventLoop;
eventLoop.exec();
eventLoop.processEvents(flags | WaitForMoreEvents | EventLoopExec);
threadData->eventDispatcher.load()->processEvents(flags);
//eventDispatcher是基类指针,子类化的有QEventDispatcherWin32、QEventDispatcherBlackberry、QEventDispatcherUNIX等
//这个是在函数QCoreApplicationPrivate::createEventDispatcher()里面根据平台宏定义来创建的QEventDispatcherWin32::createInternalHwnd()
//创建一个windows系统的隐形窗口,用于接收windows系统所有派发事件static HWND qt_create_internal_window(const QEventDispatcherWin32 *eventDispatcher)
//里面为它注册了一个叫做qt_internal_proc的WNDPROC函数QEventDispatcherWin32::installMessageHook()
//注册系统钩子qt_GetMessageHook函数,截获操作系统系统所有事件(注意:这个钩子函数是在操作系统自己的线程执行的)while(canWait)
反复查询PeekMessage(&msg, 0, 0, 0, PM_REMOVE)的消息(这个就是上面钩子函数截获的消息)
相应放入用户输入事件的队列 queuedUserInputEvents
相应放入用户输入事件的队列 queuedSocketEvents
canWait = (!retVal && !d->interrupt && (flags & QEventLoop::WaitForMoreEvents));
//如果消息处理完了,则阻塞自己,等到操作系统有消息发送过来,才会继续往下执行
MsgWaitForMultipleObjectsEx(nCount, pHandles, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);LRESULT QT_WIN_CALLBACK qt_GetMessageHook(int code, WPARAM wp, LPARAM lp)
//由于这个函数是在操作系统线程里执行的???所以要尽量的快消息是PM_REMOVE,则
PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS, 0, 0);
//通过PostMessage()函数将事件发送到那个隐形窗口对来处理,对象窗口专门处理LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp)
//处理上面说的隐形窗口的消息QWindowsGuiEventDispatcher::sendPostedEvents()
QEventDispatcherWin32::sendPostedEvents();
QWindowSystemInterface::sendWindowSystemEvents(m_flags);
QGuiApplicationPrivate::processWindowSystemEvent(event);
QGuiApplicationPrivate::processWheelEvent(static_cast<QWindowSystemInterfacePrivate::WheelEvent *>(e));
QGuiApplicationPrivate::processMouseEvent(static_cast<QWindowSystemInterfacePrivate::MouseEvent *>(e));
QGuiApplication::sendSpontaneousEvent(window, &ev);
notifyInternal2(receiver, event)
QCoreApplication::notify(QObject *receiver, QEvent *event)
//这个是虚函数,会对应到子类的具体函数,一般是QApplication::notify
bool eventAccepted = mouse->isAccepted(); //也就是事件默认是接收状态
QPointer<QWidget> pw = w;while (w)
res = d->notify_helper(w, w == receiver ? mouse : &me);
// 这里让事件过滤器先执行了
if (sendThroughObjectEventFilters(receiver, e))
return true; // 遍历安装到receiver的事件过滤器对象们,先让它们的eventFilter()函数执行
for (int i = 0; i < receiver->d_func()->extraData->eventFilters.size(); ++i)
if (obj->eventFilter(receiver, event)) //这儿就是我们熟悉的事件过滤器函数了
return true;// deliver the event
bool consumed = receiver->event(e); //这儿就是我们熟悉的可以重写的事件处理函数了//这里以继承Qobject的Qwidget为例:
bool QWidget::event(QEvent *event)
switch (event->type())
case QEvent::MouseMove:
mouseMoveEvent((QMouseEvent*)event);
break; case QEvent::MouseButtonPress:
mousePressEvent((QMouseEvent*)event);
break;。。。
QCoreApplicationPrivate::setEventSpontaneous(e, false);
return consumed;eventAccepted = (w == receiver ? mouse : &me)->isAccepted();
if (res && eventAccepted)
break;
w = w->parentWidget(); //不断寻找父窗口对象

以上是关于delphi重写事件的主要内容,如果未能解决你的问题,请参考以下文章

Delphi实现窗口一直在桌面工作区内显示(重写WM_WINDOWPOSCHANGING消息)

转: Delphi的OverRideOverLoad和Virtual方法

delphi中覆盖override父类的静态方法和虚函数有啥不同?

c#如何重写类?

如何重写私有属性setter?

Winform重写键盘按键事件