您可以开发一个非可视组件并将其放在每个表单上,但这必须是一个非常复杂的组件,它要么使用 RTTI,要么查看 Windows 消息以尝试侦听表单中其他可视组件发生的情况。
我看到两种更简单的方法可以至少完成您所要求的部分内容......
1) 拦截对事件处理程序的调用
开发一个组件(比我上面提到的那个简单一些),它连接到您感兴趣的控制类的事件,记录调用,然后将它们转发到原始事件处理程序(如果有的话)。
假设您对TWinControl.OnEnter
和感兴趣OnExit
,TButton.OnClick
并且TEdit.OnChange
...
TEventLogger = Class(TComponent)
Private
Procedure Hookup(Const EventName: String; Const Component: TComponent; Const LoggingHandler: TNotifyEvent; Var Handler: TNotifyEvent);
Public
Procedure Setup(Const Container: TComponent);
Procedure HandleChange(Sender: TObject);
Procedure HandleClick(Sender: TObject);
Procedure HandleEnter(Sender: TObject);
Procedure HandleExit(Sender: TObject);
End;
Procedure TEventLoggerHookup(Const EventName: String; Const Component: TComponent; Const LoggingHandler: TNotifyEvent; Var Handler: TNotifyEvent);
Begin
if Assigned(Handler) Then
Handlers.AddObject(Component.Name + ';' + EventName, Handler);
Handler := LoggingHandler;
End;
Procedure TEventLogger.Setup(Const Container: TComponent);
Var
i: Integer;
c: TComponent;
Begin
For i:=0 to Container.ComponentCount - 1 Do Begin
c := Container.Components[i];
if c Is TWinControl Then Begin
Hookup('Enter',HandleEnter,TWinControl(c).OnEnter;
Hookup('Exit',HandleExit,TWinControl(c).OnExit;
End;
If c Is TButton Then
Hookup('Click',HandleClick,TButton(c).OnClick;
If c Is TEdit Then
Hookup('Change',HandleChange,TEdit(c).OnChange;
End;
End;
Procedure TEventLogger.Procedure HandleChange(Sender: TObject);
Var
s: String;
i: Integer;
e: TNotifyEvent;
Begin
With Sender As TComponent Do Begin
s := Name + ';Change';
Log(s);
i:= Handlers.IndexOf(s);
If i <> -1 Then Begin
e := Handlers.Objects[i];
e(Sender);
End;
End;
End;
这没有经过测试,但我认为你明白了。要记住的事情:
- 处理程序很简单
TStringList
……但您可以使用通用的TDictionary<Key,Value>
. 当然,您应该适当地创建和销毁它。
- 所有列出的事件都是
TNotifyEvents
。但是您可以轻松地重载 Hookup 过程来处理其他类型的 ov 事件。
- 当组件已经创建并且属性从 dfm 流入时,必须执行设置。这可能需要您向每个表单添加一些代码。
- 如果您的应用程序在运行时以动态方式创建控件,或者如果它在运行时与事件处理程序混淆,则此技术可能(并且将会)适得其反。
2)使用继承
为您要记录的每个使用的可视组件派生一类新的可视组件。您需要为每一个定义一个 Log 方法并覆盖您感兴趣的特定“事件”方法。例如:
TLogButton = class(TButton)
private
procedure Log(Const Event: String);
protected
procedure Click; override;
end;
TLogButton.Click;
begin
Log('Click');
Inherited;
end;
然后,您可以实现 Log 方法,添加您需要的所有信息(表单和组件类和名称,或其他)。如果你想做一些复杂的事情,那么你最好将日志操作委托给一个专门的类。
这也没有经过测试。要记住的事情:
- 要设置它,您可以使用大量搜索和替换...在 pas 和 dfm 文件中修改
TButton
... TLogButton
(我希望您使用文本 dfm,如果不是,最好将它们转换为文本)。
- 您必须将新单元添加到包含您感兴趣的控件的所有表单的界面部分
- 您应该在自定义库中注册新类,以便在 IDE 中使用它们,否则您将无法在设计时打开表单。
- 您必须为您感兴趣的每个类都这样做......如果应用程序使用许多不同但相似的控件类,这可能会变得混乱。
- 您必须知道要记录的类的内部结构...并非所有方法都是可覆盖的...许多第三方库没有提供源代码...这可能是地狱。
结论
总的来说......我可能会选择后一种解决方案。在单个控件类上实现基本上更容易,并且您可能已经通过一堆类获得了一些非常有用的用户活动跟踪。前一种解决方案更复杂一些,可能会扩展到涵盖某些情况......但是很难彻底测试,如果您不太了解您的代码库,风险太大。