2

So, here is the discussion I have just read: http://www.mail-archive.com/delphi@delphi.org.nz/msg02315.html

BeginUpdate and EndUpdate is not thi procedures I need ...

Overriding API Call? I tried to get Update procedures code from ComCtrls unit, nut did not found...

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 ... ?

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!

 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?

HX_unbanned
  • 583
  • 1
  • 15
  • 51
  • I don't get flickering in my statusbar. What do you do to make it flicker? LockWindowUpdate will only help if you change the UI a lot and temporarily don't want it to be updated. – Lars Truijens Jul 17 '09 at 07:02
  • @Lars: LockWindowUpdate() should not be used for reducing flicker. In fact, it shouldn't be used at all, in the days of desktop compositing. See http://blogs.msdn.com/oldnewthing/archive/2007/02/22/1742084.aspx, http://blogs.msdn.com/oldnewthing/archive/2007/02/23/1747713.aspx and related postings by Raymond Chen. – mghie Jul 17 '09 at 08:34
  • Hm, yeah, I just added it as possible part of possible solution ... Thanks for links, mghie ;) – HX_unbanned Jul 17 '09 at 09:08
  • What version of Delphi are you using? – Vivian Mills Jul 17 '09 at 14:11
  • The only time I see flickering in my statusbar is when I'm trying to force the repaint faster than the refresh of the monitor. Is that what your talking about? If not, I'm not sure what your seeing. Could you provide a sample program/code that demonstrates the problem? – Vivian Mills Jul 17 '09 at 19:30
  • @Ryan , Yes, excatly that what I am taling about. Do you have some synchronizing code ( my monitor has refresh time at 4ms )... – HX_unbanned Jul 20 '09 at 06:14
  • @HX_unbanned: The 4 ms of your monitor are the time to change from light to dark or vice versa. The graphics card does send a new picture 60 times per second (every 16,7 ms). Unless you are programming a game you should not need to deal with this at all, other than making sure that updates are not unnecessarily fast. 20 or 30 times per second is plenty enough. – mghie Jul 20 '09 at 09:18
  • Ok, I see. I was thinking about this from the begining, but I was ( and am ) afraid that this might leave some glitches on the statusbars text area ( ok, "text area" is not the right term - better would be stringlists item text ) - like some not redrawn pixels ( leftovers from a character or something like that ) ... – HX_unbanned Jul 20 '09 at 09:32

2 Answers2

3

The most important advise I can give you is to limit the number of status bar updates to maybe 10 or 20 per seconds. 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:

  • 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.

  • 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. This will create an instance of your own TStatusBar class instead of the one in 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.

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. This doesn't really work for standard controls like TStatusBar, as the background has to be drawn somewhere. 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. 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.

Community
  • 1
  • 1
mghie
  • 32,028
  • 6
  • 87
  • 129
  • Maybe you should do as mghie suggested: limit the frequency of StatusBar's updates - just call update every n-th time. – Darius Jul 20 '09 at 07:59
  • @Darius - well, take a look at first post - blockread() reads binary file, even with optinal check parameter, very fast and this frequency is important as this statusbar model can be used as instant debugger. – HX_unbanned Jul 20 '09 at 08:04
  • fix spelling with OR statement, mghie ;) Also - there is now some system lag when used this procedure - as warned in my link :( Hm, any other solution? Mybe it could be done, of course, with a bit larger code, but with WM_ERASEBKGND ... althought I did search ComCtrl unit, but did not find full StatusBar.Update code ... As much as I understand, the overriding of this API call would be simply not calling it in practice? – HX_unbanned Jul 20 '09 at 08:09
  • 1
    In my opinion limiting the frequency of StatusBar updates won't make those data less informational. In fact, such updates shouldn't be called that way, cause those VCL calls are affecting the performance of whole I/O operation. Much better approach would be using a separate thread for doing this. – Darius Jul 20 '09 at 08:26
  • hm, than I could use Shell_Notify() or SHChangeNotify() ? Problem is that I dont know correct API Call syntax to do it ... And - I never read about ShelObj and ShellAPI units in depth ... – HX_unbanned Jul 20 '09 at 09:08
  • @mghie - about Edit - emm, yeah, my goul is to make full ( although pretty curvehands.dll-like ) support for WinXP SP3 and Post-Windows XP versions, so I mainly try to go through Shell resources, not deal with GDI, TCanvas, and Graphics classes/units ... – HX_unbanned Jul 20 '09 at 09:13
  • @HX_unbanned: Sorry, I don't understand what Shell programming has to do with this, at all. – mghie Jul 20 '09 at 09:19
  • @mghie - Ok, never mind then. I suppose I am thinking the wrong way after all. Don't get confused - I'm just learning delphi. imho all vcl components connect during runetime directly to os shell and takes from them all the resources. the os api then manages shells resources, so i think that they are pretty dependent. Maybe I am wrong? If yes, please give some links to read and learn. P.S. Sorry if I disinform you and other members and readers. We all are just humans.. – HX_unbanned Jul 20 '09 at 09:39
2

You should check whether setting the TWinControl.DoubleBuffered property to True of the TStatusBar component will make it work. Also you can try enabling this property to the status bar's parent component (probably 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. First example found after using google: here.

----- 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. 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. 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!

Darius
  • 524
  • 1
  • 5
  • 11
  • Well, I did take a look at this link and as I knew, there is doublebuffered property, which did not help me .... Also - Question is updated. Please give some other answers / comments! – HX_unbanned Jul 20 '09 at 05:18