德尔福/拉撒路|有没有一种方法可以简化全局OnMouseEnter事件处理?

时间:2019-05-17 03:37:10

标签: delphi event-handling portability lazarus simplify

问题

我想在Windows上的 Delphi XE6 中简化以下代码,随着我添加越来越多类型的组件,该代码变得难以维护。

可移植性注意事项:我想稍后在 Linux上的Lazarus 2.0.2 中使用相同的代码,几乎没有更改,因此Windows消息处理已不再可用问题。

问题是,我似乎找不到一种将OnMouseEnter事件处理程序分配给整个Form上所有组件的方法。

我已尽其所能,包括对象树中常见的各种类。 OnMouseEnter事件似乎没有共同点。

事件处理程序本身仅包含一个命令(procedure = void function),并且将不再包含该命令,也许这可以简化整个问题?

正如您在下面看到的那样,此刻,我需要将每种类型的组件(目前仅TLabel,TButton和TEdit)添加到for循环中。


procedure TFormMain.FormCreate(Sender: TObject);
var
    I: Integer;
begin
    for I := 0 to FormMain.ComponentCount - 1 do
    begin
        if FormMain.Components[I] is TLabel then
        begin
            (FormMain.Components[I] as TLabel).OnMouseEnter
                := @CustomGenericMouseEnter;
        end;
        if FormMain.Components[I] is TButton then
        begin
            (FormMain.Components[I] as TButton).OnMouseEnter
                := @CustomGenericMouseEnter;
        end;
        if FormMain.Components[I] is TEdit then
        begin
            (FormMain.Components[I] as TEdit).OnMouseEnter
                := @CustomGenericMouseEnter;
        end;
    end;
end;

procedure TFormMain.CustomGenericMouseEnter(Sender: TObject);
begin
    SingleCustomProcedure; // no arguments, nor return value
end;

动机

我正在编写颜色选择器应用程序,因此希望向用户显示鼠标坐标。

我那里有一个轮询计时器,我不想添加多余的代码,所以我希望这是不言自明的:

procedure TFormMain.TimerMousePollTimer(Sender: TObject);
begin
    if MousePosChanged then
    begin
        LabelEdit_MousePosX.Text := MousePosX.ToString;
        LabelEdit_MousePosY.Text := MousePosY.ToString;
    end;
end;

此外,我确实实现了 OnMouseLeave

1 个答案:

答案 0 :(得分:6)

  

OnMouseEnter事件似乎没有共同之处。

实际上是。 OnMouseEnterTControl的成员,所有视觉控件都来自于published,但大多数控件不会将其提升为protected。但是,由于它被声明为type TControlAccess = class(TControl) end; procedure TFormMain.FormCreate(Sender: TObject); var I: Integer; Comp: TComponent; begin for I := 0 to ComponentCount - 1 do begin Comp := Components[I]; if Comp is TControl then TControlAccess(Comp).OnMouseEnter := CustomGenericMouseEnter; end; end; ,因此您可以使用访问器类在任何控件上访问它,例如:

TControlAccess

之所以可行,是因为TControl获得对TControlAccess的所有受保护成员的访问权,并且声明TControlAccess的单位可以访问OnMouseEnter的所有受保护成员。

另一方面,protected最初是uses ..., TypInfo; procedure TFormMain.FormCreate(Sender: TObject); var I: Integer; Comp: TComponent; Prop: PPropInfo; M: TMethod; begin TNotifyEvent(M) := CustomGenericMouseEnter; for I := 0 to ComponentCount - 1 do begin Comp := Components[I]; if not (Comp is TControl) then Continue; Prop := GetPropInfo(Comp, 'OnMouseEnter', [tkMethod]); if Prop <> nil then SetMethodProp(Comp, Prop, M); end; end; ,因此控件可以决定是否要公开对其的访问。如果您想遵守该决定,并且仅将其设置为已促进该决定的控件,则可以使用RTTI,例如:

uses
  ..., System.Rtti;

procedure TFormMain.FormCreate(Sender: TObject);
var
  I: Integer;
  Ctx: TRttiContext;
  Comp: TComponent;
  Prop: TRttiProperty;
  V: TValue;
begin
  V := TValue.From<TNotifyEvent>(CustomGenericMouseEnter);
  for I := 0 to ComponentCount - 1 do
  begin
    Comp := Components[I];
    if not (Comp is TControl) then Continue;
    Ctx.GetType(Comp.ClassType).GetProperty('OnMouseEnter');
    if (Prop <> nil) and (Prop.Visibility in [TMemberVisibility.mvPublic, TMemberVisibility.mvPublished]) then
      Prop.SetValue(Comp, V);
  end;
end;

或者(仅适用于Delphi 2010 +):

using dbConn