8 votes

Comportement étrange de TEdit et du gestionnaire de messages WM_PAINT

J'essaie de mettre en œuvre mon propre dessin sur un TEdit lorsqu'il n'a pas le focus (montrer les ellipses en TEdit lorsque l'éditeur n'affiche pas complètement son texte). J'ai donc commencé par ce code :

type
  TEdit = class(StdCtrls.TEdit)
  private
    FEllipsis: Boolean;
    FCanvas: TCanvas;
    procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  end;

constructor TEdit.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FEllipsis := False;
  FCanvas := TControlCanvas.Create;
  TControlCanvas(FCanvas).Control := Self;
end;

destructor TEdit.Destroy;
begin
  FCanvas.Free;
  inherited;
end;

procedure TEdit.WMPaint(var Message: TWMPaint);
begin
  if FEllipsis and (not Focused) then
  begin
    // Message.Result := 0;
    // TODO...
  end
  else
    inherited;
end;

Remarquez que lorsque FEllipsis and (not Focused) le gestionnaire de message ne fait rien.

Maintenant, j'ai laissé tomber un TButton et 2 TEdit sur le formulaire, et ajouté des contrôles de formulaire OnCreate :

procedure TForm1.FormCreate(Sender: TObject);
begin
  Edit2.FEllipsis := True;
end;

Je m'attendais à Edit1 pour tirer normalement, et Edit2 de ne rien dessiner à l'intérieur du contrôle d'édition.

Au lieu de cela, le gestionnaire de messages a été traité sans fin, Edit1 ne s'affichait pas non plus, et l'application entière s'étouffait (avec une utilisation de 25 % du processeur !). J'ai également essayé de renvoyer Message.Result := 0 - même effet.

Maintenant, la partie "étrange" : Lorsque j'obtiens la poignée du canevas avec BeginPaint Tout fonctionne comme prévu.

procedure TEdit.WMPaint(var Message: TWMPaint);
var
  PS: TPaintStruct;
begin
  if FEllipsis and (not Focused) then
  begin    
    if Message.DC = 0 then
      FCanvas.Handle := BeginPaint(Handle, PS)
    else
      FCanvas.Handle := Message.DC;
    try
      // paint on FCanvas...
    finally
      FCanvas.Handle := 0;
      if Message.DC = 0 then EndPaint(Handle, PS);
    end;
  end
  else
    inherited;
end;

Remarquez que je n'ai pas appelé inherited soit.

Comment expliquer ce comportement ? Merci de votre compréhension.

13voto

David Heffernan Points 292687

Lorsqu'une fenêtre est invalidée, il lui est demandé de se rendre valide au prochain cycle de peinture. Typiquement, cela se produit dans la boucle de messages du fil principal lorsque GetMessage constate que la file d'attente est vide. À ce moment-là, le WM_PAINT sont synthétisés et envoyés à la fenêtre.

Lorsque la fenêtre reçoit ces messages, sa tâche consiste à se peindre. Cela se fait généralement par des appels à BeginPaint et ensuite EndPaint . L'appel à BeginPaint valide le recto du client de la fenêtre. C'est l'élément d'information critique qui vous manque.

Dans votre code, vous n'avez pas appelé inherited et n'a donc rien peint, et n'a pas appelé BeginPaint / EndPaint . Parce que vous n'avez pas appelé BeginPaint la fenêtre reste invalide. C'est ainsi qu'un flot incessant de WM_PAINT sont générés.

La documentation pertinente peut être consultée à l'adresse suivante aquí :

En Début de la peinture valide automatiquement l'ensemble de l'espace client.

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