调用Update过程时,TStatusBar闪烁。如何轻松解决这个问题

时间:2009-07-17 05:39:19

标签: windows delphi graphics controls

所以,这是我刚读过的讨论: http://www.mail-archive.com/delphi@delphi.org.nz/msg02315.html

BeginUpdate和EndUpdate不是我需要的程序......

覆盖API调用?我试图从ComCtrls单元获取更新程序代码,但没有找到坚果......

如果唯一的文字发生变化,也许你可以在这里发布一个代码来修复状态栏compoent的闪烁?我的意思是 - 像TextUpdate或某种TCanvas方法或PanelsRepaint ......?

闪烁是由此代码引起的:

Repeat
   BlockRead(Fp, BuffArrayDebug[LineIndex], DataCapac, TestByteBuff); // DataCapac = SizeOf(DWORD)
   ProgressBar1.StepIt;
   if RAWFastMode.Checked then begin       // checks for fast mode and modifyies progressbar
    if BuffArrayDebug[LineIndex] = 0 then begin ProgressBar2.Max := FileSize(Fp) - DataCapac; ProgressBar2.Position := (LineIndex + 1) * DataCapac; LineDecr := True; end;
   end else begin ProgressBar2.Max := FileSize(Fp); ProgressBar2.Position := LineIndex * DataCapac end;
   if PreviewOpn.Caption = '<' then begin  // starts data copying to preview area if expanded
    Memo1.Lines.BeginUpdate;
    if (LineIndex mod DataCapac) > 0 then HexMerge := HexMerge + ByteToHex(BuffArrayDebug[LineIndex]) else
     begin
      Memo1.Lines.Add(HexMerge); HexMerge := '';
     end;
    Memo1.Lines.EndUpdate;
   end;
   StatusBar1.Panels[0].Text := 'Line: ' + Format('%.7d',[LineIndex]) + ' | Data: ' + Format('%.3d',[BuffArrayDebug[LineIndex]]) + ' | Time: ' + TimeToStr(Time - TimeVarStart); StatusBar1.Update;
    if FindCMDLineSwitch(ParamStr(1)) then begin
     TrayIcon.BalloonTitle := 'Processing ' + ExtractFileName(RAWOpenDialog.FileName) + ' and reading ...';
     TrayIcon.BalloonHint :=  'Current Line: ' + inttostr(LineIndex) + #10#13 + ' Byte Data: ' + inttostr(TestByteBuff) + #10#13 + ' Hex Data: ' + ByteToHex(TestByteBuff);
     TrayIcon.ShowBalloonHint;
    end;
  Inc(LineIndex);
 Until EOF(Fp);

有什么想法吗?


有这个链接评论(http://www.stevetrefethen.com/blog/UsingTheWSEXCOMPOSITEWindowStyleToEliminateFlickerOnWindowsXP.aspx)并且有程序可行(没有任何闪烁),但是它是VVVVVVVEEEEEERRRRRRYYYYYY慢!

 1 type
 2   TMyForm = class(TForm)
 3   protected
 4     procedure CreateParams(var Params: TCreateParams); override;
 5   end;
 6 
 7 ...
 8 
 9 procedure TMyForm.CreateParams(var Params: TCreateParams);
10 begin
11   inherited;
12   // This only works on Windows XP and above
13   if CheckWin32Version(5, 1) then
14     Params.ExStyle := Params.ExStyle or WS_EX_COMPOSITED;
15 end;
16 

另外 - 目标不是表单,而是StatusBar ...如何将此方法分配给状态栏?

2 个答案:

答案 0 :(得分:2)

您应该检查将 TWinControl.DoubleBuffered 属性设置为TStatusBar组件的 True 是否可以正常工作。您也可以尝试将此属性启用到状态栏的父组件(可能是TForm)。这是一个盲目的镜头 - 从这里无法访问编译器。另一个想法是覆盖 WM_ERASEBKGND 消息而不调用继承的。使用谷歌后找到的第一个例子:here

-----作者评论后更新

我终于可以访问编译器了,现在它正在运行。我们可以使用WS_EX_COMPOSITED解决方案。您所需要的只是基于TCustomStatusBar创建自己的自定义组件,或者只是创建一个类包装器并在运行时创建状态栏实例。像这样:

TMyStatusBar = class( TCustomStatusBar )
protected

  { Flickering work-around }
  procedure CreateParams( var Params : TCreateParams ) ; override ;

end ;

TForm1 = class( TForm )
  // (...)
private

  FStatusBar : TMyStatusBar ;

  // (...)

end ;

-------------

procedure TMyStatusBar.CreateParams( var Params : TCreateParams ) ;
begin
  inherited ;

  if CheckWin32Version( 5,1 ) then
    Params.ExStyle := Params.ExStyle or WS_EX_COMPOSITED ;
end ;

-------------

{ Creating component in runtime }    
procedure TForm1.FormCreate( Sender : TObject ) ;
begin
  FStatusBar := TMyStatusBar.Create( Self ) ;
  FStatusBar.Parent := Self ;
  FStatusBar.Panels.Add ;
end ;

它对我有用。祝你好运!

答案 1 :(得分:2)

我能给你的最重要的建议是将状态栏更新的数量限制为每秒10或20。更多只会造成不必要的闪烁,对用户没有任何好处 - 他们无法处理快速的信息。

好的,这样就可以了:如果你想为状态栏使用WS_EX_COMPOSITED扩展样式,你基本上有三种选择:

  • 创建一个覆盖CreateParams()方法的后代类,并将其安装到IDE中(如果您不希望将其作为IDE中自己的组件),请创建状态栏。运行时。

  • 在另一个单元中创建一个名为TStatusBar的后代类,覆盖CreateParams()方法,并使用状态栏控件将ComCtrls之后的单位添加到表单单元。这将创建您自己的TStatusBar类的实例,而不是ComCtrls中的实例。有关该技术的另一个示例,请参阅this answer,希望它足够清楚。

  • 使用vanilla TStatusBar类并在运行时设置WS_EX_COMPOSITED扩展样式。

我更喜欢第三个选项作为最容易实验的选项,所以这里是示例代码:

procedure TForm1.FormCreate(Sender: TObject);
var
  SBHandle: HWND;
begin
  // This only works on Windows XP and above
  if CheckWin32Version(5, 1) then begin
    // NOTE: the following call will create all necessary window handles
    SBHandle := StatusBar1.Handle;
    SetWindowLong(SBHandle, GWL_EXSTYLE,
      GetWindowLong(SBHandle, GWL_EXSTYLE) or WS_EX_COMPOSITED);
  end;
end;

修改

如果您希望代码正确支持最近的Windows版本和视觉样式,您甚至不应该考虑自己处理WM_ERASEBKGND - 通常的技术涉及该方法的空处理程序,并在{{中绘制背景1}}处理程序。这不适用于WM_PAINT等标准控件,因为必须在某处某处绘制背景。如果您只是跳过TStatusBar处理程序中的背景图,则需要使用所有状态栏中所有状态栏的所有者绘制的面板,否则将无法绘制背景,并且下面的窗户将闪耀。此外,所有者绘制的面板的代码可能非常复杂。

同样,更好的做法是解决发布代码中的混乱问题,将工作人员与显示代码正确分开,并将状态栏文本的更新速度降低到合理的水平。在每秒超过监视器更新数量方面没有任何意义,即使这对于游戏和类似的可视化也是明智的。