一行のコードはTForm色の前世の生を設定します.

9156 ワード

万が一からの投稿:http://www.cnblogs.com/del/archive/2008/04/27/1173658.html確かに行コード設定TFormコントロールの色を作りました.しかし、実際の状況は、VCLフレームがこの過程で大量の作業を行い、複数のメッセージの送信と応答を経て、目的を達成しました.大体の順序は以下の通りです.
procedure TForm1.Button1Click(Sender: TObject);
begin
  Self.Color := clRed;
end;

procedure TControl.SetColor(Value: TColor);
begin
  if FColor <> Value then
  begin
    FColor := Value;
    FParentColor := False;
    Perform(CM_COLORCHANGED, 0, 0); //      ,               ,        ,                
  end;
end;

procedure TCustomForm.CMColorChanged(var Message: TMessage);
begin
  inherited;
  if FCanvas <> nil then FCanvas.Brush.Color := Color; //      Canvas.Brush.Color      ,          ,       ,VCL      FillRect API,     Canvas,      
end;

procedure TWinControl.CMColorChanged(var Message: TMessage);
begin
  inherited; //           ,          ,            。             ,     。
  FBrush.Color := FColor; //   ,       ,      Brush  
  NotifyControls(CM_PARENTCOLORCHANGED); //      ,       ,     ,          
end;

procedure TControl.CMColorChanged(var Message: TMessage);
begin
  Invalidate; //    ,             。        TWinControl.Invalidate;
end;

procedure TWinControl.Invalidate;
begin
  //   ,      WParam 0,   API     ,            。
  Perform(CM_INVALIDATE, 0, 0); //      ,        ,  CM         ,        。   ,          ,          。
end;

procedure TWinControl.CMInvalidate(var Message: TMessage);
var
  I: Integer;
begin
  if HandleAllocated then
  begin
    if Parent <> nil then
      Parent.Perform(CM_INVALIDATE, 1, 0); //      ,  ,     (       )。Form1 Parent Application,     。
    if Message.WParam = 0 then
    begin
      // API,      NULL  ,        ;     TRUE     。
      InvalidateRect(FHandle, nil, not (csOpaque in ControlStyle)); //         , Form1  ,      
      { Invalidate child windows which use the parentbackground when themed }
      if ThemeServices.ThemesEnabled then
        for I := 0 to ControlCount - 1 do
          if csParentBackground in Controls[I].ControlStyle then // important
            Controls[I].Invalidate;
    end;
  end;
end;
以上の過程はForm 1の画板を失効させました(結局、Form 1.Fravas.Brushを通じて作用します.クラスの属性Colorは表象だけです.)、続いてもう一つのForm 1を描く過程があります.TForm 1はTFormから継承され、TFormはTWinControlから継承され、TCustomControlではなく、WM_に応答します.PAINTメッセージはWMPaint関数をカバーしていますので、WindowsはWM(u)をPAINTは直接Form 1に送ります.呼び出し順序は以下の通りです.
TCustom Form.WMEraseBkgnd(var Message:TWMEraseBkgnd)//通常の状況とアイコンの状況を区別するTWinControl.WMEraseBkgnd(var Message:TWMEraseBkgnd);背景色を描く(古い背景を消すのに相当)TCustom Form.WMPaint;通常の状況とアイコンの状況を区別するTWinControl.WMPaint;ダブルバッファとセルフ絵を判断するには、ごく少数のWindowsが持つコントロールのパッケージ(例えば、TEdit、TButton)以外は、PaintHandlerがTWinControl.PaintHandler(var Message:TWMPaint)を実行します.まず、現在のwinコントロールを描画します.例えば、Form 1(おそらくトリミングした後の残りの部分)を描画して、そのすべての図形のサブコントロールのprocedure TCustom Form.PaintWindow(DC:HDC)//WM_を使うPAINTメッセージは、DCハンドルでForm 1フォームのprocedure TCustom.Paintを描きます.プログラマイベントを呼び出したり、デザイン時のグリッドを表示したりします.
------------------------------------------
Form 1の初期色はどこに設定されていますか?回答はコードに設定されていませんが、dfmにclBtnFace色が設定されています.手動でclRedに変更すれば、すぐに効果が見られます.これは空白のフォームのdfm内容で、全部で14項目です.
object Form1: TForm1
  Left = 0
  Top = 0
  Height = 282
  Width = 418
  Caption = 'Form1'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
end
------------------------------------------
また、なぜTCustom Formでソースコードを直接変更したのか、最終的に分かりましたが、効果はなかなか得られませんでした.
constructor TCustomForm.CreateNew(AOwner: TComponent; Dummy: Integer);
begin
  Color := clRed; //              ?
  Brush.Color := clRed; 
  FCanvas.Brush.Color := clRed; 
end;
TFormの作成順序は以下の通りです.
begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

procedure TApplication.CreateForm(InstanceClass: TComponentClass; var Reference);
var
  Instance: TComponent;
begin
  Instance := TComponent(InstanceClass.NewInstance); //   ,        。    
  TComponent(Reference) := Instance;
  try
    Instance.Create(Self); //        TForm.Create;
  except
    TComponent(Reference) := nil;
    raise;
  end;
  if (FMainForm = nil) and (Instance is TForm) then
  begin
    TForm(Instance).HandleNeeded;
    FMainForm := TForm(Instance);
  end;
end;
TForm.reateは先にTForm.reat Newを呼び出すことができます.後にInitInhered Componentを呼び出してdfmファイルを読み込むと、記憶ワードdfmファイルの色が私の手動で指定したclRed色を覆っています.これはずっと有効になりません.だからTCustom Form.reate関数の中のInitInheited Component文の後で書くべきです:Color:=clBlue;Brush.Coolor:=clBlue;すぐ有効になります.でも、書いたら:Canvas.Brush.Coolor:=clBlue;これは、Canvasがこの過程で使われていないため、TCustom Form.ccoolorChend関数でクラスの属性Colorの値によってカバーされているため、有効になりません.
------------------------------------------
Form 1.Coolorについて、Brush.Coolor、Canvas.Brush.Coolorの3つの色の値の間の関係分析:この2つを使えば、Color:=clRed;Brush.Coolor:=clBlue;TCustom Form.reateに書いてありますが、どちらを後ろに書いても後の文の色に合わせて設定されます.しかし、その過程は違っています.1.もしColor:=clRed;後に書いてください.クラスの属性を呼び出したのと、後の一連の変化に相当します.TWinControl.C MColorChendを実行すると、FBrush.Coolor=Flarを実行します.FBRUsh.Coolorの値をカバーするに相当します.前の文の効果は無効になります.2.Brush.Coolor:=clBlue;後に書いて、前の文の効果を実行した後に、Brush.Coolorの値を簡単に上書きしました.前の文の効果は自然に効果がありません.まとめ:この二つの言葉は同じ効果がありますが、実行過程は大きく違っています.Brush.Coolorを使う:=clBlue;これはDelphiの言語レベルだけで値を変えて、背景を更新する時にFillRectに直接使用させます.Colorを使用する場合:=clRed;実は2歩に分けて、第1歩は全体のForm 1の取引先の区を失効させるので、第2歩はDelphi言語のレベルがBrushの値を変えるのです.この上の二歩は全部WM_です.ERASEBKGNDメッセージとWM_PAINTメッセージは来る前に準備しています.そうすると、メッセージが更新されるとすぐに更新効果があります.
以上の分析を通して、私は突然問題に気づきました.Brush.Coolorを直接実行すれば、ブラシの色を変えただけで、顧客エリアを失効させませんでした.効果がありますか?試してみました.procedure TForm 1.Button 2 lick(Sender:TObject)BeginBrush.Coolor:=clGreen;end;ボタンを押すと、フォーム1はやはり色を変える効果がありません.この説明はブラシの色が変えられましたが、結局一つのステップが足りなくなりました.取引先エリアは無効になりませんので、やはり効果がありません.次の顧客エリアが失効するまで、効果が発揮されるので、ウィンドウを最小化して、再度最大化を回復します.そうすると、Form 1クライアントエリアは緑色になります.そしてこれからもずっと緑を保っています.もっと面白いのは、別のウィンドウ(例えばメモ帳)でForm 1の一部の顧客エリアをブロックしてから移動すると、この部分の顧客エリアの色は緑色で、他の部分はまだ赤色です.
constructor TCustom Form.reatにBrush.Coolorを書きます.すぐに有効になります.Form 1は一度も表示されていないので、初めて表示される時には、自動的に背景メッセージを消去します.ブラシの色は先ほど設定された色です.FillRect APIに直接使用されますので、すぐに効果があります.ですから、これは特殊な状況です.普通の状況ではいけません.このように変更できます.procedure TForm 1.Button 3 Click(Sender:TObject)beginnInvalidate;Brush.Coolor:=clGreen;end;このようにVCLフレームの実行過程とは意味があります.もちろん効果があります.更にこのように変えます.procedure TForm 1.Button 3 Click(Sender:TObject);BeginBrush.Coolor:=clGreen;Invalidate;end;同じ効果がありますが、実はこのように書くのがもっと合理的だと思います.準備万端です.またメッセージを送って対応します.もちろん大丈夫です.
TCustom Form.ccoolorChend関数にはありますが、if Flavas<>nil then Fravas.Brush.Color;しかし、これはキャンバスの絵を専門に使う時に使います.このときVCLはFillRect APIが描いた効果を使っていますので、この言葉を遮断しても大丈夫です.
最後にコードでこの三つの色の関係をまとめます.
procedure TForm1.Button2Click(Sender: TObject);
begin
  Brush.Color := clGreen;
  if (Color=clGreen) then
    ShowMessage('yes'); //    
  if (Canvas.Brush.Color=clGreen) then
    ShowMessage('yes'); //    
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
  Color := clGreen;
  if (Brush.Color=clGreen) then
    ShowMessage('yes'); //   
  if (Canvas.Brush.Color=clGreen) then
    ShowMessage('yes'); //   
end;

procedure TForm1.Button4Click(Sender: TObject);
begin
  Canvas.Brush.Color := clGreen;
  if (Brush.Color=clGreen) then
    ShowMessage('yes'); //    
  if (Color=clGreen) then
    ShowMessage('yes'); //    
end;
------------------------------------------
もう一つの問題は、この色がどのwindapiを使っているかということです.FillRectを検索することによって分かります.
procedure TWinControl.WMEraseBkgnd(var Message: TWMEraseBkgnd);
begin
  with ThemeServices do
  if ThemesEnabled and Assigned(Parent) and (csParentBackground in FControlStyle) then
    begin
      { Get the parent to draw its background into the control's background. }
      DrawParentBackground(Handle, Message.DC, nil, False);
    end
    else
    begin
      { Only erase background if we're not doublebuffering or painting to memory. }
      if not FDoubleBuffered or
         (TMessage(Message).wParam = TMessage(Message).lParam) then
        FillRect(Message.DC, ClientRect, FBrush.Handle); //   ,       (          ),        
    end;

  Message.Result := 1;
end;
------------------------------------------
まとめ:クラスの属性を変更するとすぐに有効になります.クラスの属性に対応するSet関数を呼び出して、メッセージを送って本当にウィンドウに表示します.WM_ならメッセージは直接問題を解決することができます.フォームのタイトルを設定するなど、十分ではない場合もあります.CMを使う必要があります.メッセージはさらに、フォームの色を変更するなどの処理に役立ちます.