如何消除TPaintBox右边缘的闪烁(例如调整大小时)

2024-02-28

总结:
假设我有一个 TForm 和两个面板。面板对齐 alTop 和 alClient。 alClient面板包含一个TPaintBox,其OnPaint涉及绘图代码。

组件上 DoubleBuffered 的默认值为 false。

在绘制过程中,闪烁很明显,因为表格、面板都绘制了背景。

由于窗体被面板覆盖,因此拦截其 WM_ERASEBKGND 消息可能没问题。如果没有,人们可能会看到面板上闪烁,并且当调整表单大小时,面板的右边缘会闪烁,因为表单会绘制其背景。

其次,因为 alTop 面板旨在成为某些按钮的容器,所以将其 DoubleBuffered 设置为 true 可能很好,以让 Delphi 确保其上没有闪烁。它可能不会带来太多的性能负担。

第三,由于 alClient 面板仅用作另一个绘图组件的容器,因此该面板很可能是not参与最终绘图的创作。在这方面,使用 TPanel 后代而不是标准 TPanel 可能会更好。在此 TPanel 后代中,重写受保护的过程 Paint,并且在该过程内不执行任何操作,尤其是继承的调用,以避免基类 TCustomPanel.Paint 中的 FillRect 调用。此外,拦截WM_ERASEBKGND消息并且内部也不做任何事情。这是因为当TPanel.ParentBackground为False时,Delphi负责重绘背景,而当为True时,ThemeService负责。

最后,要在 TPaintBox 中进行无闪烁的绘制:
(1) 使用VCL内置绘图例程,可能更好......
(2) 使用OpenGL,启用OpenGL的双缓冲。
(3) ...

===问:如何消除TPaintBox右边缘的闪烁?===

假设对于一个 TForm,我有两个面板。顶部的 alTop 相对于表单对齐,并被视为按钮的容器。另一种是相对于窗体对齐的alClient,并被视为绘图组件的容器(例如VCL 中的TPaintBox 或Graphics32 中的TPaintBox32)。对于后一个面板,其WM_ERASEBKGND消息被拦截。

现在,我在以下示例代码中使用 TPaintBox 实例。在其 OnPaint 处理程序中,我有两种选择来绘制我希望无闪烁的绘图。选择1是在填充矩形后绘制。由于其父面板不应擦除背景,因此绘图应不闪烁。选择 2 是在 TBitmap 上绘图,然后将其 Canvas 复制回绘画箱。

然而,这两个选择都在闪烁,其中第二个选择尤其闪烁。我主要关心的是选择 1。如果您调整表单大小,您可以看到闪烁的主要部分发生在右边缘。为什么会出现这种情况?有人可以帮忙评论一下原因和可能的解决方案吗? (注意,如果我在这里使用 TPaintBox32 而不是 TPaintBox,右边缘根本不会闪烁。)

我的第二个问题是,当使用选项 1 时,一小部分闪烁会随机发生在颜料盒上。它不是很明显,但如果您快速调整表单大小,仍然可以观察到。此外,当使用选项 2 时,这种闪烁会变得更加严重。我没有找到这个的原因。有人可以帮忙评论一下可能的原因和解决方案吗?

任何建议表示赞赏!

    unit uMainForm;

    interface

    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      ExtCtrls, Dialogs;

    type
      TMainForm = class(TForm)
        procedure FormCreate(Sender: TObject);
      private
        { Private declarations }
        FPnlCtrl, FPnlScene: TPanel;
        FPbScene: TPaintBox;

        OldPnlWndProc: TWndMethod;

        procedure PnlWndProc(var Message: TMessage);
        procedure OnScenePaint(Sender: TObject);
      public
        { Public declarations }
      end;

    var
      MainForm: TMainForm;

    implementation

    {$R *.dfm}

    procedure TMainForm.FormCreate(Sender: TObject);
    begin
      Self.Color := clYellow;
      Self.DoubleBuffered := False;

      FPnlCtrl := TPanel.Create(Self);
      FPnlCtrl.Parent := Self;
      FPnlCtrl.Align := alTop;
      FPnlCtrl.Color := clPurple;
      FPnlCtrl.ParentColor := False;
      FPnlCtrl.ParentBackground := False;
      FPnlCtrl.FullRepaint := False;
      FPnlCtrl.DoubleBuffered := False;

      FPnlScene := TPanel.Create(Self);
      FPnlScene.Parent := Self;
      FPnlScene.Align := alClient;
      FPnlScene.Color := clBlue;
      FPnlScene.ParentColor := False;
      FPnlScene.ParentBackground := False;
      FPnlScene.FullRepaint := False;
      FPnlScene.DoubleBuffered := False;

      FPbScene := TPaintBox.Create(Self);
      FPbScene.Parent := FPnlScene;
      FPbScene.Align := alClient;
      FPbScene.Color := clRed;
      FPbScene.ParentColor := False;

      //
      OldPnlWndProc := Self.FPnlScene.WindowProc;
      Self.FPnlScene.WindowProc := Self.PnlWndProc;

      FPbScene.OnPaint := Self.OnScenePaint;

    end;

    procedure TMainForm.PnlWndProc(var Message: TMessage);
    begin
      if (Message.Msg = WM_ERASEBKGND) then
        Message.Result := 1
      else
        OldPnlWndProc(Message);
    end;

    procedure TMainForm.OnScenePaint(Sender: TObject);
    var
      tmpSceneBMP: TBitmap;
    begin
      // Choice 1
       FPbScene.Canvas.FillRect(FPbScene.ClientRect);
       FPbScene.Canvas.Ellipse(50, 50, 150, 150);

      // Choice 2
    //  tmpSceneBMP := TBitmap.Create;
    //  tmpSceneBMP.Width := FPbScene.ClientWidth;
    //  tmpSceneBMP.Height := FPbScene.ClientHeight;
    //  tmpSceneBMP.Canvas.Brush.Color := FPbScene.Color;
    //  tmpSceneBMP.Canvas.FillRect(FPbScene.ClientRect);
    //  tmpSceneBMP.Canvas.Ellipse(50, 50, 150, 150);
    //  FPbScene.Canvas.CopyRect(FPbScene.ClientRect, tmpSceneBMP.Canvas,
    //    FPbScene.ClientRect);

    end;

    end.

===问:如何正确拦截面板重绘背景? ===
(如果我应该在一个单独的问题中问这个问题,请直接说出来,我会删除它。)

新建一个VCL应用程序,粘贴示例代码,附加FormCreate,运行调试。现在将鼠标悬停在表单上,​​您可以看到面板正在明显地重新绘制其背景。但是,如示例代码所示,我应该已经通过拦截 WM_ERASEBKGND 消息来拦截此行为。

请注意,如果我注释掉这三行,

FPnlScene.Color := clBlue;
FPnlScene.ParentColor := False;
FPnlScene.ParentBackground := False;  

然后可以捕获WM_ERASEBKGND消息。我对这种差异一无所知。

有人可以帮助评论这种行为的原因,以及如何正确拦截 WM_ERASEBKGND 消息(当 ParentBackground := False 时)?

    unit Unit1;

    interface

    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      ExtCtrls, Dialogs;

    type
      TForm1 = class(TForm)
        procedure FormCreate(Sender: TObject);
      private
        { Private declarations }
        FPnlScene: TPanel;
        FPbScene: TPaintBox;

        FOldPnlWndProc: TWndMethod;

        procedure PnlWndProc(var Message: TMessage);

        procedure OnSceneMouseMove(Sender: TObject; Shift: TShiftState;
          X, Y: Integer);
        procedure OnScenePaint(Sender: TObject);
      public
        { Public declarations }
      end;

    var
      Form1: TForm1;

    implementation

    {$R *.dfm}

    procedure TForm1.FormCreate(Sender: TObject);
    begin
      Self.Color := clYellow;
      Self.DoubleBuffered := False;

      FPnlScene := TPanel.Create(Self);
      FPnlScene.Parent := Self;
      FPnlScene.Align := alClient;
      FPnlScene.Color := clBlue;
      FPnlScene.ParentColor := False;
      FPnlScene.ParentBackground := False;
      FPnlScene.FullRepaint := False;
      FPnlScene.DoubleBuffered := False;

      FPbScene := TPaintBox.Create(Self);
      FPbScene.Parent := FPnlScene;
      FPbScene.Align := alClient;
      FPbScene.Color := clRed;
      FPbScene.ParentColor := False;

      //
      FOldPnlWndProc := Self.FPnlScene.WindowProc;
      Self.FPnlScene.WindowProc := Self.PnlWndProc;

      Self.FPbScene.OnMouseMove := Self.OnSceneMouseMove;
      Self.FPbScene.OnPaint := Self.OnScenePaint;

    end;

    procedure TForm1.PnlWndProc(var Message: TMessage);
    begin
      if Message.Msg = WM_ERASEBKGND then
      begin
        OutputDebugStringW('WM_ERASEBKGND');
        Message.Result := 1;
      end
      else
        FOldPnlWndProc(Message);
    end;

    procedure TForm1.OnSceneMouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    begin
      FPbScene.Repaint;
    end;

    procedure TForm1.OnScenePaint(Sender: TObject);
    begin
      FPbScene.Canvas.FillRect(FPbScene.ClientRect);
      FPbScene.Canvas.Ellipse(50, 50, 150, 150);
    end;

    end.

通常的技术是使用 form.DoubleBuffered,我看到你已经在代码中这样做了,所以如果那么简单,我认为你已经解决了它。

我认为除了从屏幕外位图直接拉伸绘制到 Paintbox.Canvas 上之外,还可以避免 OnPaint 中的任何操作。 OnPaint 中的任何其他内容都可能是引起闪烁的错误。这意味着,在 OnPaint 中不会修改 TBitmap。让我再说第三次;不要更改绘制事件中的状态。绘制事件应包含“位图位图传输”操作、GDI 矩形和线条调用等,但仅此而已。

我犹豫是否向任何人推荐他们尝试使用 WM_SETREDRAW,但这是人们使用的一种技术。您可以捕获移动/调整窗口大小事件或消息,并打开/关闭 WM_SETREDRAW,但这充满了复杂性和问题,因此我不推荐它。您还可以调用各种Win32函数来锁定窗口,这些都是非常危险的,不推荐。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何消除TPaintBox右边缘的闪烁(例如调整大小时) 的相关文章

  • 在 Outlook 中创建带有附件的邮件并显示它

    我想在 Outlook 中创建一封带有附件的邮件并在发送之前显示它 但我想我已经尝试了几乎在网上找到的所有示例 但没有任何运气 我可以使用 Indy 但我非常想使用 Outlook 来确保邮件正确 因为它是用于商业用途 函数的任何输入 该函
  • Winform 没有.NET 框架?

    我必须创建一些表单并将其作为直接 EXE 提供 而不是安装程序 它安装 NET 框架 最终用户对此不满意 他们想要可以直接打开和工作的东西 我知道它可以作为网络完成 但我正在寻找 winforms 吗 请建议哪种工具 技术可以处理这个问题
  • 调用泛型类型的方法?

    为什么下面的代码在 Delphi XE 中会产生错误 unit UTest interface type TTest class public procedure Foo
  • 能否从 Vista Shell 获取 48x48 或 64x64 图标?

    如果 Vista Shell 中存在 48x48 或 64x64 图标 如何使用 SHGetFileInfo 获取在 TImage 中显示图标的句柄 我想从图像列表中选择一个代表文件夹路径的图标 并在 Timage 中显示 48x48 或
  • 为应用程序启用主题

    我有一个旧的应用程序 在Win XP中的delphi 7中启动 现在我正在使用delphi 2009 win Vista 如果我开始一个新项目 所有按钮都有一个圆形边缘 但在我的旧应用程序中 所有按钮都有 方形 形状的外观 有什么设置我错过
  • 如何将 TGifImage 中的帧提取为位图?

    下面的演示尝试在表单的画布上绘制 GIF 这不起作用 图像不会前进 如何让它发挥作用 procedure TForm1 FormCreate Sender TObject begin GIF TGIFImage Create GIF Loa
  • Delphi中的抽象类

    我正在使用一个具有许多抽象类的组件套件 现在我想应用多态性 但在创建对象时收到错误抽象类 即使我不需要 我是否应该重写所有虚拟方法 有什么解决方法或解决方案吗 为了创建类的实例 您需要重写所有声明为虚拟抽象的方法 即使您不使用它们 如果您确
  • 在 Delphi 2007 中将具有透明度的位图保存为 PNG

    我有一个包含透明度信息的 Delphi 位图 32 位 我需要将其转换并保存为 PNG 文件 同时保留透明度 我目前拥有的工具是graphics32 Library GR32 PNG 由Christian Budde 提供 和PNGImag
  • 在TImageViewer中,如何获取用户点击图片的位置?

    在TImageViewer控件中 用户可以缩放或平移图片 我的问题是 当用户点击图片时 如何获取用户在图片上的点击位置 尤其是用户可以对图片进行放大 缩小或平移之后 如何获取对应的图片点击位置呢 As shown below How to
  • Delphi - 如何获取 USB 可移动硬盘和记忆棒的列表?

    在我的应用程序 Delphi 中 我需要列出所有 USB 存储设备 这些可以是闪存棒or外部存储驱动器 有一个Jvcl成分JvDriveCombo 并且它有DriveType属性 问题是我是否选择DriveType Fixed那么除了外部驱
  • 是否可以声明长度受限且不从 0/1 开始的字符串类型?

    在 Delphi 中 可以声明整数值的子范围 例如 type myInt 2 150 它将 myInt 类型的值限制为 2 到 150 之间的值 但是如果我想限制字符串的长度怎么办 如果我写 type myString string 150
  • 如何释放 TInterfacedObject 中的 TObject 成员

    我知道接口对象是引用计数的 因此不需要手动释放它 但如果它有一个 TObject 继承成员 我是否应该在析构函数中手动释放该成员 考虑以下代码 program Project2 APPTYPE CONSOLE R res uses Syst
  • Delphi应用程序窗口z顺序和MainFormOnTaskBar属性

    我正在维护一个最初用 Delphi 7 编写并移植到 Delphi XE 的应用程序 使用 Windows 7 我们遇到了一些问题 例如模态窗口出现在主窗口下方 以及最终无法与程序交互 因为用户需要与模态窗体交互 而这是不可能的 因为它位于
  • 如何用不同的颜色绘制选定的列表框项目?

    是否可以更改 TListBox 中的项目选择焦点颜色和文本颜色 当项目中未启用主题或列表框样式设置为所有者绘制时 项目周围的选择将被涂成蓝色 我相信这是由系统的外观设置全局定义的 我想将所选项目的颜色更改为自定义颜色 举个例子 结果会是这样
  • 如何使用 FieldDefs 在运行时创建新的 SQLite 文件和表?

    我正在使用 Delphi Seattle 在全新的 SQLite 文件中创建一个全新的表 并且仅使用 FieldDefs 和非可视代码 我可以使用 ExecSQL CREATE TABLE 语法创建一个表 但不能如下所示 我得到 没有这样的
  • 防止多个实例 - 但还要处理命令行参数?

    我正在从我的应用程序处理与 Windows 相关的扩展文件 因此 当您在 Windows 中双击文件时 它将执行我的程序 然后我从那里处理该文件 如下所示 procedure TMainForm FormCreate Sender TObj
  • 使用 PutBlock 并将字节数组全部设置为零的 EIPHTTPProtocolExceptionPeer 异常

    使用 Delphi XE2 Update 3 我在将零字节块上传到 Azure 时遇到问题 当我说零字节时 我指的是每个元素设置为零的字节数组 不是零长度 例如 var ConInfo TAzureConnectionInfo RespIn
  • Delphi - Indy - 保存 GMail 草稿

    我一直在 Delphi 下使用 Indy 通过 gmail 帐户发送消息 使用 TIdSMTP 和 TIdMessage 组件 这绝对没问题 但是 我的客户请求将消息保存到 DRAFTS 文件夹 以便他在实际发送消息之前对 以编程方式创建的
  • Delphi 的内存分析工具?

    我建立了一个项目并运行它 然后在 Process Explorer 中查看它 结果发现它在启动时使用的 RAM 比我想象的要多 5 倍 现在 如果我的程序运行得太慢 我会将其连接到分析器并让它告诉我什么正在使用我的所有周期 有没有类似的工具
  • 如果加载 dll 找不到依赖项,有什么方法可以捕获错误吗?

    我正在编写一个 Windows 32 位程序 可以使用多个可能的 dll 之一 所以它尝试依次加载每个 dll 使用SysUtils SafeLoadLibrary如果加载成功 它就会使用该 dll 不幸的是 其中一些 dll 静态链接到其

随机推荐

  • 如何在 mod_rewrite 中设置可选参数

    我在一个新项目中 正在设计 URL 结构 问题是我希望 URL 看起来像这样 category 23 keyword 5 正常页面是 search php q keyword cat 23 page 5所以我的问题是 cat and pag
  • 不同类别因素的欧几里得距离按组迭代

    更新 Rui 建议的答案很棒并且可以正常工作 然而 当我在大约 700 万个观察值 我的实际数据集 上运行它时 R 陷入了计算块 我使用的是具有 64GB RAM 的机器 任何其他解决方案将不胜感激 我有一个专利数据框 其中包含公司 申请年
  • 首先按 null 排序,然后按其他变量排序

    这是我现在的代码 SELECT id number FROM Media WHERE user 10 ORDER BY id number 但我希望它看起来像 SELECT id number FROM Media WHERE user 1
  • 如何隐藏 F# 中的方法?

    我目前正在 F 中实现 Spec 框架 我想隐藏我的 Equals GetHashCode 等方法should类型 以便 API 不会因这些而混乱 我知道在 C 中 这是通过让类实现如下接口来完成的 using System using S
  • 在 Nuget 包中公开 Azure Functions

    我们希望在我们的不止一种产品中实现可重用的功能 我想做的是 创建一个包含一个或多个 Azure Functions 附加了 FunctionNameAttribute 的静态方法 的 C 项目 将此项目转为NuGet包 在 Azure Fu
  • 从前序和后序列表重建树

    考虑这样一种情况 您有两个节点列表 您只知道其中一个是某棵树的前序遍历的表示 另一个是同一棵树的后序遍历的表示 我相信可以从这两个列表精确地重建树 并且我认为我有一个算法可以做到这一点 但尚未证明 由于这将是硕士项目的一部分 我需要绝对确定
  • Node Mongo Native - 如何判断游标何时耗尽?

    的文档节点 mongodb nativecollection find 功能 https github com mongodb node mongodb native find说它创建一个游标对象 该对象延迟返回匹配的文档 此外 游标的基本
  • tkinter 小部件的 cnf 参数

    所以 我正在研究代码here http svn python org projects python branches pep 0384 Lib tkinter init py在每个班级 几乎 我都看到一个争论cnf 到构造函数 但除非我错
  • org-mode取消\hypersetup后有什么影响?

    我用自己的序言在 org 模式下制作 pdf 但生成的 PDF 或 tex 文件始终显示以下信息 format hypersetup n pdfkeywords s n pdfsubject s n pdfcreator s n org e
  • 如何创建具有延迟的可观察对象

    Question 出于测试目的 我正在创建Observable替换实际 http 调用返回的可观察对象的对象Http 我的可观察对象是使用以下代码创建的 fakeObservable Observable create obs gt obs
  • 什么是 gitlab runner

    我想我从根本上错过了一些东西 我是 CI CD 新手 正在尝试使用 gitlab 建立我的第一个管道 该项目是一个预先存在的 PHP 项目 我还不想清理它 目前我已经将整个东西推入了 docker 容器 并且它与谷歌云的 mysql 数据库
  • 模拟跨上下文连接--LINQ/C#

    问题是这样的 我有 2 个数据上下文 我想对其进行联接 现在我知道 LINQ 不允许从一个上下文连接到另一个上下文 并且我知道有 2 种可能的解决方案是创建单个数据上下文或有 2 个单独的查询 这就是我现在正在做的事情 然而我想做的是 模拟
  • 如何管理 git 中的重叠存储库,包括同一目录中的文件?

    我有一个复杂的存储库 有时代码段之间的逻辑边界跨越目录边界 有时目录 X 中的单个文件确实需要与目录 Y 中的文件一起使用 例如 假设我有一个如下所示的中央存储库 a foo a bar b baz1 b baz2 我希望我的本地存储库最终
  • 如何通过 Curl 和 PHP 发送 SOAP XML?

    这已经困扰我好几天了 我正在尝试通过 Curl 发送 SOAP 帖子 但我总是收到 无法连接到主机 错误 但是 我真的不知道如何解决 我有一个 ASP 版本 它可以在相同的 URL 和数据下正常工作 我认为这只是 PHP Curl 的事情
  • AWS Lambda Python 3.7 运行时异常日志记录

    使用 Python 3 7 运行时时引发的未处理异常似乎不会像在 Python 3 6 中那样记录到 CloudWatch 如何在 Python 3 7 中设置记录器来捕获此信息 还发布在 AWS 论坛上 https forums aws
  • pytorch index_put_给出运行时错误:“索引”的导数未实现

    这是后续问题这个问题 https stackoverflow com q 65584330 3337089 我尝试使用index put 如建议的答案 https stackoverflow com a 65584479 3337089 但
  • 当有很多要发送的值时,将值传递给函数的最佳方法是什么?

    当您必须将许多值传递给函数并且其中一些值可能是可选的时 定义方法签名的最佳方法是什么 将来 我可能必须传递更多变量或减去一些传递给函数的值 例如 电话和地址可选 function addInfo name dob phone address
  • 针对 R+(版本 30 及更高版本)要求已安装 APK 的 resources.arsc 未压缩存储并在 4 字节边界上对齐

    我正在尝试将 android 目标 API 从 29 更新到 30 我已更新 compileSdkVersion 30 targetSdkVersion 30 buildToolsVersion 30 0 2 该应用程序与zipalign
  • cocoa 应用程序中提示 root 访问权限

    我希望我的程序以要求 root 访问权限的提示 警报开始 用户必须输入密码 然后应用程序就会启动 我一直在环顾四周 但我不太确定该怎么做 非常感谢您的帮助 Thanks 这是苹果公司关于此事的文档 http developer apple
  • 如何消除TPaintBox右边缘的闪烁(例如调整大小时)

    总结 假设我有一个 TForm 和两个面板 面板对齐 alTop 和 alClient alClient面板包含一个TPaintBox 其OnPaint涉及绘图代码 组件上 DoubleBuffered 的默认值为 false 在绘制过程中