2017-10-31 25 views
8

Tôi đang cố gắng triển khai bản vẽ của riêng mình trên điều khiển TEdit khi không có tiêu điểm (hiển thị dấu ba chấm trong TEdit khi trình chỉnh sửa không hiển thị đầy đủ văn bản). Vì vậy, tôi đã gắn dấu sao với mã này:TEDit và WM_PAINT xử lý tin nhắn hành vi lạ

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; 

Lưu ý rằng khi xử lý tin nhắn không làm gì cả.

Bây giờ tôi đã đánh rơi một TButton và 2 TEdit điều khiển trên biểu mẫu, và hình thức OnCreate thêm:

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

tôi mong đợi Edit1 để vẽ bình thường, và Edit2 không để vẽ bất cứ thứ gì bên trong chỉnh sửa kiểm soát.

Thay vào đó, trình xử lý tin nhắn được xử lý không ngừng, Edit1 cũng không được rút ra và toàn bộ ứng dụng bị nghẹt thở (với mức sử dụng CPU 25%!). Tôi cũng đã cố gắng trả lại Message.Result := 0 - cùng một hiệu ứng.

Bây giờ, đối với phần "lạ": Khi tôi có được tay cầm canvas với BeginPaint, mọi thứ hoạt động như mong đợi.

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; 

Lưu ý Tôi cũng không gọi inherited.

Làm cách nào để giải thích hành vi này? Cảm ơn.

Trả lời

13

Khi cửa sổ không hợp lệ, nó được yêu cầu tự làm cho hợp lệ ở chu kỳ sơn tiếp theo. Thông thường điều đó xảy ra trong vòng lặp tin nhắn chính khi GetMessage phát hiện thấy hàng đợi trống. Tại thời điểm đó, WM_PAINT tin nhắn được tổng hợp và gửi đến cửa sổ.

Khi cửa sổ nhận được các thông báo này, nhiệm vụ của nó là tự vẽ. Điều đó thường được thực hiện với các cuộc gọi đến BeginPaint và sau đó EndPaint. Cuộc gọi đến BeginPaint xác thực trực quan của cửa sổ. Đây là phần thông tin quan trọng mà bạn thiếu.

Bây giờ, trong mã của bạn, bạn không gọi inherited và do đó không vẽ bất kỳ thứ gì và không gọi BeginPaint/EndPaint. Vì bạn không gọi BeginPaint, cửa sổ vẫn không hợp lệ. Và do đó, một luồng vô tận gồm WM_PAINT thư được tạo.

Các tài liệu liên quan có thể tìm thấy here:

Các BeginPaint chức năng tự động xác nhận toàn bộ vùng client.

+1

Cảm ơn! Tài liệu hướng dẫn rõ ràng: * "Hệ thống tiếp tục tạo các tin nhắn WM_PAINT cho đến khi vùng cập nhật hiện tại được xác nhận" * – zig

+1

Đây phải là mô tả ngắn gọn nhất về quá trình vẽ thông báo của cửa sổ mà tôi đã đọc. –