delphieventskeypaintpaintbox

I need Key Events for TPaintBox


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?


Solution

  • 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;