0 votes

Composant Delphi non peint

J'ai un composant (descendant de TPanel) dans lequel j'ai implémenté les propriétés Transparency et BrushStyle (en utilisant TImage).

Tout va bien lorsque j'ai un seul composant de ce type sur le formulaire. Mais lorsque je place plusieurs composants de ce type sur le formulaire, seul le premier composant visible est peint. Lorsque le formulaire est déplacé et que le premier composant se trouve sous une autre fenêtre ou en dehors du bureau, le composant suivant est peint.

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.

Qu'est-ce qui ne va pas ?

4voto

mghie Points 25960

OK, quelques conseils :

  • Un seul composant est dessiné, car pendant la peinture, la zone client du contrôle est à nouveau invalidée, de sorte que vous créez un flux infini de WM_PAINT et le deuxième composant n'est jamais dessiné. Jusqu'à ce que le premier soit rendu invisible, comme vous le décrivez. Vous pouvez le voir à la charge du processeur, le fait d'avoir un de vos composants sur un formulaire utilise 100 % d'un cœur sur mon système (Delphi 2007, composant créé au moment de l'exécution).

  • Vous devriez essayer de supprimer le bitmap dans lequel vous dessinez et utiliser la propriété DoubleBuffered à la place.

  • À quoi sert réellement FImage ?

  • Si vous modifiez les paramètres de création en fonction de la valeur de la propriété Transparent, vous devez recréer la poignée de la fenêtre lorsque la propriété change.

  • Peut-être pouvez-vous vous débarrasser complètement du composant et utiliser une TPaintBox à la place ? Elle est transparente tant que vous ne peignez pas l'arrière-plan vous-même. Mais je ne peux pas dire à partir de votre code ce que vous voulez réellement réaliser, donc c'est difficile à dire.

4voto

Rob Kennedy Points 107381

Je pense que vous voulez un contrôle qui peut contenir d'autres contrôles - comme TPanel peut faire - et un contrôle qui peut afficher le contenu de la fenêtre en dessous de lui - comme TImage peut faire lorsque son Transparent est définie. Il semble que vous ayez l'impression erronée que si vous placez un contrôle au-dessus d'un autre, vous obtiendrez le comportement des deux combinés. C'est ce qui ne va pas.

La première chose à faire est de se débarrasser de la TImage contrôle. C'est rendre les choses plus compliquées qu'elles ne le devraient. Lorsque vous devez dessiner un motif de pinceau sur le panneau, dessinez-le directement sur le panneau.

Ensuite, il faut savoir que le ws_ex_Transparent Le style de la fenêtre détermine si frères et sœurs de la fenêtre sont peints en premier. Cela ne dit rien sur la question de savoir si les parent de la fenêtre est repeinte. Si le parent de votre panneau possède la propriété ws_ClipChildren il ne se peindra pas sous l'endroit où votre panneau est censé se trouver. Il semblerait que cela vous aiderait si le parent de votre contrôle de panneau avait la propriété ws_ex_Composited mais en tant qu'auteur de composants, vous n'avez pas le contrôle sur les parents de vos contrôles.

TImage est capable d'apparaître transparent parce qu'il ne s'agit pas d'un contrôle fenêtré. Il n'a pas de poignée de fenêtre, et les règles du système d'exploitation concernant la peinture et le découpage ne s'appliquent donc pas à lui. Du point de vue de Windows, TImage n'existe pas du tout. Ce que nous, dans le monde Delphi, percevons comme le TImage La peinture elle-même est en fait la fenêtre parentale qui s'en remet à un sous-programme séparé pour peindre une certaine région de la fenêtre parentale. C'est pour cette raison que le sous-programme TImage le code de peinture peut simplement ne pas peindre sur une partie de la zone du parent.

Si je devais faire cela, je me demanderais si le contrôle avec le motif de la brosse doit vraiment être un contrôle de conteneur. Pourrais-je utiliser à la place un contrôle TImage avec un motif de brosse répétitif dessiné dessus ? D'autres contrôles peuvent toujours être placés au-dessus, mais ils ne seront pas considérés comme des enfants du contrôle de motif.

0voto

Olivier Pons Points 5085

Essayez de regarder le Bibliothèque Graphics32 : il est très doué pour dessiner des choses et travaille grand avec les bitmaps et la transparence

0voto

Si vous souhaitez que le panneau soit transparent, il vous suffit de remplacer Paint et de ne rien faire (ou de peindre une image transparente, par exemple), ainsi que d'attraper le message WM_ERASEBKGND et de ne rien faire non plus. Cela permet de s'assurer que le panneau ne se peint pas lui-même.

Veillez également à exclure le drapeau csOpaque de ControlStyle, afin que le parent sache qu'il doit se peindre sous le panneau.

Ce que vous avez dans Paint est absolument horrible, d'ailleurs (je parle du truc RedrawWindow). Débarrassez-vous-en. Et WS_EX_TRANSPARENT n'est destiné qu'aux fenêtres de haut niveau, pas aux contrôles.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X