Delphi控件的透明與不透明(要挨個解釋一下原因),對InvalidateControl的關鍵理解


procedure TForm1.Button3Click(Sender: TObject);
begin
if (csOpaque in ControlStyle) then ShowMessage('不透明')
else ShowMessage('透明') // Form透明
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
if (csOpaque in Panel1.ControlStyle) then ShowMessage('不透明') // Panel不透明
else ShowMessage('透明')
end;

if (csOpaque in Button1.ControlStyle) then ShowMessage('不透明')
else ShowMessage('透明') // Button透明

if (csOpaque in label1.ControlStyle) then ShowMessage('不透明') // Label不透明
else ShowMessage('透明')

if (csOpaque in image1.ControlStyle) then ShowMessage('不透明')
else ShowMessage('透明') // image1沒有內容的時候,就是透明;有內容的時候,就是不透明

if (csOpaque in bitbtn1.ControlStyle) then ShowMessage('不透明')
else ShowMessage('透明') // bitbtn1有沒有內容都是透明

http://bbs.2ccc.com/topic.asp?topicid=461248

---------------------------------------------------------------

對InvalidateControl的關鍵理解:

1.它是TControl的非虛函數,雖然它可以被TWinControl調用,但是根據VCL的其它代碼,充分證明它實際上只能被圖形子控件所使用:

procedure TWinControl.RemoveControl(AControl: TControl);
begin
  Perform(CM_CONTROLCHANGE, Integer(AControl), Integer(False)); // 通知父控件
  if AControl is TWinControl then
    with TWinControl(AControl) do
    begin
      RemoveFocus(True); // 類函數
      DestroyHandle; // important5 銷毀它和它所有的子控件
    end
  else
    if HandleAllocated then
      AControl.InvalidateControl(AControl.Visible, False); // important5 讓圖形控件消失使用這種手法
  Remove(AControl); // 類函數
  Perform(CM_CONTROLLISTCHANGE, Integer(AControl), Integer(False)); // 通知父控件
  Realign;
end;

procedure TControl.DoDock(NewDockSite: TWinControl; var ARect: TRect);
begin
  { Erase TControls before UpdateboundsRect modifies position }
  if not (Self is TWinControl) then InvalidateControl(Visible, False);
  if Parent <> NewDockSite then
    UpdateBoundsRect(ARect) // 類函數
  else
    BoundsRect := ARect; // 類屬性
  if (NewDockSite = nil) or (NewDockSite = NullDockSite) then Parent := nil;
end;

2. 它的實際內容:

procedure TControl.InvalidateControl(IsVisible, IsOpaque: Boolean);
var
  bParentOpaque: Boolean;
  bChlipped: Boolean; 
  Rect: TRect;
begin
  if (IsVisible or (csDesigning in ComponentState) and not (csNoDesignVisible in ControlStyle))
     and (Parent <> nil) and Parent.HandleAllocated then
  begin
    Rect := BoundsRect; // 類函數,簡單計算(根據控件的長寬高)標簽的坐標以及尺寸
    bParentOpaque := csOpaque in Parent.ControlStyle; // Form默認透明(csOpaque不在風格里)。但是父控件不一定是Form,不要思維僵化在這里。
    bChlipped:=BackgroundClipped; 
    // 實驗說明后兩個一般情況下都是False
    // 第三個參數為False,則保持背景不變。Not作用符以后,有三者條件之一成立即可,就會保持背景不變。
    // IsOpaque表示TControl自己不透明,完全遮住了父控件相應的區域,當然不用重繪背景
    // bParentOpaque表示父控件自己就是不透明的,現有的背景已經足夠(不需要更新父父控件的背景),那么無論TControl子控件怎么辦自繪,都不需要更新背景。
    // bChlipped 重合了
    // 反過來說,自己透明(而且有可能是從不透明變成透明),這時當然要重繪背景。因為原來那部分背景根本就沒有繪制。
    //           父控件透明,也可以從不透明變成透明,這時也要背景重繪。fixme 如果父控件一直是透明的呢,那么每次繪制都要求父父控件背景重繪,因為父父控件的狀態也有可能在改變。
    // fixme 有空仔細研究背景消息 才能深刻理解
    InvalidateRect(Parent.Handle, @Rect, not (IsOpaque or bParentOpaque or bChlipped)); // API
  end;
end;

關鍵在於最后一個參數,三個條件之一成立,結果就是false,就不需要重繪背景了。這三個條件分別是:

1. 自身不透明(比如自身是Panel,或者自身是默認狀態的標簽),那么在自己的區域坐標內,自己可以隨意重繪,而整個父控件的大背景不用變化。這個最容易理解。

2. 父控件不透明(比如父控件是Panel),那么父控件的背景不用看別的控件臉色,子控件在上面無論怎么自繪,也不需要改變父控件的背景。這個也能理解。

3. 兄弟控件的矩形區域與自己完全相同(也就是當前控件的區域坐標,與另一個z軸比它低、且不透明的兄弟控件的區域坐標完全一致,此時bChipped=true,此時當前控件的背景色要看這個兄弟控件的顏色即可,與父控件的顏色完全無關了)。這個也好理解,當前控件怎么自繪,都不影響和不需要父控件的背景。

但是,如果透明標簽放在Form上,那么IsOpaque=false, bParentOpaque=false, bChipped=false,那么此時就會重繪整個句柄的區域,而不僅僅是指定的部分。

If the bErase parameter is TRUE for any part of the update region, the background is erased in the entire region, not just in the specified part.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM