Delphi component not colored

I have a component (descendat of TPanel) where I have implemented the Transparency and BrushStyle properties (using TImage).

This is all fine when I have one component of this type in a form. Bun, when I pun on the form more components of this type, only the first visible component is colored. When the form is moved and the first component is under another window or external desktop, the next component is painted.

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.

      

What's wrong?

+1


a source to share


4 answers


OK, a few tips:



  • Only one component is created because during drawing the client area of โ€‹โ€‹the control is invalidated again, so you create an endless stream of WM_PAINT messages and the second component is never drawn. Until the first becomes invisible, as you described. You can see this from cpu load if one of your components in the form is using 100% of one core on my system (Delphi 2007, runtime-generated component).

  • You should try to delete the bitmap you drew and use the DoubleBuffered property instead.

  • What is FImage Really Used For?

  • If you change creation options based on the value of the Transparent property, you need to recreate the window handle when the property changes.

  • Perhaps you can get rid of the component entirely and use TPaintBox instead? It is transparent until you paint the background yourself. But I can't tell from your code what you actually want to achieve, so it's hard to tell.

+5


a source


I think you want a control that can contain other controls, for example, TPanel

and a control that can display the contents of the window below it - for example TImage

, when its property is Transparent

set, It looks like you were wrong that if you put one control on top of the other, you get the behavior of both. Here's what happened.

The first thing you need to do is get rid of the control TImage

. It just makes things more complicated than necessary. When you need to paint a brush pattern on the panel, draw it directly on the panel.

Next, understand that the window style ws_ex_Transparent

determines whether the siblings of the window are painted. It doesn't say anything about whether the window's parent is being redrawn. If the parent of your panel has a style style ws_ClipChildren

, then it will not draw itself under your panel. It looks like this will help you if the parent of your panel control is styled ws_ex_Composited

, but as a component writer, you don't gain control over the parents of your controls.



TImage

may appear transparent because it is not a windowed control. It does not have a window handle, so OS rules regarding drawing and cropping do not apply to it. From a Windows perspective, TImage

it doesn't exist at all. What we in the Delphi world perceive as the picture itself is TImage

really the parent window, setting aside for a separate subroutine to draw a specific area of โ€‹โ€‹the parent window. Because of this, the drawing code TImage

may simply not draw over some parent area.

If I were doing this, I would ask if the brush drawing control really needs to be a container control. Can I instead use a regular one TImage

with a repeating brush pattern drawn on it? Other controls can still sit on top of it, but they will not be considered child template controls.

+4


a source


Try looking at the Graphics32 library : it is very good at drawing things and works great with bitmaps and transparency

0


a source


If you want the panel to be transparent, all you have to do is override Paint and do nothing (or draw a transparent image, for example), and also catch the WM_ERASEBKGND message and do nothing here. This will ensure that the panel doesn't draw at all.

Make sure the csOpaque checkbox is excluded from the ControlStyle, so the parent knows it should draw itself under the panel.

What you have in Paint is absolutely awful by the way (I mean the RedrawWindow thing). Get rid of it. And WS_EX_TRANSPARENT is for top level windows only, not controls.

0


a source







All Articles