Delphi组件未绘制

时间:2009-05-11 15:34:42

标签: delphi panel custom-component tpanel

我有组件(TPanel的后代),我实现了Transparency和BrushStyle(使用TImage)属性。

当我在表单上有一个这种类型的组件时,一切都没问题。发髻当我在表格上加注这种类型的更多组件时,只涂上第一个可见组件。当移动表单并且第一个组件位于其他窗口或外部桌面下时,下一个组件将被绘制。

unit TransparentPanel;

interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls, stdctrls;

type
  TTransparentPanel = class(TPanel)
  private
    FTransparent: Boolean;
    FBrushStyle: TBrushStyle;
    FImage: TImage;

    procedure SetTransparent(const Value: Boolean);
    procedure SetBrushStyle(const Value: TBrushStyle);
  protected
    procedure CreateParams(var Params: TCreateParams); override;
    procedure Paint; override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    property Transparent: Boolean read FTransparent write SetTransparent default
      True;
    property BrushStyle: TBrushStyle read FBrushStyle write SetBrushStyle default
      bsBDiagonal;
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('TransparentPanel', [TTransparentPanel]);
end;

constructor TTransparentPanel.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);

  FTransparent := True;
  FBrushStyle := bsBDiagonal;

  FImage := TImage.Create(Self);
  FImage.Align := alClient;
  FImage.Parent := Self;
  FImage.Transparent := FTransparent;
end;

procedure TTransparentPanel.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  if ((not (csDesigning in ComponentState)) and FTransparent) then
    Params.ExStyle := Params.ExStyle or WS_EX_TRANSPARENT;
end;

destructor TTransparentPanel.Destroy;
begin
  if Assigned(FImage) then
    FreeAndNil(FImage);

  inherited Destroy;
end;

procedure TTransparentPanel.Paint;
var
  XBitMap,
    BitmapBrush: TBitmap;
  XOldDC: HDC;
  XRect: TRect;
  ParentCanvas: TCanvas;
begin
  {This panel will be transparent only in Run Time}
  if (csDesigning in ComponentState) or (not FTransparent) or (FBrushStyle in [bsClear, bsSolid]) then
    inherited Paint
  else
  begin
    XRect := ClientRect;
    XOldDC := Canvas.Handle;
    XBitMap := TBitmap.Create;
    BitmapBrush := TBitmap.Create;
    try
      XBitMap.Height := Height;
      XBitMap.Width := Width;
      Canvas.Handle := XBitMap.Canvas.Handle;
      inherited Paint;
      RedrawWindow(Parent.Handle, @XRect, 0,
        RDW_ERASE or RDW_INVALIDATE or
        RDW_NOCHILDREN or RDW_UPDATENOW);

      BitmapBrush.Width := FImage.Width;
      BitmapBrush.Height := FImage.Height;

      BitmapBrush.Canvas.Brush.Color := clBlack;
      BitmapBrush.Canvas.Brush.Style := FBrushStyle;
      SetBkColor(BitmapBrush.Canvas.Handle, clWhite);
      BitmapBrush.Canvas.FillRect(BitmapBrush.Canvas.ClipRect);

      FImage.Canvas.Draw(0, 0, BitmapBrush);
    finally
      Canvas.Handle := XOldDC;
      Canvas.BrushCopy(XRect, XBitMap, XRect, Color);
      XBitMap.Free;
      BitmapBrush.Free;
    end;
  end;
end;

procedure TTransparentPanel.SetBrushStyle(const Value: TBrushStyle);
begin
  if (FBrushStyle <> Value) then
  begin
    FBrushStyle := Value;
    Invalidate;
  end
end;

procedure TTransparentPanel.SetTransparent(const Value: Boolean);
begin
  if (FTransparent <> Value) then
  begin
    FTransparent := Value;
    FImage.Transparent := Value;
    Invalidate;
  end;
end;

end.

有什么问题?

4 个答案:

答案 0 :(得分:5)

好的,一些提示:

  • 仅绘制一个组件,因为在绘制期间,控件的客户区域再次失效,因此您创建了无限的 WM_PAINT 消息流,并且永远不会绘制第二个组件。直到第一个变得不可见,正如你所描述的那样。您可以从CPU负载中看到这一点,表单上的某个组件使用我系统上100%的一个核心(Delphi 2007,在运行时创建的组件)。

  • 您应该尝试删除您绘制的位图,并改为使用DoubleBuffered属性。

  • FImage实际上用于什么?

  • 如果根据Transparent属性的值修改create参数,则需要在属性更改时重新创建窗口句柄。

  • 也许你可以完全摆脱这个组件,而是使用TPaintBox代替?只要您不自己绘制背景,它就是透明的。但我无法从你的代码中看出你实际想要达到的目标,所以很难说。

答案 1 :(得分:4)

我认为你想要一个可以包含其他控件的控件 - 比如TPanel可以做的 - 以及一个可以显示窗口内部窗口内容的控件 - 就像TImage可以做的那样Transparent 1}}属性已设置。看起来你错误地认为,如果你把一个控件放在另一个控件之上,你就会得到两者的组合行为。 那是出了什么问题。

你应该做的第一件事是摆脱TImage控件。这只会使事情变得比他们需要的更复杂。当您需要在面板上绘制画笔图案时,将其直接绘制到面板上。

接下来,要意识到ws_ex_Transparent窗口样式控制窗口的兄弟是否先被绘制。这没有说明窗口的是否被重新绘制。如果面板的父级设置了ws_ClipChildren样式,则它不会在面板所在的位置下绘制自己。如果您的面板控件的父级设置了ws_ex_Composited样式,它看起来会对您有所帮助,但作为组件编写者,您无法控制控件的父级。

TImage能够显示为透明,因为它不是窗口控件。它没有窗口句柄,因此关于绘画和剪辑的操作系统规则不适用于它。从Windows的角度来看,TImage根本不存在。我们Delphi世界认为TImage绘画本身实际上是父窗口延迟到一个单独的子程序来绘制父窗口的某个区域。因此,TImage绘画代码可能无法在父母的某些区域上绘画。

如果我这样做,我会问自己,带刷子图案的控件是否真的需要成为容器控件。我可以改为使用普通的TImage并在其上绘制重复的笔刷图案吗?其他控件仍然可以在其上面,但它们不会被视为模式控件的子项。

答案 2 :(得分:0)

尝试查看Graphics32 library:它非常擅长绘制内容并使用位图和透明度很好

答案 3 :(得分:0)

如果您希望面板是透明的,您需要做的就是覆盖Paint并且不执行任何操作(或者绘制透明图像),并且还捕获WM_ERASEBKGND消息并在此处不执行任何操作。这样可以确保面板完全不会自行绘制。

还要确保从ControlStyle中排除csOpaque标志,以便父级知道它应该在面板下面绘制自己。

你在Paint中拥有的东西绝对是可怕的,顺便说一句(我的意思是RedrawWindow的东西)。摆脱它。并且WS_EX_TRANSPARENT仅用于顶层窗口,而不是用于控件。