I see my version of delphi, doesn't have Key Events(OnKeyDown, OnKeyUp, OnKeyPress) events for TPaintBox. I would like to process something like that. Had somebody a paintbox with these events?
2 Answers
Like TLama said, you'll need to inherit from TCustomControl. But you'll need some additional code to publish all keyboard events. You could choose the easy way and inherit from TPanel since TPanel already exposes a Canvas and a number of keyboard events.
But here's some code to show how to create and register a new control that published the properties of TCustomControl and introduces a new OnPaint event:
If you create a new Package, add this unit, and install it, you will have a new TGTPaintBox control that can have the focus (although you don't see it). It can retrieve keyboard input too.
unit uBigPaintbox;
interface
uses Windows, Classes, Messages, Controls;
type
TGTPaintBox = class(TCustomControl)
private
FOnPaint: TNotifyEvent;
protected
// Three methods below are for transparent background. This may not work that great,
// and if you don't care about it, you can remove them.
procedure WMEraseBkGnd(var Msg: TWMEraseBkGnd); message WM_ERASEBKGND;
procedure CreateParams(var Params: TCreateParams); override;
procedure SetParent(AParent: TWinControl); override;
protected
procedure Paint; override;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
public
property Canvas;
published
// Introduce OnPaint event
property OnPaint: TNotifyEvent read FOnPaint write FOnPaint;
// Publish keyboard and mouse events.
property OnKeyPress;
property OnKeyDown;
property OnKeyUp;
property OnClick;
property OnDblClick;
property OnMouseUp;
property OnMouseDown;
property OnMouseMove;
// And some other behavioral property that relate to keyboard input.
property TabOrder;
property TabStop;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('GolezTrol', [TGTPaintBox]);
end;
{ TGTPaintBox }
procedure TGTPaintBox.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
Params.ExStyle := Params.ExStyle or WS_EX_TRANSPARENT;
end;
procedure TGTPaintBox.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
Y: Integer);
begin
inherited;
// Focus the control when it is clicked.
if not (csDesigning in ComponentState) and CanFocus then
SetFocus;
end;
procedure TGTPaintBox.Paint;
begin
inherited;
// Call paint even if it is assigned.
if Assigned(FOnPaint) then
FOnPaint(Self);
end;
procedure TGTPaintBox.SetParent(AParent: TWinControl);
var
NewStyle: Integer;
begin
inherited;
if AParent = nil then
Exit;
// Make sure the parent is updated too behind the control.
NewStyle := GetWindowLong(AParent.Handle, GWL_STYLE) and not WS_CLIPCHILDREN;
SetWindowLong(AParent.Handle, GWL_STYLE, NewStyle);
end;
procedure TGTPaintBox.WMEraseBkGnd(var Msg: TWMEraseBkGnd);
begin
SetBkMode(Msg.DC, TRANSPARENT);
Msg.Result := 1;
end;
end.
I've added some functionality in an attempt to make the control transparent, because a PaintBox is too. One disadvantage is that you need to repaint the parent to clear the previously drawn content. In the demo application this is easy enough. I just invalidate the form instead of the control. :p
If you don't need it, you can remove WMEraseBkGnd
, CreateParams
and SetParent
from the control.
Small demo: Put a label on a form. Put a TGTPaintBox on top of it and make it a bit larger. Then add a timer, and maybe some other controls.
Make sure you set the TabStop property of the GTPaintBox to True
.
Then, implement the following events;
// To repaint the lot.
procedure TForm1.Timer1Timer(Sender: TObject);
begin
Invalidate;
end;
// Capture key input and save the last entered letter in the tag.
procedure TForm1.GTPaintBox1KeyPress(Sender: TObject; var Key: Char);
begin
if Key in ['a'..'z'] then
TGTPaintBox(Sender).Tag := Integer(Key);
end;
// Paint the control (this is called every second, when the timer invalidates the form
procedure TForm1.GTPaintBox1Paint(Sender: TObject);
var
PaintBox: TGTPaintBox;
begin
PaintBox := TGTPaintBox(Sender);
// Draw a focus rect too. If you want the control to do this, you would normally
// implement it in the control itself, and make sure it invalides as soon as it
// receives or loses focus.
if PaintBox.Focused then
PaintBox.Canvas.DrawFocusRect(PaintBox.Canvas.ClipRect);
// It just draws the character that we forced into the Tag in the KeyPress event.
PaintBox.Canvas.TextOut(Random(200), Random(200), Char(PaintBox.Tag));
end;
TPaintBox
is aTGraphicControl
descendant and can't get the focus, so it can't even receive keyboard events. So you would have to derive the paint box from another class,TCustomControl
can be the right one for you. In other words, let theTPaintBox
as it is and look for an alternative control or develop your own fromTCustomControl
;) – TLama