不要在绘图处理程序中进行“无效”操作。
Invalidating 会导致发送一个
WM_PAINT
,这当然会重新开始绘图处理。即使您不移动鼠标,您发布的代码示例也会一遍又一遍地运行 'OnPaint' 事件。由于您的绘图依赖于光标位置,因此您将使用 'OnMouseMove' 事件完成此操作。但是,您还需要拦截其他窗口控件的鼠标消息。以下示例使用 'ApplicationEvents' 组件,出于这个原因。如果您的应用程序将有多个窗体,则需要设计一种机制来区分您正在绘制的窗体。
另请参见文档,VCL 的 Invalidate
使整个窗口失效。您不需要这样做,您只是绘制一个小矩形,而且您知道您要绘制什么和已经绘制了哪些部分。只需使您要绘制和已绘制的位置无效。
至于在控件上绘图,实际上画图部分很容易,但是您不能使用提供的画布来完成。表单具有 WS_CLIPCHILDREN
样式,子窗口的表面将被从更新区域中排除,因此您必须使用 GetDCEx
或 GetWindowDC
。正如评论中所提到的 'user205376',擦除您所绘制的内容会有些棘手,因为您可能正在多个控件上绘制一个矩形。但是该 API 也有快捷方式,您将在代码中看到。
我尝试对代码进行了一些注释以便于跟踪,但省略了错误处理。实际绘画可以在 'OnPaint' 事件处理程序中完成,但未从 'TWinControl' 下降的控件在处理程序之后绘制。因此,在 WM_PAINT 处理程序中完成它。
type
TForm1 = class(TForm)
[..]
ApplicationEvents1: TApplicationEvents;
procedure FormCreate(Sender: TObject);
procedure ApplicationEvents1Message(var Msg: tagMSG; var Handled: Boolean);
private
FMousePt, FOldPt: TPoint;
procedure WM_PAINT(var Msg: TWmPaint); message WM_PAINT;
public
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
FOldPt := Point(-1, -1);
end;
procedure TForm1.ApplicationEvents1Message(var Msg: tagMSG;
var Handled: Boolean);
var
R: TRect;
Pt: TPoint;
begin
if Msg.message = WM_MOUSEMOVE then begin
FMousePt := Point(-1, -1);
if (FOldPt.X > 0) and (FOldPt.Y > 0) then begin
R := Rect(FOldPt.X - 10, FOldPt.Y - 10, FOldPt.X, FOldPt.Y);
InvalidateRect(Handle, @R, True);
RedrawWindow(Handle, @R, 0,
RDW_INVALIDATE or RDW_UPDATENOW or RDW_ALLCHILDREN);
end;
if Msg.hwnd = Handle then
FMousePt := SmallPointToPoint(TSmallPoint(Msg.lParam))
else begin
if GetAncestor(Msg.hwnd, GA_ROOT) = Handle then begin
Pt := SmallPointToPoint(TSmallPoint(Msg.lParam));
windows.ClientToScreen(Msg.hwnd, Pt);
FMousePt := ScreenToClient(Pt);
end;
end;
if PtInRect(ClientRect, FMousePt) then begin
R := Rect(FMousePt.X - 10, FMousePt.Y - 10, FMousePt.X, FMousePt.Y);
InvalidateRect(Handle, @R, False);
end;
end;
end;
procedure TForm1.WM_PAINT(var Msg: TWmPaint);
var
DC: HDC;
Rgn: HRGN;
begin
inherited;
if (FMousePt.X > 0) and (FMousePt.Y > 0) then begin
FOldPt := FMousePt;
DC := GetDCEx(Handle, 0, DCX_PARENTCLIP);
Rgn := CreateRectRgn(ClientRect.Left, ClientRect.Top,
ClientRect.Right, ClientRect.Bottom);
SelectClipRgn(DC, Rgn);
DeleteObject(Rgn);
SelectObject(DC, GetStockObject(DC_BRUSH));
SetDCBrushColor(DC, ColorToRGB(clRed));
FillRect(DC, Rect(FMousePt.X - 10, FMousePt.Y - 10, FMousePt.X, FMousePt.Y), 0);
ReleaseDC(Handle, DC);
end;
end;
TMemo
上放置一个TButton
,您是否期望备忘录能够在按钮表面上绘制?如果您将 Microsoft WordPad 窗口放在 Microsoft Paint 窗口上方,您是否期望 Paint 窗口能够在 WordPad 窗口内部绘制? - Andreas Rejbrand