Delphi TLabel和TGroupbox标题在调整大小时闪烁 因此,我有一个应用程序,它加载不同的插件并创建 TPageControl上的每个新选项卡 每个DLL都有一个与其关联的TForm 创建表单时,将其父hWnd作为新的TTabSheet
因为就VCL而言,TTabSheets不是表单的父级(不想使用动态RTL和其他语言制作的插件),所以我必须手动处理大小调整。我是这样做的:Delphi TLabel和TGroupbox标题在调整大小时闪烁 因此,我有一个应用程序,它加载不同的插件并创建 TPageControl上的每个新选项卡 每个DLL都有一个与其关联的TForm 创建表单时,将其父hWnd作为新的TTabSheet,delphi,delphi-xe,flicker,groupbox,tpagecontrol,Delphi,Delphi Xe,Flicker,Groupbox,Tpagecontrol,因为就VCL而言,TTabSheets不是表单的父级(不想使用动态RTL和其他语言制作的插件),所以我必须手动处理大小调整。我是这样做的: var ChildHandle : DWORD; begin If Assigned(pcMain.ActivePage) Then begin ChildHandle := FindWindowEx(pcMain.ActivePage.Handle, 0, 'TfrmPluginForm', nil); If ChildHa
var
ChildHandle : DWORD;
begin
If Assigned(pcMain.ActivePage) Then
begin
ChildHandle := FindWindowEx(pcMain.ActivePage.Handle, 0, 'TfrmPluginForm', nil);
If ChildHandle > 0 Then
begin
SetWindowPos(ChildHandle, 0, 0, 0, pcMain.ActivePage.Width, pcMain.ActivePage.Height, SWP_NOZORDER + SWP_NOACTIVATE + SWP_NOCOPYBITS);
end;
end;
现在,我的问题是,当应用程序调整大小时,所有的TgroupBox和TgroupBox中的TLabel都会闪烁。不在TGroupBox内的TLabel很好,不会闪烁
我尝试过的事情:
- WM_SETREDRAW后面跟着一个重画窗口
- tGroupBox和tLabel上的ParentBackground设置为False
- 双缓冲区:=真
- LockWindowUpdate(是的,尽管我知道它非常错误)
- 透明:=False(甚至覆盖create以编辑ControlState)
SWP_NOCOPYBITS
标志,并设置页面控件的DoubleBuffered
:
uses
VCLFixPack;
procedure TForm1.FormCreate(Sender: TObject);
begin
PageControl1.DoubleBuffered := True;
//Setup test conditions:
FForm2 := TForm2.Create(Self);
FForm2.BorderStyle := bsNone;
FForm2.BoundsRect := TabSheet1.ClientRect;
Windows.SetParent(FForm2.Handle, TabSheet1.Handle);
FForm2.Show;
PageControl1.Anchors := [akLeft, akTop, akRight, akBottom];
PageControl1.OnResize := PageControl1Resize;
end;
procedure TForm1.PageControl1Resize(Sender: TObject);
begin
SetWindowPos(FForm2.Handle, 0, 0, 0, TabSheet1.ClientWidth,
TabSheet1.ClientHeight, SWP_NOZORDER + SWP_NOACTIVATE);
end;
我发现唯一有效的方法是使用
WS\u EX\u COMPOSITED
窗口样式。这是一个性能限制,所以我只在调整大小循环中启用它。根据我的经验,在我的应用程序中,使用内置控件,只有在调整表单大小时才会出现闪烁
您应该首先执行一个快速测试,看看这种方法是否可以通过简单地将WS_EX_COMPOSITED
窗口样式添加到所有窗口控件中来帮助您。如果这样的话,你可以考虑下面更先进的方法:
快速黑客
procedure EnableComposited(WinControl: TWinControl);
var
i: Integer;
NewExStyle: DWORD;
begin
NewExStyle := GetWindowLong(WinControl.Handle, GWL_EXSTYLE) or WS_EX_COMPOSITED;
SetWindowLong(WinControl.Handle, GWL_EXSTYLE, NewExStyle);
for i := 0 to WinControl.ControlCount-1 do
if WinControl.Controls[i] is TWinControl then
EnableComposited(TWinControl(WinControl.Controls[i]));
end;
例如,在OnShow
中为您的TForm
调用此函数,并传递表单实例。如果这有帮助的话,那么你真的应该更敏锐地实施它。我将从我的代码中提供相关摘录,以说明我是如何做到这一点的
完整代码
procedure TMyForm.WMEnterSizeMove(var Message: TMessage);
begin
inherited;
BeginSizing;
end;
procedure TMyForm.WMExitSizeMove(var Message: TMessage);
begin
EndSizing;
inherited;
end;
procedure SetComposited(WinControl: TWinControl; Value: Boolean);
var
ExStyle, NewExStyle: DWORD;
begin
ExStyle := GetWindowLong(WinControl.Handle, GWL_EXSTYLE);
if Value then begin
NewExStyle := ExStyle or WS_EX_COMPOSITED;
end else begin
NewExStyle := ExStyle and not WS_EX_COMPOSITED;
end;
if NewExStyle<>ExStyle then begin
SetWindowLong(WinControl.Handle, GWL_EXSTYLE, NewExStyle);
end;
end;
function TMyForm.SizingCompositionIsPerformed: Boolean;
begin
//see The Old New Thing, Taxes: Remote Desktop Connection and painting
Result := not InRemoteSession;
end;
procedure TMyForm.BeginSizing;
var
UseCompositedWindowStyleExclusively: Boolean;
Control: TControl;
WinControl: TWinControl;
begin
if SizingCompositionIsPerformed then begin
UseCompositedWindowStyleExclusively := Win32MajorVersion>=6;//XP can't handle too many windows with WS_EX_COMPOSITED
for Control in ControlEnumerator(TWinControl) do begin
WinControl := TWinControl(Control);
if UseCompositedWindowStyleExclusively then begin
SetComposited(WinControl, True);
end else begin
if WinControl is TPanel then begin
TPanel(WinControl).FullRepaint := False;
end;
if (WinControl is TCustomGroupBox) or (WinControl is TCustomRadioGroup) or (WinControl is TCustomGrid) then begin
//can't find another way to make these awkward customers stop flickering
SetComposited(WinControl, True);
end else if ControlSupportsDoubleBuffered(WinControl) then begin
WinControl.DoubleBuffered := True;
end;
end;
end;
end;
end;
procedure TMyForm.EndSizing;
var
Control: TControl;
WinControl: TWinControl;
begin
if SizingCompositionIsPerformed then begin
for Control in ControlEnumerator(TWinControl) do begin
WinControl := TWinControl(Control);
if WinControl is TPanel then begin
TPanel(WinControl).FullRepaint := True;
end;
UpdateDoubleBuffered(WinControl);
SetComposited(WinControl, False);
end;
end;
end;
function TMyForm.ControlSupportsDoubleBuffered(Control: TWinControl): Boolean;
const
NotSupportedClasses: array [0..1] of TControlClass = (
TCustomForm,//general policy is not to double buffer forms
TCustomRichEdit//simply fails to draw if double buffered
);
var
i: Integer;
begin
for i := low(NotSupportedClasses) to high(NotSupportedClasses) do begin
if Control is NotSupportedClasses[i] then begin
Result := False;
exit;
end;
end;
Result := True;
end;
procedure TMyForm.UpdateDoubleBuffered(Control: TWinControl);
function ControlIsDoubleBuffered: Boolean;
const
DoubleBufferedClasses: array [0..2] of TControlClass = (
TMyCustomGrid,//flickers when updating
TCustomListView,//flickers when updating
TCustomStatusBar//drawing infidelities , e.g. my main form status bar during file loading
);
var
i: Integer;
begin
if not InRemoteSession then begin
//see The Old New Thing, Taxes: Remote Desktop Connection and painting
for i := low(DoubleBufferedClasses) to high(DoubleBufferedClasses) do begin
if Control is DoubleBufferedClasses[i] then begin
Result := True;
exit;
end;
end;
end;
Result := False;
end;
var
DoubleBuffered: Boolean;
begin
if ControlSupportsDoubleBuffered(Control) then begin
DoubleBuffered := ControlIsDoubleBuffered;
end else begin
DoubleBuffered := False;
end;
Control.DoubleBuffered := DoubleBuffered;
end;
procedure TMyForm.UpdateDoubleBuffered;
var
Control: TControl;
begin
for Control in ControlEnumerator(TWinControl) do begin
UpdateDoubleBuffered(TWinControl(Control));
end;
end;
过程TMyForm.WMEnterSizeMove(var消息:TMessage);
开始
继承;
开始化;
结束;
过程TMyForm.WMExitSizeMove(var消息:TMessage);
开始
端部尺寸;
继承;
结束;
程序集合成(WinControl:TWinControl;值:布尔值);
变量
ExStyle,NewExStyle:DWORD;
开始
ExStyle:=GetWindowLong(WinControl.Handle,GWL_ExStyle);
如果值为,则开始
NewExStyle:=ExStyle或WS_EX_COMPOSITED;
结束,否则开始
NewExStyle:=ExStyle且未合成WS_EX_;
结束;
如果是NewExStyleExStyle,则开始
SetWindowLong(WinControl.Handle、GWL_EXSTYLE、NewExStyle);
结束;
结束;
函数TMyForm.sizingCompositionPerformed:布尔值;
开始
//看看老的新的东西,税收:远程桌面连接和绘画
结果:=未恢复;
结束;
程序TMyForm.BeginSizing;
变量
UseCompositedWindowsTypeExclusive:布尔值;
控制:t控制;
WinControl:双控;
开始
如果执行了SizingComposition,则开始
UseCompositedWindowsTypeExclusive:=Win32MajorVersion>=6//XP无法处理太多WS_EX_合成的窗口
对于ControlEnumerator(TWinControl)中的控件,请执行begin
WinControl:=TWinControl(控制);
如果使用CompositedWindowsTypeExclusive,则开始
设置合成(WinControl,真);
结束,否则开始
如果WinControl为TPanel,则开始
TPanel(WinControl).FullRepaint:=假;
结束;
如果(WinControl为TCustomGroupBox)或(WinControl为TCustomRadioGroup)或(WinControl为TCustomGrid),则开始
//找不到其他方法让这些笨拙的顾客停止闪烁
设置合成(WinControl,真);
如果控制支持双缓冲(WinControl),则结束else,然后开始
WinControl.DoubleBuffered:=真;
结束;
结束;
结束;
结束;
结束;
程序TMyForm.EndSize;
变量
控制:t控制;
WinControl:双控;
开始
如果执行了SizingComposition,则开始
对于ControlEnumerator(TWinControl)中的控件,请执行begin
WinControl:=TWinControl(控制);
如果WinControl为TPanel,则开始
TPanel(WinControl).FullRepaint:=真;
结束;
更新双缓冲(WinControl);
SetComposited(WinControl,假);
结束;
结束;
结束;
函数TMyForm.ControlSupportsDoubleBuffered(控件:TWinControl):布尔值;
常数
NotSupportedClasses:TControlClass的数组[0..1](
TCustomForm,//一般策略是不将缓冲区窗体加倍
TCustomRichEdit//如果使用双缓冲,则无法绘制
);
变量
i:整数;
开始
对于i:=低(NotSupportedClass)到高(NotSupportedClass)不开始
如果不支持控件类[i],则开始
结果:=假;
出口
结束;
结束;
结果:=真;
结束;
程序TMyForm.UpdateDoubleBuffered(控制:TWinControl);
函数控件双缓冲:布尔值;
常数
DoubleBufferedClasses:TControlClass的数组[0..2](
TMyCustomGrid,//更新时闪烁
TCustomListView,//更新时闪烁
TCustomStatusBar//图形不真实,例如文件加载期间的我的主窗体状态栏
);
变量
i:整数;
开始
如果不在RemoteSession中,则开始
//看看老的新的东西,税收:远程桌面连接和绘画
对于i:=低(双缓冲类)到高(双缓冲类)不开始
如果控件为DoubleBufferedClasses[i],则开始
结果:=真;
出口
结束;
结束;
结束;
结果:=假;
结束;
变量
双缓冲:布尔;
开始
如果控制支持双缓冲(控制),则开始
双重缓冲:=控制双重缓冲;
结束,否则开始
双缓冲:=假;
结束;
Control.DoubleBuffered:=双缓冲;
结束;
程序TMyForm.UpdateDoubleBuffered;
变量
控制:t控制;
开始
用于ControlEnumerator(TWinControl)中的控件
typedef LRESULT CALLBACK(*PWndProc)(HWND, UINT, WPARAM, LPARAM);
void SetNonFlickeringWndProc(TWinControl &control, std::map<HWND,PWndProc> &list, PWndProc new_proc)
{
if (control.Handle == 0)
{
return;
}
PWndProc oldWndProc = (PWndProc)SetWindowLong(control.Handle, GWL_WNDPROC, (LONG)new_proc);
list[control.Handle] = oldWndProc;
int count = control.ControlCount;
for (int i = 0; i < count; i++)
{
TControl *child_control = control.Controls[i];
TWinControl *child_wnd_control = dynamic_cast<TWinControl*>(child_control);
if (child_wnd_control == NULL)
{
continue;
}
SetNonFlickeringWndProc(*child_wnd_control, list, new_proc);
}
}
void RestoreWndProc(std::map<HWND,PWndProc> &old_wnd_proc)
{
std::map<HWND,PWndProc>::iterator it;
for (it = old_wnd_proc.begin(); it != old_wnd_proc.end(); it++)
{
LONG res = SetWindowLong(it->first, GWL_WNDPROC, (LONG)it->second);
}
old_wnd_proc.clear();
}
std::map<HWND,PWndProc> oldwndproc; // addresses for window procedures for all components in form
LRESULT CALLBACK NonFlickeringWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == WM_ERASEBKGND)
{
return 1;
}
return ((PWndProc)oldwndproc[hwnd])(hwnd, uMsg, wParam, lParam);
}
void __fastcall TForm1::FormShow(TObject *Sender)
{
oldwndproc.clear();
SetNonFlickeringWndProc(*this, oldwndproc, &NonFlickeringWndProc);
}
void __fastcall TForm1::FormClose(TObject* Sender, TCloseAction& Action)
{
RestoreWndProc(oldwndproc_etype);
}
TLabel = class( stdCtrls.TLabel )
protected
procedure WMEraseBkgnd(var Message: TWmEraseBkgnd); message WM_ERASEBKGND;
end;
procedure TLabel.WMEraseBkgnd(var Message: TWmEraseBkgnd);
begin
Message.Result:=1; // Fake erase
end;