Почему TCanvas.Rectangle () с ручкой pmXOR работает только «иногда»? - PullRequest
2 голосов
/ 26 января 2011

Я разработал следующий метод AnimateRects () для рисования анимационного прямоугольника на рабочем столе Windows.Я использую его для анимации отображения модальной формы, чтобы она «выросла» из ячейки сетки.

Я вызываю метод один раз с параметром bExpand = True прямо перед отображением формы.Затем, когда пользователь закрывает форму, я вызываю ее снова, но с bExpand = False, чтобы показать, что форма «сворачивается» в ячейку сетки.

Проблема заключается в случае bExpand = False ... В первом случаеитерация цикла: first вызов Rectangle (r) рисует прямоугольник, как и ожидалось, но это так, как если бы second вызов Rectangle (r) никогда не вызывался - первыйпрямоугольник никогда не получает XORed.Таким образом, после того, как нарисована последовательность «сворачивающихся» прямоугольников, я получаю первый прямоугольник, оставшийся на экране как артефакт .

Есть идеи, что я делаю неправильно?

const
  MSECS_PER_DAY = 24.0 * 60.0 * 60.0 * 1000;

procedure DelayMSecs(msecs: Word);
var
  Later:  TDateTime;
begin
  Later := Now + (msecs / MSECS_PER_DAY);
  while Now < Later do begin
    Application.ProcessMessages;
    sleep(0);     //give up remainder of our time slice
  end;
end;


procedure T_fmExplore.AnimateRects(ASourceRect, ADestRect: TRect; bExpand:
    boolean; bAdjustSourceForFrame: boolean = True);
const
  MINSTEPS = 10;
  MAXSTEPS = 30;
  MAXDELAY = 180;              //150 - 200 is about right
  MINDELAY = 1;
var
  iSteps: integer;
  DeltaHt: Integer;               //Rect size chg for each redraw of animation window
  DeltaWidth: Integer;
  DeltaTop :  integer;            //Origin change for each redraw
  DeltaLeft :  integer;
  NewWidth, NewHt: Integer;
  iTemp: Integer;
  iDelay: integer;
  r : Trect;
  ScreenCanvas: TCanvas;
begin
  r := ASourceRect;
  with r do begin
    NewWidth :=   ADestRect.Right - ADestRect.Left;           //Target rect's Width
    NewHt :=      ADestRect.Bottom - ADestRect.Top;           //Target rect's Height
        //Temporarily, Deltas hold the total chg in Width & Height
    DeltaWidth := NewWidth - (Right - Left);                //NewWidth - old width
    DeltaHt :=    NewHt - (Bottom - Top);
        //With a static number of iSteps, animation was too jerky for large windows.
        //So we adjust the number of iSteps & Delay relative to the window area.
    iSteps := Max( DeltaWidth * DeltaHt div 6500, MINSTEPS );  //eg. 10 iSteps for 250x250 deltas (62500 pixels)
    iSteps := Min( iSteps, MAXSTEPS );
        //Now convert Deltas to the delta in window rect size
    DeltaWidth := DeltaWidth div iSteps;
    DeltaHt :=    DeltaHt div iSteps;
    DeltaTop :=   (ADestRect.Top - ASourceRect.Top) div iSteps;
    DeltaLeft :=  (ADestRect.Left - ASourceRect.Left) div iSteps;

    iDelay := Max( MAXDELAY div iSteps, MINDELAY );

    ScreenCanvas := TCanvas.Create;
    try
      ScreenCanvas.Handle := GetDC( 0 );              //Desktop
      try
        with ScreenCanvas do begin
          Pen.Color := clWhite;
          Pen.Mode := pmXOR;
          Pen.Style := psSolid;
          Pen.Width := GetSystemMetrics(SM_CXFRAME);
          Brush.Style := bsClear;
          if bAdjustSourceForFrame then
            InflateRect(ASourceRect, -Pen.Width, -Pen.Width);

          repeat
            iTemp := (Bottom - Top) + DeltaHt;        //Height
            if (bExpand and (iTemp > NewHt)) or (not bExpand and (iTemp < NewHt)) then begin
              Top := ADestRect.Top;
              Bottom := Top + NewHt;
            end else begin
              Top := Top + DeltaTop;            //Assign Top first...Bottom is calc'd from it
              Bottom := Top + iTemp;
            end;

            iTemp := (Right - Left) + DeltaWidth;     //Width
            if (bExpand and (iTemp > NewWidth)) or (not bExpand and (iTemp < NewWidth)) then begin
              Left := Left + DeltaLeft;
              Right := Left + NewWidth;
            end else begin
              Left := Left + DeltaLeft;         //Assign Left first...Right is calc'd from it
              Right := Left + iTemp;
            end;

            ScreenCanvas.Rectangle(r);
            SysStuff.DelayMSecs( iDelay );
            ScreenCanvas.Rectangle(r);               //pmXOR pen ...erase ourself

          until (Right - Left = NewWidth) and (Bottom - Top = NewHt);
        end;
      finally
        ReleaseDC( 0, ScreenCanvas.Handle );
        ScreenCanvas.Handle := 0;
      end;
    finally
      ScreenCanvas.Free;
    end;
  end;
end;

1 Ответ

1 голос
/ 26 января 2011

Проблема, скорее всего, в том, что вы начинаете рисовать прямоугольники, пока модальная форма еще видна. В какой-то момент форма исчезает с экрана с прямоугольником на ней, и когда вы рисуете тот же прямоугольник, чтобы стереть предыдущий, он теперь на экране. Обратите внимание, что вызов «Свободный», «Скрыть» и т. Д. В форме не приведет к его немедленному скрытию.

(редактировать: это требует некоторого объяснения: форма будет скрыта до запуска следующей строки кода, но нет никакой гарантии относительно того, когда непокрытые окна обновят свои недействительные области ).

Решением будет Sleep через некоторое время после закрытия модальной формы и до вызова AnimateRects или, возможно, вызова Application.ProcessMessages. Последнее, вероятно, не очень поможет, если модальная форма не полностью находится в окне вашего собственного приложения. И первое, вероятно, не очень поможет, если модальная форма находится над приложением, которое постоянно делает свой собственный рисунок в то же время. Как и диспетчер задач ф.и ...

edit: Хотя меня за это не одобряют, эта проблема как раз и объясняет, почему существует LockWindowUpdate. Когда вы подумаете об этом, вы увидите, что то, что вы делаете, не отличается от того, что делает оболочка, когда она показывает контур перетаскивания окна при его перемещении (когда «показывать содержимое окна при перетаскивании» отключено) .

...