2011-01-26 42 views
2

我开发了以下AnimateRects()方法在Windows桌面上绘制动画矩形。我使用它来动画显示模态窗体,使它看起来像是从一个网格单元“长大”的。为什么带有pmXOR笔的TCanvas.Rectangle()只能“有时”工作?

在窗体显示之前,我使用bExpand参数= True调用该方法一次。然后,当用户关闭窗体时,我再次调用窗体,但使用bExpand = False,以将窗体“折叠”显示到网格单元格中。

的问题是与bExpand =假的情况下...在循环的第一次迭代中,第一调用矩形(R)绘制预期的长方形,但它仿佛第二调用矩形(r)从未被调用 - 第一个矩形永远不会被异或。因此,在绘制了“折叠”矩形序列之后,我将屏幕上的第一个矩形作为工件

任何想法我做错了什么?

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

你确定你确实想这么做吗?桌面并不是真的可以吸引你。另外,我用你的DelayMSecs例程把我的鼻子打开。开始“睡眠(0)”不会放弃时间片。如果你的是唯一可运行的线程,那么你将坐在一个小忙环路。 MsgWaitForMultipleObjects有什么问题? –

+0

是的,我真的想这样做 - 这是基于网格应用程序的用户界面的重要组成部分。 (用户需要关于模态对话框的“源”的视觉提示。)对于DelayMSecs,这只是一种东西,随着我将这个例程拼凑在一起并且很快使用。 (首先得到一些有用的东西,然后进行优化。) –

+0

这里的要点是,我试图弄清楚为什么我第二次调用Rectangle()可能只是“什么都不做”。 –

回答

1

问题很可能是您开始绘制矩形而模态窗体仍然可见。在一个点上,表单从屏幕上消失,上面有一个矩形,当你绘制同一个矩形来删除前一个时,它现在在屏幕上。请注意,在表单上调用“免费”,“隐藏”等操作不会立即隐藏。

(编辑:这需要一些解释:代码的下一行运行之前的表格将被隐藏,但不能保证何时未覆盖的窗口(S)将更新其无效区域)。

的解决办法是Sleep一会儿模态窗体关闭后AnimateRects之前被调用,或或者叫Application.ProcessMessages。如果模式表单不完全在您自己的应用程序窗口上,后者可能不会有太大的帮助。如果模式形式是在一个应用程序不断同时进行自己的绘图的情况下,前者可能没有太大的帮助。像任务经理f.i ...

编辑:虽然我可能会为此而皱眉,这个问题正是为什么LockWindowUpdate存在。当你考虑它的时候,你会发现当你在移动它时显示一个窗口的拖动轮廓(当“拖动时显示窗口内容”被禁用时),你所做的与shell没有什么不同。 。

+1

重新编辑:不,这与LockWindowUpdate()试图解决的问题不是同一个问题。 OP所具有的情况既不是(必然)用户发起的,也不限于单个实例。没有办法可以同时执行多个拖放动作,动画不会受到这种限制。真正的拖拽操作完全有可能会干扰动画,所以'LockWindowUpdate()'在这里肯定是不合适的。在现代(合成)GUI系统中,Xor绘画通常是错误的。应该使用覆盖窗口。 – mghie

+0

@mghie - 我不知道OP的动画是否是用户启动的,但好的,指出了! –

+0

是的,就是这样.--感谢您的帮助。接下来,我将通过按照mghie的建议绘制一个透明的覆盖窗口来实现这一点。 –

相关问题