TStatusBar мерцает при вызове процедуры обновления. Способы безболезненно исправить это - PullRequest
2 голосов
/ 17 июля 2009

Итак, вот обсуждение, которое я только что прочитал: http://www.mail-archive.com/delphi@delphi.org.nz/msg02315.html

BeginUpdate и EndUpdate - это не те процедуры, которые мне нужны ...

Переопределение вызова API? Я пытался получить код процедур обновления из модуля ComCtrls, гайка не найдена ...

Может быть, вы могли бы опубликовать здесь код, чтобы исправить мерцание компонента строки состояния, если в нем изменяется только текст? Я имею в виду - что-то вроде 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), и есть процедура, которая работает (без мерцания), НО ЭТО ВВВВВВВееЕЕЭРРРРРГГГГГ МЕДЛЕННО!

 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 ]

2 голосов
/ 20 июля 2009

Самый важный совет, который я могу вам дать, - ограничить количество обновлений строки состояния до 10 или 20 в секунду. Больше просто вызовет ненужное мерцание, без какой-либо выгоды для пользователя - они все равно не могут обрабатывать информацию так быстро.

ОК, с этим в стороне: если вы хотите использовать расширенный стиль WS_EX_COMPOSITED для строки состояния, у вас есть три основных варианта:

  • Создайте класс-потомок, который переопределяет метод CreateParams(), и либо установите его в свою среду IDE, либо (если вы не хотите использовать его в качестве собственного компонента в среде IDE), создайте строку состояния во время выполнения.

  • Создайте дочерний класс с тем же именем TStatusBar в другом модуле, переопределите метод CreateParams() и добавьте этот модуль после ComCtrls к элементам формы, используя элементы управления в строке состояния. Это создаст экземпляр вашего собственного TStatusBar класса вместо класса ComCtrls. См. этот ответ для другого примера техники, надеюсь, она достаточно ясна.

  • Используйте класс 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;

Edit:

Если вы хотите, чтобы ваш код должным образом поддерживал последние версии Windows и визуальные стили, вам даже не следует думать о том, чтобы обрабатывать WM_ERASEBKGND самостоятельно - обычная техника включает пустой обработчик для этого метода и рисование фона в обработчике WM_PAINT , Это действительно не работает для стандартных элементов управления, таких как TStatusBar, так как фон должен быть нарисован где-то . Если вы просто пропустите рисование фона в обработчике WM_ERASEBKGND, вам нужно будет использовать нарисованные владельцем панели, охватывающие всю строку состояния , в противном случае фон просто не будет отрисован, и окно под ним будет просвечивать Кроме того, код для нарисованной владельцем панели, вероятно, будет очень сложным.

Опять же, гораздо лучше было бы распутать путаницу в размещенном вами коде, должным образом отделить работника от отображаемого кода и снизить скорость обновления текстов строки состояния до чего-то разумного. Просто нет никакого смысла проходить мимо количества обновлений монитора в секунду, и даже это имеет смысл только для игр и подобных визуализаций.

2 голосов
/ 19 июля 2009

Вы должны проверить, будет ли работать свойство TWinControl.DoubleBuffered равным True компонента TStatusBar. Также вы можете попробовать включить это свойство для родительского компонента строки состояния (возможно, TForm). Это слепой выстрел - отсюда нет доступа к компилятору. Еще одна мысль - переопределить сообщение WM_ERASEBKGND без вызова унаследованного . Первый пример, найденный после использования Google: здесь .

----- Обновление после комментария автора

Я наконец-то получил доступ к компилятору и теперь он работает. Мы можем использовать решение 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 ;

И это работает для меня. Удачи!

...