gpt4 book ai didi

delphi - 在 Delphi 窗体中绘制控件

转载 作者:行者123 更新时间:2023-12-03 15:20:31 26 4
gpt4 key购买 nike

如何在表单 Canvas 上以及表单上的控件上绘制内容?

我尝试以下操作:

procedure TForm1.FormPaint(Sender: TObject);
var x,y: Integer;
begin
x := Mouse.CursorPos.X - 10;
y := Mouse.CursorPos.Y - 10;
x := ScreentoClient(point(x,y)).X - 10;
y := ScreenToClient(point(x,y)).Y - 10;
Canvas.Brush.Color := clRed;
Canvas.FillRect(rect(x, y, x + 10, y + 10));
Invalidate;
end;

矩形是在绘制其他控件之前绘制的,因此它隐藏在控件后面(根据 Delphi 文档,这是预期行为)。

我的问题是如何绘制控件?

最佳答案

不要在绘画处理程序中“无效”。 Invalidating导致发送WM_PAINT,这当然会重新开始绘画处理。即使您不移动鼠标,您发布的代码示例也会导致“OnPaint”事件一次又一次运行。由于您的绘图取决于光标的位置,因此您可以为此使用“OnMouseMove”事件。但是您还需要拦截其他窗口控件的鼠标消息。因此,下面的示例使用“ApplicationEvents”组件。如果您的应用程序有多个表单,您需要设计一种机制来区分您正在绘制的表单。

另请参阅文档,VCL 的 Invalidate使整个窗口无效。您不需要这样做,您正在绘制一个小矩形,并且您确切地知道您在绘制的位置。只需使您要绘制的位置和已绘制的位置无效即可。

至于在控件上绘图,实际上绘图部分很容易,但你不能使用提供的 Canvas 来做到这一点。表格有 WS_CLIPCHILDREN样式,子窗口的表面将被排除在更新区域之外,因此您必须使用 GetDCExGetWindowDC 。正如评论中提到的“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
// no rectangle drawn at form creation
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

// assume no drawing (will test later against the point).
// also, below RedrawWindow will cause an immediate WM_PAINT, this will
// provide a hint to the paint handler to not to draw anything yet.
FMousePt := Point(-1, -1);


// first, if there's already a previous rectangle, invalidate it to clear
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);

// invalidate childs
// the pointer could be on one window yet parts of the rectangle could be
// on a child or/and a parent, better let Windows handle it all
RedrawWindow(Handle, @R, 0,
RDW_INVALIDATE or RDW_UPDATENOW or RDW_ALLCHILDREN);
end;


// is the message window our form?
if Msg.hwnd = Handle then
// then save the bottom-right coordinates
FMousePt := SmallPointToPoint(TSmallPoint(Msg.lParam))
else begin
// is the message window one of our child windows?
if GetAncestor(Msg.hwnd, GA_ROOT) = Handle then begin
// then convert to form's client coordinates
Pt := SmallPointToPoint(TSmallPoint(Msg.lParam));
windows.ClientToScreen(Msg.hwnd, Pt);
FMousePt := ScreenToClient(Pt);
end;
end;

// will we draw? (test against the point)
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
// save where we draw, we'll need to erase before we draw an other one
FOldPt := FMousePt;

// get a dc that could draw on child windows
DC := GetDCEx(Handle, 0, DCX_PARENTCLIP);

// don't draw on borders & caption
Rgn := CreateRectRgn(ClientRect.Left, ClientRect.Top,
ClientRect.Right, ClientRect.Bottom);
SelectClipRgn(DC, Rgn);
DeleteObject(Rgn);

// draw a red rectangle
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;

关于delphi - 在 Delphi 窗体中绘制控件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4478649/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com