gpt4 book ai didi

windows - 窗口向左无闪烁扩展(调整大小)

转载 作者:可可西里 更新时间:2023-11-01 10:42:01 27 4
gpt4 key购买 nike

假设您有一个可以向左展开以显示其他控件的表单:

折叠:

Collapsed form

展开:

Expanded form

在 Delphi 中实现这一点的最简单方法是使用 alRight 作为所有控件的主要 anchor (而不是 alLeft),然后简单地调整宽度和 X 坐标的形式。您可以单独设置 WidthLeft 属性,也可以使用同时设置它们的函数,例如

if FCollapsed then
SetWindowPos(Handle, 0, Left - Width, Top, 2 * Width, Height, 0)
else
SetWindowPos(Handle, 0, Left + Width div 2, Top, Width div 2, Height, 0)

问题 是在展开或折叠时窗体始终可见的部分(在本例中为按钮)有相当明显的闪烁。自己试试吧!

操作系统可以将窗体的大小调整到左边而根本没有任何闪烁——只需使用鼠标捕获窗体的左边缘,然后向左或向右拖动鼠标——但我做不到在 Windows API 中找到公开这种调整大小的任何函数。

我尝试使用几个不同的 Windows API 函数来调整窗体的大小和位置,尝试了它们的各种参数(例如,SWP_* 标志),尝试了 LockWindowUpdate , WM_SETREDRAW, TForm.DoubleBuffered 等无济于事。我还研究了使用 WM_SYSCOMMAND SC_SIZE 方法的可能性。

我还不确定问题出在操作系统级别还是 VCL 级别。

有什么建议吗?

编辑:看到这个问题获得了非常接近的选票,我感到非常惊讶。让我试着澄清一下:

  1. 创建一个新的 VCL 表单应用程序。

  2. 在主窗体的右侧添加几个按钮,在左侧添加一个备忘录。在所有 控件上将Anchors 设置为[alTop, alRight]。在按钮的 OnClick 处理程序上,添加以下代码:

    if FCollapsed then
    SetWindowPos(Handle, 0, Left - Width, Top, 2 * Width, Height, 0)
    else
    SetWindowPos(Handle, 0, Left + Width div 2, Top, Width div 2, Height, 0);

    FCollapsed := not FCollapsed;

    其中 FCollapsed 是表单的私有(private) bool 字段(初始化为 false)。

  3. 现在,重复单击按钮。 (或者给其中一个键盘焦点并按住 Enter 键几秒钟。)您可能会注意到显示器上带有按钮的区域不会显示完美的静止图像,但会闪烁.此外,您实际上可能会在实际按钮列的左侧看到按钮的“重影”。

我无法使用屏幕捕捉捕捉到这一毫秒的闪烁,所以我使用数码相机来记录我的屏幕:

https://privat.rejbrand.se/VCLFormExpandFlicker.mp4

在这个视频片段中,显然按钮列不是屏幕上的静态图像;取而代之的是,每次调整表单大小时,该区域都会有几毫秒的变化。同样明显的是,左侧有一列“幽灵”按钮。

我的问题是是否有任何相当简单的方法来消除这些视觉人工制品(至少对我来说是非常明显的,即使您展开/折叠表单一次)。

在我工作的 Windows 10/Delphi 10.1 计算机上,当我使用鼠标拖动窗体的最左边缘时,窗体以完美的方式调整大小:窗体未受影响的客户区在显示器上完全静止。但是,在我家里的 Windows 7/Delphi 2009 PC 上,我确实看到当我这样做时有很多重新定位。

最佳答案

我可以提供一些见解,说明为什么您会看到另一半 UI 的重影图像,并可能提供阻止它的方法。幻影表示有人在复制您的客户区像素(并将它们复制到错误的位置,总是在您的窗口中左对齐),然后您才有机会使用正确的像素重新绘制它们。

这些重影像素可能有两个不同的、重叠的来源。

第一层适用于所有 Windows 操作系统,来自 BitBlt里面SetWindowPos .你可以去掉那个BitBlt在几个方面。您可以创建自己的自定义实现 WM_NCCALCSIZE告诉 Windows 什么都不做 blit(或者在它自己的顶部 blit 一个像素),或者你可以拦截 WM_WINDOWPOSCHANGING (首先将其传递给 DefWindowProc )并设置 WINDOWPOS.flags |= SWP_NOCOPYBITS ,这会禁用 BitBlt在对 SetWindowPos() 的内部调用中Windows 在调整窗口大小时所做的。这与跳过 BitBlt 的最终效果相同。 .

然而,Windows 8/10 aero 添加了另一个更麻烦的层。应用程序现在绘制到一个屏幕外缓冲区,然后由新的、邪恶的 DWM.exe 窗口管理器合成。事实证明 DWM.exe 有时会自己做 BitBlt在旧版 XP/Vista/7 代码已经完成的操作之上键入操作。阻止 DWM 执行它的 blit 要困难得多;到目前为止,我还没有看到任何完整的解决方案。

突破XP/Vista/7层,至少提升8/10层性能的示例代码,请看:

How to smooth ugly jitter/flicker/jumping when resizing windows, especially dragging left/top border (Win 7-10; bg, bitblt and DWM)?

因为你有多个子窗口,情况就更复杂了。 BitBlt我上面提到的类型操作发生在整个顶级窗口上(它们将窗口视为一组像素,而不管下面有多少个窗口,也不管 CLIPCHILDREN )。但是您需要让窗口自动移动,以便在下一次重绘时它们都能正确定位。你可能会发现 BeginDeferWindowPos/DeferWindowPos/EndDeferWindowPos对此很有用(但只有在上述技巧不起作用时才去那里)。

关于windows - 窗口向左无闪烁扩展(调整大小),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48825519/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com