Solved

TEdit - Creating a Custom TEdit Component with Text Indent

Posted on 2013-06-11
3
2,086 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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

Free Tool: IP Lookup

Get more info about an IP address or domain name, such as organization, abuse contacts and geolocation.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Have you ever had your Delphi form/application just hanging while waiting for data to load? This is the article to read if you want to learn some things about adding threads for data loading in the background. First, I'll setup a general applica…
Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
NetCrunch network monitor is a highly extensive platform for network monitoring and alert generation. In this video you'll see a live demo of NetCrunch with most notable features explained in a walk-through manner. You'll also get to know the philos…
Monitoring a network: how to monitor network services and why? Michael Kulchisky, MCSE, MCSA, MCP, VTSP, VSP, CCSP outlines the philosophy behind service monitoring and why a handshake validation is critical in network monitoring. Software utilized …

688 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