Solved

TEdit - Creating a Custom TEdit Component with Text Indent

Posted on 2013-06-11
3
1,892 Views
Last Modified: 2013-06-12
Hi,

I'm trying to create my own Delphi TEdit Component that will allow me to indent the text in the Text Field from the left (Not using spaces) by a certain number of pixels. This number will be specified in a Property called Indent but I also want to keep all the other visual and formatting features provided but TEdit.

I have managed to compile and install my own component using the code below with mixed results. In the most part this component works, but I lose all the other visual elements and formatting that the base TEdit component provides.

For example, I can no longer get the black border to show around the textbox when I set the Ctrl3D property to false, I can't highlight text or do any other type of text formatting.

If I remove the Paint procedure or comment out the FillRect(ClientRect) line, the component's visual formatting works but the text is painted incorrectly and the cursor is a few pixels behind the end of the line. Do I need to inherit the edit properties from TEdit before re-painting the component?

I'm new to component building and may have coded this all wrong so sorry for the code I've assembled, I've based most of it from what I've managed to read from the internet.

Thanks for your help.


unit TNewEdit;

interface

uses
  System.SysUtils, System.Classes, Vcl.Controls, Vcl.StdCtrls, Vcl.Graphics,
  System.Types, Winapi.Windows, Winapi.Messages;

type
  TMyEdit = class(TEdit)
  private
    { Private declarations }
    FIndent : integer;
    FCanvas : TCanvas;
    procedure SetIndent(const Value:integer);
    procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
  protected
    { Protected declarations }
    procedure Paint; virtual;
    procedure WndProc(var Message: TMessage); override;
    procedure PaintWindow(DC: HDC); override;
    property Canvas: TCanvas read FCanvas;
  public
    { Public declarations }
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    { Published declarations }
    property Indent: Integer read FIndent write SetIndent default 3;
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('My Components', [TMyEdit]);
end;

constructor TMyEdit.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FCanvas := TControlCanvas.Create;
  TControlCanvas(FCanvas).Control := Self;
  FIndent:=3;
end;

destructor TMyEdit.Destroy;
begin
  FCanvas.Free;
  inherited Destroy;
end;

procedure TMyEdit.Paint;
var
  IRec: TRect;
begin
  inherited;

  IRec := ClientRect;
  IRec.Left := IRec.Left + FIndent;
  IRec.Top  := IRec.Top + 2;

  FCanvas.FillRect(ClientRect);
  FCanvas.Brush.Assign(Self.Brush);
  FCanvas.Font.Assign(Self.Font);

  DrawText(FCanvas.Handle, Self.Text, Length(Self.Text), IRec, DT_LEFT);

end;

procedure TMyEdit.SetIndent(const Value: Integer);
begin
  if FIndent <> Value then
   begin
     FIndent := Value;
     Invalidate;
   end;
end;

procedure TMyEdit.PaintWindow(DC: HDC);
begin
  inherited;
  FCanvas.Lock;
  try
   FCanvas.Handle := DC;
   try
     TControlCanvas(FCanvas).UpdateTextFlags;
     Paint;
   finally
     FCanvas.Handle := 0;
   end;
  finally
   FCanvas.Unlock;
  end;
end;

procedure TMyEdit.WMPaint(var Message: TWMPaint);
begin
  ControlState := ControlState+[csCustomPaint];
  inherited;
  ControlState := ControlState-[csCustomPaint];
end;

procedure TMyEdit.WndProc(var Message: TMessage);
begin
 inherited WndProc(Message);
  with Message do
    case Msg of
      CM_MOUSEENTER, CM_MOUSELEAVE, WM_LBUTTONUP, WM_LBUTTONDOWN,
      WM_KEYDOWN, WM_KEYUP,
      WM_SETFOCUS, WM_KILLFOCUS,
      CM_FONTCHANGED, CM_TEXTCHANGED:
      begin
        Invalidate;
      end;
   end; 
end;

end.

Open in new window

0
Comment
Question by:PlantEric
  • 2
3 Comments
 
LVL 22

Accepted Solution

by:
Ferruccio Accalai earned 500 total points
ID: 39240816
Why don't you simply set the left margin? All your code is not needed, you should simply send a message to change the left margin of the Tedit control

Take a look at this example

unit MyEdit;

interface

uses
  SysUtils, Classes, Controls, StdCtrls, windows, messages;

type
  TMyEdit = class(TEdit)
  private
    { Private declarations }
    fIndent: Integer;
    procedure SetIndent(value: Integer);
  protected
    { Protected declarations }
  public
    { Public declarations }
  published
    { Published declarations }
    property Indent: Integer read fIndent write SetIndent;
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('Samples', [TMyEdit]);
end;

procedure TMyEdit.SetIndent(value: Integer);
begin
  if fIndent <> value then
  begin
    fIndent := value;
    // her we use EC_USEFONTINFO accordin to what explained by msdn at http://msdn.microsoft.com/en-us/library/bb761649%28VS.85%29.aspx
    SendMessage(self.Handle, EM_SETMARGINS, EC_USEFONTINFO, value);
  end;
end;

end.

Open in new window

0
 

Author Closing Comment

by:PlantEric
ID: 39241495
Great solution thanks!

Only thing is, when re-loading the project in Delphi it doesn't show the margin at design time only when running, unless I change the indent at design time but then goes back when re-loaded.

Many thanks.
0
 
LVL 22

Expert Comment

by:Ferruccio Accalai
ID: 39241893
@when re-loading the project in Delphi it doesn't show the margin at design time

As in my simple sample everything works fine, I guess that it could depend on something in your component constructor. Can you show me how at the end have you coded your component?
0

Featured Post

Highfive Gives IT Their Time Back

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

Suggested Solutions

Introduction The parallel port is a very commonly known port, it was widely used to connect a printer to the PC, if you look at the back of your computer, for those who don't have newer computers, there will be a port with 25 pins and a small print…
In my programming career I have only very rarely run into situations where operator overloading would be of any use in my work.  Normally those situations involved math with either overly large numbers (hundreds of thousands of digits or accuracy re…
This demo shows you how to set up the containerized NetScaler CPX with NetScaler Management and Analytics System in a non-routable Mesos/Marathon environment for use with Micro-Services applications.
This video shows how to remove a single email address from the Outlook 2010 Auto Suggestion memory. NOTE: For Outlook 2016 and 2013 perform the exact same steps. Open a new email: Click the New email button in Outlook. Start typing the address: …

747 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

16 Experts available now in Live!

Get 1:1 Help Now