简体   繁体   English

调用更新过程时,TStatusBar闪烁。 轻松解决此问题的方法

[英]TStatusBar flickers when calling Update procedure. Ways to painlessly fix this

So, here is the discussion I have just read: http://www.mail-archive.com/delphi@delphi.org.nz/msg02315.html 因此,这是我刚刚阅读的讨论: http : //www.mail-archive.com/delphi@delphi.org.nz/msg02315.html

BeginUpdate and EndUpdate is not thi procedures I need ... BeginUpdate和EndUpdate不是我需要的程序...

Overriding API Call? 重写API调用? I tried to get Update procedures code from ComCtrls unit, nut did not found... 我试图从ComCtrls单元获取更新过程代码,但未找到螺母。

Maybe you could post here a code to fix thi flicker of statusbar compoent if the only text changes in it? 也许您可以在此处发布代码以解决状态栏组件闪烁的问题,如果其中仅有文本更改? I mean - something like TextUpdate or some kind of TCanvas method or PanelsRepaint ... ? 我的意思是-类似TextUpdate或某种TCanvas方法或PanelsRepaint ...吗?

The flickering is caused by this code: 闪烁是由以下代码引起的:

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);

Any ideas? 有任何想法吗?


There was comment with this link ( http://www.stevetrefethen.com/blog/UsingTheWSEXCOMPOSITEWindowStyleToEliminateFlickerOnWindowsXP.aspx ) and there is procedure that works ( no flickering whastsoever ), BUT IT IS VVVVVVVEEEEEERRRRRRYYYYYY SLOW! 此链接有评论( http://www.stevetrefethen.com/blog/UsingTheWSEXCOMPOSITEWindowStyleToEliminateFlickerOnWindowsXP.aspx ),并且该程序有效(无闪烁),但VVVVVVVVEEEEEERRRRRRYYYYYY慢!

 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 

Also - the target is not the form, but the StatusBar ... how to assign this method to statusbar? 另外-目标不是表单,而是StatusBar ...如何将此方法分配给statusbar?

You should check whether setting the TWinControl.DoubleBuffered property to True of the TStatusBar component will make it work. 您应该检查是否将TStatusBar组件的TWinControl.DoubleBuffered属性设置为True可以使它工作。 Also you can try enabling this property to the status bar's parent component (probably TForm). 您也可以尝试对状态栏的父组件(可能是TForm)启用此属性。 It's a blind shot - don't have access to the compiler from here. 这是一个盲目的镜头-无法从此处访问编译器。 Another thought is to override the WM_ERASEBKGND message without calling inherited . 另一种认为是覆盖WM_ERASEBKGND消息,而不调用继承 First example found after using google: here . 使用google后找到的第一个示例: 此处

----- Update after author's comment -----作者评论后更新

I finally got access to the compiler and now it's working. 我终于可以访问编译器,并且现在可以正常工作了。 We can use the WS_EX_COMPOSITED solution. 我们可以使用WS_EX_COMPOSITED解决方案。 All you need is is to create your own custom component basing on TCustomStatusBar or just create a class wrapper and create your status bar instance in runtime. 您需要做的就是基于TCustomStatusBar创建自己的自定义组件,或者只是创建一个类包装器并在运行时创建状态栏实例。 Like this: 像这样:

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 ;

And it works for me. 它对我有用。 Good luck! 祝好运!

The most important advise I can give you is to limit the number of status bar updates to maybe 10 or 20 per seconds. 我能给您的最重要的建议是将状态栏更新的数量限制为每秒10或20。 More will just cause unnecessary flicker, without any benefit for the user - they can't process the information that fast anyway. 更多信息只会导致不必要的闪烁,对用户没有任何好处-他们仍然无法快速处理信息。

OK, with that out of the way: If you want to use the WS_EX_COMPOSITED extended style for the status bar you have basically three options: 好的,顺便说一句:如果要对状态栏使用WS_EX_COMPOSITED扩展样式,则基本上有三个选择:

  • Create a descendent class that overrides the CreateParams() method and either install this into your IDE or (if you don't want to have it as its own component in the IDE) create the status bar at runtime. 创建一个重写CreateParams()方法的后代类,然后将其安装到您的IDE中,或者(如果您不想在IDE中将其作为自己的组件)在运行时创建状态栏。

  • Create a descendent class with the same name TStatusBar in another unit, override the CreateParams() method, and add this unit after ComCtrls to the form units using status bar controls. 在另一个单元中创建一个具有相同名称TStatusBar的后代类,重写CreateParams()方法,然后使用状态栏控件将此ComCtrls之后的单元添加到表单单元。 This will create an instance of your own TStatusBar class instead of the one in ComCtrls . 这将创建您自己的TStatusBar类的实例,而不是ComCtrls中的ComCtrls See this answer for another example of the technique, hopefully its clear enough. 请参阅此答案以获取该技术的另一个示例,希望它足够清晰。

  • Use the vanilla TStatusBar class and set the WS_EX_COMPOSITED extended style at runtime. 使用香草TStatusBar类并在运行时设置WS_EX_COMPOSITED扩展样式。

I prefer the third option as the easiest one to experiment with, so here's the sample code: 我更喜欢第三个选项,因为它是最容易尝试的选项,因此下面是示例代码:

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;

Edit: 编辑:

If you want your code to properly support recent Windows versions and visual styles you should not even think of handling WM_ERASEBKGND yourself - the usual technique involves an empty handler for that method, and drawing the background in the WM_PAINT handler. 如果您希望代码正确支持最新的Windows版本和视觉样式,则您甚至不应该自己考虑处理WM_ERASEBKGND常规技术涉及该方法的空处理程序,并在WM_PAINT处理程序中绘制背景。 This doesn't really work for standard controls like TStatusBar , as the background has to be drawn somewhere . 这对于TStatusBar这样的标准控件来说TStatusBar ,因为背景必须绘制在某处 If you just skip the background drawing in the WM_ERASEBKGND handler you will need to use owner-drawn panels spanning all of the status bar , otherwise the background simply won't be drawn, and the window underneath will shine through. 如果仅跳过WM_ERASEBKGND处理程序中的背景图,则将需要使用跨所有状态栏的所有者绘制的面板,否则背景将不会被绘制,并且下面的窗口将通过。 Besides, the code for the owner-drawn panel would probably be very complex. 此外,所有者绘制面板的代码可能非常复杂。

Again, a much better course of action would be to untangle the mess in your posted code, properly separate worker from display code, and reduce the update speed of your status bar texts to something reasonable. 同样,更好的做法是解开已发布代码中的混乱情况,将工作人员与显示代码正确分开,并将状态栏文本的更新速度降低到合理的水平。 There just isn't any sense at all in going past the number of monitor updates per second, and even this is sensible only for games and similar visualizations. 超过每秒监视器更新的数量根本没有任何意义,即使只有游戏和类似的可视化程序也才有意义。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM