左侧窗口的无闪烁扩展(调整大小) [英] Flicker-free expansion (resize) of a window to the left

查看:77
本文介绍了左侧窗口的无闪烁扩展(调整大小)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设您有一个表格,可以将其扩展到左侧以显示其他控件:

Say you have a form that you can expand to the left to show additional controls:

崩溃了:

扩展后:

在Delphi中实现此目的的最简单方法是使用 alRight 作为所有控件的主要锚点(而不是 alLeft ),然后只需调整表单的宽度和X坐标即可。您既可以分别设置 Width Left 属性,也可以使用同时设置它们的函数,例如

The simplest way to achieve this in Delphi is to use alRight as the primary anchor for all controls (instead of alLeft) and then simply adjust the width and X coordinate of the form. Either you can set the Width and Left properties individually, or you can use a function that sets them simultaneously, like

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)

问题是,在展开或折叠时,表单的始终可见部分(在此示例中为按钮)有明显的闪烁。

The problem is that there is quite noticeable flickering in the always-visible part of the form (in this example, the buttons) while expanding or collapsing. Try it yourself!

操作系统可能会向左调整窗体的大小而根本没有闪烁-只需使用鼠标左键即可抓住窗体的左边缘鼠标并向左或向右拖动鼠标-但我无法在Windows API中找到任何能显示这种调整大小的函数。

It is possible for the operating system to resize the form to the left without any flickering at all -- just grab the left edge of the form using the mouse and drag the mouse to the left or right -- but I am unable to find any function in the Windows API that exposes this kind of resizing.

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

I have tried to use several different Windows API functions to resize and reposition the form, tried their various parameters (for instance, the SWP_* flags), tried LockWindowUpdate, WM_SETREDRAW, TForm.DoubleBuffered etc. to no avail. I also examined the possibility to use the WM_SYSCOMMAND SC_SIZE approach.

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

I am not yet sure if the problem lies at the OS level or the VCL level.

有什么建议吗?

编辑:我很惊讶地看到这个Q获得了密切投票。让我尝试澄清一下:

I am very surprised to see that this Q received close votes. Let me try to clarify:


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

  1. Create a new VCL forms application.

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

Add a few buttons to the right side of the main form and a memo to the left. Set Anchors to [alTop, alRight] on all controls. On the OnClick handler of the buttons, add the following code:

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 是形式(初始化为 false )。

where FCollapsed is private boolean field of the form (initialized to false).

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

Now, click the buttons repeatedly. (Or give one of them keyboard focus and hold the Enter key for a few seconds.) You will probably notice that the region with the buttons on your monitor will not display a perfect still image, but will flicker. In addition, you might actually see 'ghosts' of the buttons to the left of the actual column of buttons.

我无法使用屏幕捕获来捕获毫秒级的闪烁,因此我改用数码相机来记录我的屏幕:

I am unable to capture this millisecond flickering using screen capture, so instead I used a digital camera to record my screen:

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

在此视频中剪辑,很明显按钮列不是屏幕上的静态图像;而是,每次调整表单大小时,都需要花费几毫秒的时间,而该区域不是应该的。同样明显的是,按钮的左侧有一个幽灵列。

In this video clip, it is apparent that the column of buttons isn't a static image on the screen; instead, for a few milliseconds each time the form is resized, this region is something else than it should be. It is equally apparent that there is a 'ghost' column of buttons to the left.

我的问题是,是否有任何合理的简单方法可以消除这些视觉假象(即使您一次展开/折叠表单,至少对我来说也是非常明显的。)

My question is if there is any reasonably simple way to get rid of these visual artefacts (that at least to me are very visible even if you expand/collapse the form a single time).

在我工作的Windows 10 / Delphi 10.1计算机上,该表单当我使用鼠标拖动其最左侧的边缘时,将以完美的方式调整大小:窗体的未受影响的客户区域在监视器上是完全静态的。但是,在家里的Windows 7 / Delphi 2009 PC上,我确实看到有很多重新定位的过程。

On my Windows 10/Delphi 10.1 computer at work, the form is resized in a perfect manner when I drag its left-most edge using the mouse: the unaffected client area of the form is perfectly static on the monitor. However, on my Windows 7/Delphi 2009 PC at home, I do see that there is a lot of repositioning going on when I do this.

推荐答案

我可以为您提供一些见解,以了解为什么您会看到用户界面另一半的幻影,并可能有一种阻止它的方法。重影表示有人在复制您的客户区域像素(并将其复制到错误的位置,始终在窗口中向左平移),然后才有机会用正确的像素重绘它们。

I can provide some insight about why you see ghost images of the other half of your UI and possibly a way to stop it. The ghost image indicates that someone is copying your client area pixels (and copying them to the wrong place, always flush-left in your window) before you have a chance to redraw them with the correct pixels.

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

There are likely two different, overlapping sources of these ghost pixels.

第一层适用于所有Windows操作系统,来自<$ c SetWindowPos 中的$ c> BitBlt 。您可以通过几种方式摆脱该 BitBlt 。您可以创建自己的 WM_NCCALCSIZE 的自定义实现,以告诉Windows不进行盲处理(或对自身顶部进行消隐处理),或者可以拦截 WM_WINDOWPOSCHANGING (首先将其传递到 DefWindowProc )并设置 WINDOWPOS.flags | = SWP_NOCOPYBITS ,这会禁用Windows在调整窗口大小期间对 SetWindowPos()进行的内部调用中的 BitBlt 。最终具有跳过 BitBlt 的最终效果。

The first layer applies to all Windows OSes and comes from a BitBlt inside SetWindowPos. You can get rid of that BitBlt in several ways. You can create your own custom implementation of WM_NCCALCSIZE to tell Windows to blit nothing (or to blit one pixel on top of itself), or alternately you can intercept WM_WINDOWPOSCHANGING (first passing it onto DefWindowProc) and set WINDOWPOS.flags |= SWP_NOCOPYBITS, which disables the BitBlt inside the internal call to SetWindowPos() that Windows makes during window resizing. This has the same eventual effect of skipping the BitBlt.

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

However, Windows 8/10 aero adds another, more troublesome layer. Apps now draw into an offscreen buffer which is then composited by the new, evil DWM.exe window manager. And it turns out DWM.exe will sometimes do its own BitBlt type operation on top of the one already done by the legacy XP/Vista/7 code. And stopping DWM from doing its blit is much harder; so far I have not seen any complete solutions.

对于将突破XP / Vista / 7层并至少提高8/10性能的示例代码层,请参阅:

For sample code that will break through the XP/Vista/7 layer and at least improve the performance of the 8/10 layer, please see:

调整窗口大小时,如何平滑丑陋的抖动/闪烁/跳跃(特别是拖动左/上边框(Win 7-10; bg,bitblt和DWM)?

由于您有多个子窗口,因此情况甚至更加复杂。我上面提到的 BitBlt 类型操作会在整个顶级窗口中整体发生(无论下面有多少个窗口,它们都将其视为一组像素),并且不论 CLIPCHILDREN )。但是您需要使窗口原子移动,以便在下一次重绘时将它们都正确定位。您可能会发现 BeginDeferWindowPos / DeferWindowPos / EndDeferWindowPos 对此有用(但只有在上述技巧无效的情况下才去那里)。

Since you have multiple child windows, the situation is even a little more complicated. The BitBlt type operations I mentioned above happen on your whole top-level window as a whole (they treat the window as one set of pixels regardless of how many windows are underneath, and regardless of CLIPCHILDREN). But you need to have windows move atomically so that on the next redraw they are all positioned correctly. You may find BeginDeferWindowPos/DeferWindowPos/EndDeferWindowPos useful for that (but only go there if the above tricks do not work).

这篇关于左侧窗口的无闪烁扩展(调整大小)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆