2

Delphi7、PageControl と owner-draw を使用しています。所有者が描画していない PageControls で見られるように、タブの見栄えがよくありません。悪い点: オーナー描画を使用すると、「全体」のタブ ヘッダー領域に描画できません。タブ ヘッダーの周りの小さな 1 ~ 2 ピクセルのフレームが OS によって描画されます。

1) Delphi はオーナードローではなく、外観も問題ありません (XPMan を使用):

デルファイ システム

2) Delphi オーナー ドローでは、タブ ヘッダー全体に色を付けることができないことがわかります (XPMan を使用):

デルフィ オーナー ドロー

ここでは、現在のタブを青で、他のタブを白で描画します。例のみ。コード:

procedure TForm1.PageControl1DrawTab(Control: TCustomTabControl;
  TabIndex: Integer; const Rect: TRect; Active: Boolean);
var
  c: TCanvas;
begin
  c:= (Control as TPageControl).Canvas;
  if Active then
    c.Brush.Color:= clBlue
  else
    c.Brush.Color:= clWhite;
  c.FillRect(Rect);    
end;

2b) 実際のアプリでの Delphi オーナー ドロー (XPMan を使用):

デルファイリアルアプリ

オーナードローを使用する必要があるのはなぜですか? 単純。タブ ヘッダーに X ボタンを描画したり、上部の線をカスタム カラーでペイントしたり、イメージリストからアイコンをペイントしたりします。

PageControl オーナー描画イベントに与えられる減少 rect ではなく、タブ ヘッダーの ENTIRE rect をペイントする方法を探しています。所有者描画イベントによって指定された四角形を増やそうとしましたが、これは役に立ちません.OSはとにかくタブヘッダーの周りにこの薄い1〜2ピクセルのフレームを再描画します.

4

2 に答える 2

8

所有者が描画するネイティブの「タブ コントロール」のタブ ( TPageControlVCL では、そのアセンダントは適切に命名されていますが、クリエイティブな命名の理由は誰でも推測できます..) は、メッセージのTCustomTabControl処理中に親コントロールによって描画されることが期待されます .WM_DRAWITEM

VCL は、メッセージをメッセージに変更し、CN_DRAWITEMそれをコントロール自体に送信することで、親から負担を取り除きます。このプロセスでは、VCL はそれ以上介入しません。OnDrawTabメッセージ ハンドラーがユーザー コードによって割り当てられている場合は、メッセージ ハンドラーを呼び出し、適切なパラメーターを渡します。

したがって、タブの周囲に境界線を引くのは VCL ではなく、OS 自体です。また、明らかに、これはWM_DRAWITEMメッセージの処理中に行われるのではなく、後の描画プロセスで行われます。WM_DRAWITEMこれは、ページ コントロールの親に空のハンドラーを配置することで確認できます。その結果、イベント ハンドラーで何をペイントしても、後で OS によって境界線が取得されます。

OS が描画するものを有効にしないようにしようとするかもしれません。結局のところ、デバイス コンテキスト (Canvas.Handle として) があります。残念ながら、このルートも行き止まりです。これは、VCL がイベント ハンドラーから戻った後、デバイス コンテキストの状態を復元するためです。

したがって、唯一の方法は、OnDrawTabイベントの処理とCN_DRAWITEMメッセージに対する操作を完全に放棄することです。以下のサンプル コードではインターポーザ クラスを使用していますが、任意の方法でコントロールをサブクラス化できます。が設定されていることを確認してくださいOwnerDrawn

type
  TPageControl = class(comctrls.TPageControl)
  protected
    procedure CNDrawitem(var Message: TWMDrawItem); message CN_DRAWITEM;
  end;

  TForm1 = class(TForm)
    ..

..

procedure TPageControl.CNDrawitem(var Message: TWMDrawItem);
var
  Color: TColor;
  Rect: TRect;
  Rgn: HRGN;
begin
  Color := 0;  
  // draw in different colors so we see where we've drawn
  case Message.DrawItemStruct.itemID of
    0: Color := $D0C0BF;
    1: Color := $D0C0DF;
    2: Color := $D0C0FF;
  end;
  SetDCBrushColor(Message.DrawItemStruct.hDC, Color);

  // we don't want to get clipped in the passed rectangle
  SelectClipRgn(Message.DrawItemStruct.hDC, 0);

  // magic numbers corresponding to where the OS draw the borders
  Rect := Message.DrawItemStruct.rcItem;
  if Bool(Message.DrawItemStruct.itemState and ODS_SELECTED) then begin
    Inc(Rect.Left, 2);
//    Inc(Rect.Top, 1);
    Dec(Rect.Right, 2);
    Dec(Rect.Bottom, 3);
  end else begin
    Dec(Rect.Left, 2);
    Dec(Rect.Top, 2);
    Inc(Rect.Right, 2);
    Inc(Rect.Bottom);
  end;
  FillRect(Message.DrawItemStruct.hDC, Rect,
      GetStockObject(DC_BRUSH));

  // just some indication for the active tab
  SetROP2(Message.DrawItemStruct.hDC, R2_NOTXORPEN);
  if Bool(Message.DrawItemStruct.itemState and ODS_SELECTED) then
    Ellipse(Message.DrawItemStruct.hDC, Rect.Left + 4, Rect.Top + 4,
      Rect.Left + 12, Rect.Top + 12);

  // we want to clip the DC so that the borders to be drawn are out of region
  Rgn := CreateRectRgn(0, 0, 0, 0);
  SelectClipRgn(Message.DrawItemStruct.hDC, Rgn);
  DeleteObject(Rgn);

  Message.Result := 1;
  inherited;
end;


上記がここでどのように見えるかを次に示します。
ここに画像の説明を入力

于 2013-08-17T12:53:06.467 に答える