在 Delphi 窗体中绘制控件
如何在窗体画布上以及窗体上的控件上绘制内容?
我尝试以下操作:
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 文档,这是预期的行为)。
我的问题是如何绘制控件?
How can I draw something on the Forms canvas and over controls on the Form?
I try the following:
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;
The rectangle is drawn before other controls are drawn, so it is hidden behind the controls (this is expected behavior according to the Delphi Docs).
My questions is how can I draw over controls?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
不要在油漆处理程序中“无效”。 无效会导致
WM_PAINT
被发送,这当然会重新开始绘画处理。即使您不移动鼠标,您发布的代码示例也会导致“OnPaint”事件一次又一次运行。由于您的绘图取决于光标的位置,因此您可以为此使用“OnMouseMove”事件。但是您还需要拦截其他窗口控件的鼠标消息。由于这个原因,下面的示例使用“ApplicationEvents”组件。如果您的应用程序有多个表单,您需要设置一种机制来区分您正在绘制的表单。另请参阅文档,VCL 的
Invalidate
使整个窗口无效。您不需要这样做,您正在绘制一个小矩形,并且您确切地知道您在绘制的位置。只需使您要绘制的位置和已绘制的位置无效即可。至于在控件上绘图,实际上绘图部分很容易,但使用提供的画布无法做到这一点。表单具有
WS_CLIPCHILDREN
样式,子窗口的表面将被排除在更新区域之外,因此您必须使用GetDCEx
或GetWindowDC
。正如评论中提到的“user205376”,删除您绘制的内容有点棘手,因为您实际上可以在多个控件上绘制一个矩形。但 API 也有一个快捷方式,正如您将在代码中看到的那样。我尝试对代码进行一些注释以便能够遵循,但跳过了错误处理。实际的绘制可以在“OnPaint”事件处理程序中进行,但不是从“TWinControl”派生的控件将在处理程序之后进行绘制。所以它在 WM_PAINT 处理程序中。
Do not 'invalidate' in a paint handler. Invalidating causes a
WM_PAINT
to be sent, which of course starts the paint handling all over. Even if you don't move the mouse, the code sample you posted will cause the 'OnPaint' event to run again and again. Since your drawing depends on the position of the cursor, you'd use the 'OnMouseMove' event for this. But you need to intercept mouse messages for other windowed controls as well. The below sample uses a 'ApplicationEvents' component for this reason. If your application will have more than one form, you need to device a mechanism to differentiate which form you are drawing on.Also see on the docs that, VCL's
Invalidate
invalidates the entire window. You don't need to do that, you're drawing a tiny rectangle and you know exactly where you're drawing. Just invalidate where you'll draw and where you've drawn.As for drawing on controls, actually the drawing part is easy, but you can't do that with the provided canvas. Forms have got
WS_CLIPCHILDREN
style, child windows' surfaces will be excluded from the update region, so you'd have to useGetDCEx
orGetWindowDC
. As 'user205376' mentioned in the comments, erasing what you've drawn is a bit more tricky, since you can be drawing one rectangle actually on more than one control. But the api has a shortcut for this too, as you'll see in the code.I tried to comment a bit the code to be able to follow, but skipped error handling. The actual painting could be in the 'OnPaint' event handler, but controls which do not descend from 'TWinControl' are being painted after the handler. So it's in a WM_PAINT handler.
应用程序主窗口不能在其他控制表面上绘制。控件定期绘制和擦除自身(基于控件“绘制周期”)
您的应用程序只能在允许应用程序执行此操作的控件上绘制。许多常见控件为应用程序提供了通过控件自定义绘制技术来自定义控件外观的灵活性。
The application main window cannot draw over other control surface. Controls periodically paint and erase themselves (based on the control "paint cycle")
Your application can only draw on controls that allow the application to do it. Many common controls provide flexibility to applications for customizing the control appearance, thru control custom draw techniques.
你不能。
控件绘制在其父窗口的顶部。无论您在父窗口上绘制什么,都将在该窗口的控件后面看到。目前尚不清楚为什么需要进行这样的绘图;但是,也许您可以在表单内创建一个透明控件并将其设置为前面,然后在其画布上绘制。这样,您的绘图将看起来位于表单及其其他控件的顶部,但这样用户就无法与表单上的其他控件进行交互,因为它们位于透明控件的后面。
You can't.
Controls are drawn on top of their parent window. Whatever you draw on the parent window will be seen behind the controls over that window. It is not clear why you need to do such a drawing; however, maybe you can create a transparent control inside the form and set it to front, then draw on its canvas. That way your drawing would look on top of the form and its other controls, but that way user cannot interact with other controls on the form, because they are behind the transparent control.
你不能这样做。您需要创建一个窗口控件(例如窗口)并将该窗口放置在您想要“在其上”绘制的控件的顶部。然后,您可以
复制带有控件的窗体的位图,并使用该位图作为该新控件的背景图像,或者
使这个新窗口具有不规则形状,以便它在某些不规则形状区域之外是透明的。
You cannot do this. You need to create a windowed control (such as a window) and place this window on top of the controls you want to draw "on". Then you can either
copy the bitmap of the form with controls, and use this bitmap as the background image of this new control, or
make this new window have an irregular shape, so that it is transparent outside some irregularly shaped region.
我做了一些涉及在我的表单上的组件周围绘制句柄的事情,我所做的。
首先创建一条如下消息:
编写一个过程来处理该消息:
Validaterect 将告诉 Windows 无需重新绘制表单。您的绘画将导致部分表格“无效”。 ValidateRect 对 Windows 说一切都是“验证”的。
最后一步,您还需要覆盖绘制过程。
所以每次你的窗体需要重新绘制(WM_Paint)时,它都会调用祖先paint并向消息队列添加一条AfterPaint消息。当消息被处理时,AfterPaint被调用并绘制你的东西并告诉Windows一切都很好,防止再次调用paint。
希望这有帮助。
I did something who involve to draw handles around components on my form here what I did.
First create a message like this :
Write a Procedure to handle the message:
Validaterect will tell Windows that there is no need to repaint your form. Your painting will cause portion of the form to be "invalidate". ValidateRect say to windows everything is "validate".
You also need, last step, to override the paint procedure.
So each time your form need to be repainted (WM_Paint), it will call the ancestor paint and add a AfterPaint message to the message queue. When The message is process, AfterPaint is call and do paint your stuff and tell Windows that everything is fine, preventing another call to paint.
Hope this help.