2017-10-31 34 views
8

Próbuję zaimplementować mój własny rysunek na kontrolerze TEdit, gdy nie ma on fokusa (pokaż elipsę w TEdit, gdy edytor nie wyświetla w pełni tekstu). Więc gwiazda א ed z tym kodem:Dziwne zachowanie komunikatora TEdit i WM_PAINT

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; 

Zauważ, że gdy FEllipsis and (not Focused) komunikat obsługi nic nie robi.

Teraz spadła TButton i 2 TEdit formantów na formularzu, a dodany formularz OnCreate:

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

spodziewałem Edit1 rysować normalnie, a Edit2 nie rysować cokolwiek wewnątrz kontroli edycji.

Zamiast tego obsługa komunikatów została przetworzona w nieskończoność, Edit1 również nie została narysowana, a cała aplikacja dławiła (przy 25% obciążeniu procesora!). Próbowałem również powrócić Message.Result := 0 - ten sam efekt.

Teraz, dla części "dziwnej": gdy zdobędę klamkę płótna z BeginPaint, wszystko działa zgodnie z oczekiwaniami.

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; 

Uwaga Nie zadzwoniłem również pod numer inherited.

Jak wyjaśnić to zachowanie? Dzięki.

Odpowiedz

13

Kiedy okno jest unieważnione, jest proszony o potwierdzenie ważności podczas następnego cyklu malowania. Zazwyczaj dzieje się to w pętli komunikatów głównego wątku, gdy GetMessage stwierdza, że ​​kolejka jest pusta. W tym momencie komunikaty WM_PAINT są syntezowane i wysyłane do okna.

Gdy okno otrzymuje te wiadomości, jego zadaniem jest malowanie się. Zazwyczaj wykonuje się to za pomocą połączeń z numerem BeginPaint, a następnie EndPaint. Wywołanie BeginPaint sprawdza poprawność klienta okna rect. To jest najważniejsza informacja, której brakuje.

Teraz w Twoim kodzie nie zadzwoniłeś pod numer inherited, więc nic nie malowałeś i nie zadzwoniłeś pod numer BeginPaint/EndPaint. Ponieważ nie zadzwoniłeś pod numer BeginPaint, okno pozostaje nieważne. I tak generowany jest nieskończony strumień wiadomości WM_PAINT.

Stosowna dokumentacja można znaleźć here:

BeginPaint funkcja automatycznie sprawdza cały obszar roboczy.

+1

Dzięki! Dokumentacja wyjaśniła: * "System nadal generuje komunikaty WM_PAINT do momentu sprawdzenia aktualnego regionu aktualizacji" * – zig

+1

To musi być najlepszy krótki opis procesu malowania wiadomości systemu Windows, który przeczytałem jeszcze. –