【问题标题】:Make Timage move down and left 3 pixels when Mouse enters and return to original position when mouse leaves鼠标进入时使图像向下和左移动3个像素,鼠标离开时返回原始位置
【发布时间】:2018-01-16 03:57:55
【问题描述】:

我有一个包含大约 168 个 Timage 对象的表单,其中包含用户可选择的图标。

当鼠标悬停在 Timage 对象上时,我希望每个图标向下和向右移动 3 个像素。当鼠标离开 Timage 时,我希望它返回到原来的位置。这将为用户界面添加令人愉悦的效果。

我知道我可以在 OnMouseEnter 和 OnMouseLeave 事件中做到这一点,并且效果很好 - 但是我不禁认为必须有一种更优雅/更有效的方法来为所有 168 个 Timage 对象产生这种效果,而不是创建 168 个OnMouseEnter 程序和 168 个 OnMouseLeave 程序。

非常感谢任何帮助...

【问题讨论】:

    标签: delphi delphi-xe3 delphi-xe4


    【解决方案1】:

    创建一个OnMouseEnter 事件处理程序并将其分配给每个组件就足够了(OnMouseLeave 类似)。

    如果这些组件是在设计时创建的(很难想象),那么您可以在表单设计器中选择所有 168 个图像,然后转到对象检查器并在一个正如 Remy Lebeau 在 cmets 中所写的那样,单打独斗。替代方式 - 使用现有的组件列表(假设所有者是表单并且表单上没有其他 TImage):

    for i := 0 to Components.Count - 1 do
      if Components[i] is TImage then  //perhaps more conditions to separate needed images
         TImage(Components[i]).OnMouseEnter := EnterHandler;
    

    如果组件是在运行时创建的并且它们存储在数组或列表中,则处理程序分配更简单:

    for i := 0 to Images.Length - 1 do
       Images[i].OnMouseEnter := EnterHandler;
    

    然后您可以使用事件的Sender 参数处理每个组件:

    procedure TMyForm.EnterHandler(Sender: TObject);
    begin
      TImage(Sender).Left := TImage(Sender).Left + 3;
      TImage(Sender).Top := TImage(Sender).Top + 3;
    end;
    
    procedure TMyForm.LeaveHandler(Sender: TObject);
    begin
      TImage(Sender).Left := TImage(Sender).Left - 3;
      TImage(Sender).Top := TImage(Sender).Top - 3;
    end;
    

    【讨论】:

    • @Remy 你认为在代码中分配处理程序的例子在这里没有用吗?
    • 并非如此,因为它假设用户已将 TImage 对象存储在数组中,但在用户的问题中没有任何迹象。此外,可以在设计时通过对象检查器分配事件处理程序,这是在设计时创建的组件的首选解决方案。只需在表单设计器中选择所有 168 个图像,然后转到对象检查器并一次性分配事件。
    • @RemyLebeau 除非表单上没有其他内容,否则“简单地”选择 168 个组件并不一定是一件容易的事……更何况还有 no-undo 的风险鼠标滑;)
    • @J...:您始终可以使用结构窗格来选择它们。
    • @RemyLebeau 假设它们在某种程度上是连续列出的,并且没有嵌套在面板或其他子控件中。不幸的是,ctrl+shift 在结构窗格中无法正常工作。
    【解决方案2】:

    这里最简洁的解决方案是创建一个自定义组件,并让您的设计远离如此繁重且扁平的设计时布局。这些自然变得难以维护和修改。

    也就是说,如果您想要快速破解以节省大量打字和点击,您可以使用插入器类来注入此鼠标行为。

    在表单单元的interface 部分,在表单的类声明上方添加以下类:

    type
      TImage = class(Vcl.ExtCtrls.TImage)
      private
        procedure CMMouseEnter(var Message: TMessage); message CM_MOUSEENTER;
        procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
      end;       
    
      TForm1 = class(TForm)
        { ... rest of your form as normal }
      end;
    

    然后,在implementation 部分中,添加以下内容:

    procedure TImage.CMMouseEnter(var Message: TMessage);
    begin
      inherited;
      Top := Top + 3;   
      Left := Left + 3;
    end;
    
    procedure TImage.CMMouseLeave(var Message: TMessage);
    begin
      inherited;
      Top := Top - 3;   
      Left := Left - 3;
    end;
    

    像这样定义插入器会有效地导致您修改后的TImage 类替换在设计时放置在表单上的所有现有TImage 组件。

    请注意,此示例仅适用于 Windows 上的 VCL。对于使用 FMX 的跨平台解决方案,所有 UI 控件都有虚拟的 DoMouseEnter()DoMouseLeave() 方法,您可以使用 override 代替,例如:

    type
      TImage = class(FMX.Objects.TImage)
      protected
        procedure DoMouseEnter; override;
        procedure DoMouseLeave; override;
      end;       
    
    ...
    
    procedure TImage.DoMouseEnter;
    begin
      inherited;
      Top := Top + 3;   
      Left := Left + 3;
    end;
    
    procedure TImage.DoMouseLeave;
    begin
      inherited;
      Top := Top - 3;   
      Left := Left - 3;
    end;
    

    【讨论】:

    • 您不需要单独的TMyCustomImage 类,插入器可以直接从原始类派生:TImage = class(Vcl.ExtCtrls.TImage)。并且您的插入器的消息处理程序不需要复制原始代码,只需调用 inherited 即可进行默认处理。此外,interposer 不是类助手,因此它们不受西雅图以外的类助手的限制。插入器在所有版本中都能正常工作。
    • @RemyLebeau inherited 是否适用于非虚拟方法? ....实际上,尝试它更容易 - 它确实如此。学到了一些新东西。
    • 没有。但是,在本例中,插入器处理的是消息,而不是覆盖方法,因此inherited 会将消息传递给基类消息调度器进行默认处理。
    • 非常感谢所有提供建议解决方案的贡献者。至于在设计时创建 168 个 Timage 对象,也许我手头的时间有点多;)再一次 - 非常感谢 - 我的应用程序现在进展顺利!
    猜你喜欢
    • 2012-11-11
    • 1970-01-01
    • 1970-01-01
    • 2021-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-26
    相关资源
    最近更新 更多