Jump to content




Recent Status Updates

  • Photo
      15 Sep
    Error

    Programming is something that I enjoy and want to make a career out of. But, I usually tend to start things and not finish them. Any advice on how I can finish what I start?

    Show comments (2)
View All Updates

Developed by Kemal Taskin
Photo
- - - - -

TEdit with Limited Acceptable Characters

custom edit control tedit descendant character filter custom control class delphi inheritance

  • Please log in to reply
No replies to this topic

#1 Luthfi

Luthfi

    CC Leader

  • Expert Member
  • PipPipPipPipPipPipPip
  • 1,318 posts
  • Programming Language:PHP, Delphi/Object Pascal, Pascal, Transact-SQL
  • Learning:C, Java, PHP

Posted 18 October 2012 - 02:59 AM

In previous tutorials (Numbers Only TEdit and Numbers Only TEdit Descendant) we have discussed how to get numbers-only TEdit. However you've got to admit that we had many occasions that we need more than that. For example we may need to also accept period and/or comma when accepting numbers. Or we might have to limit to certain characters set only which is not numbers only.

This tutorial will show you how to develop a descendant of TEdit control that be able to limit itself to only accept certain set of characters.

Since this TEdit descendant is capable to replace the TEditEx we created in "Numbers Only TEdit Descendant" tutorial, we will also name it TEditEx.

Properties
Let's start by defining properties for TEditEx.


Property IsLimitChars

We want TEditEx to be able to handle ordinary TEdit tasks. Therefore we need a flag to indicate if we want the characters limitation or not. For this we need a boolean property. We'll name it IsLimitChars. When this property has true value, TEditEx will only accept certain set of characters.


Property AcceptableChars

Of course we need a property to specify which character(s) that acceptable when the character limitation is active. In this case we can simply use string type, because a string could contain multiple characters. AcceptableChars would be a perfect name for this property.


Property IsCaseSensitive

Alphabet characters could come in two modes, upper and lower case. Sometimes we want TEditEx to limit to character(s) in certain case only. Therefore we need another flag to indicate whether TEditEx should care about the case difference. Let's define a boolean property with name of IsCaseSensitive for this flag. I believe IsCaseSensitive would be a quite intuitive name.


Grouping the Properties

Original TEdit itself already got a lot of published properties, crowding the Object Inspector. Personally I want to have closely related properties to be near each other to make it easier for me to understand what's going on should I encounter problems after changing multiple properties. To group the above properties, I decided to delegate the properties to a TPersistent object that will be manipulate privately by TEditEx. I will name the TPersistent descendant class TCharsLimittingOption.

Here is the declaration of TCharsLimittingOption class.
 

type
  TCharsLimittingOption=class(TPersistent)
  private
    FOnChanged: TNotifyEvent;
    FUpdateLvl: Integer;
    FIsLimitChars: Boolean;
    FAcceptableChars: string;
    FIsCaseSensitive: Boolean;

    procedure SetAcceptableChars(const Value: string);
    procedure SetIsCaseSensitive(const Value: Boolean);
    procedure SetIsLimitChars(const Value: Boolean);
  protected
    procedure Changed;
    procedure AssignTo(Dest: TPersistent); override;

    property onchanged: TNotifyEvent read Fonchanged write Fonchanged;
  public
    procedure BeginUpdate;
    procedure EndUpdate;
  published
    property IsLimitChars : Boolean read FIsLimitChars write SetIsLimitChars default False;
    property IsCaseSensitive: Boolean read FIsCaseSensitive write SetIsCaseSensitive default False;
    property AcceptableChars: string read FAcceptableChars write SetAcceptableChars;
  end;

 

The most important aspect of this class is that when either one of the property is changed (through one of the "Set" methods or through the Assign/AssignTo), the onchanged event will be triggered.

Full source code of this class is included in zip file attached at the end of this tutorial. Be sure to inspect the code to fully understand about this class.


Using TCharsLimittingOption in TEditEx

We need an instance of TCharsLimittingOption "inside" private section of TEditEx. Then we publish it as a property. Something like this.
 

TEditEx=class(StdCtrls.TEdit)
private
  ...
  FCharsLimittingOption: TCharsLimittingOption;
  procedure SetCharsLimittingOption(AValue: TCharsLimittingOption);
  ...
published
  ...
  property CharsLimittingOption: TCharsLimittingOption read FCharsLimittingOption write SetCharsLimittingOption;
  ...
end;

 

Note the set method that we used. We can not just simply replace content of FCharsLimittingOption with new one assigned through this property. It would introduce memory leak or access violation. We simply assign the properties of the supplied TCharsLimittingOption to FCharsLimittingOption through its Assign method. Like shown in the implementation of SetCharsLimittingOption below.
 

procedure TEditEx.SetCharsLimittingOption(AValue: TCharsLimittingOption);
begin
  FCharsLimittingOption.Assign(AValue);
end;

 

Of course FCharsLimittingOption must be instanced and freed in TEditEx's constructor and destructor respectively.

Using this grouping trick you will get something like shown below. Doesn't it make it easier to examine TEditEx in design time?

LimitCharsTEdit_GroupProperties01.png


TEditEx Coding

In order to uphold the limitation TEditEx has to do these.
 

  • Check a string value against the acceptable characters.
  • Intercept keyboard press, check against the rule and if everything okay continue with "standard" process.
  • Intercept pasting operation, check clipboard content against the rule, and if the clipboard content is okay process with "standard" process.


Check a String value Against the Rule

Obviously TEditEx needs a method that checks a string against the current rule. Let's implement it as a function method that returns boolean. It returns true if the given string is accepted by the rule, and false when otherwise. We will name this method an intuitive name of IsAcceptable.

I am going to put this method in protected section, hoping that descendant of TEditEx could use it if the need arise.

So the declaration is like this.
 

type
TEditEx=class(StdCtrls.TEdit)
...
protected
  function IsAcceptable(AStr: string): Boolean;
...
end;

 

And the implementation would be.
 

function TEditEx.IsAcceptable(AStr: string): Boolean;
var
  i: Integer;
  vAcceptableChars: string;
begin
  Result := True;

  if FCharsLimittingOption.IsCaseSensitive then
    vAcceptableChars := FCharsLimittingOption.AcceptableChars
  else begin
    vAcceptableChars := UpperCase(FCharsLimittingOption.AcceptableChars);
    AStr := UpperCase(AStr);
  end;

  for i := 1 to Length(AStr) do
    if System.Pos(AStr, vAcceptableChars) < 1 then
    begin
      Result := False;
      Exit;
    end;
end;


 

Intercepting Keyboard Press

TEdit is a descendant of TWinControl. Therefore it also inherits KeyPress dynamic method. {I]KeyPress will automatically be called after processing of WM_KEY message yield "valid" character. This is why I believe overriding this dynamic method is the best place to intercept keyboard press.

Let's override!

Add this declaration.



type
  TEditEx=class(StdCtrls.TEdit)
  ...
  protected
    procedure KeyPress(var Key: Char); override;
  ...
  end;

 

And the implementation would be.
 

procedure TEditEx.KeyPress(var Key: Char);
begin
  if not (Key in [#13, #8, #22])
     and FCharsLimittingOption.IsLimitChars
     and not IsAcceptable(Key) then
    raise EUnacceptableChar.Create('Typing unacceptable character');
  inherited;
end;


 

Intercept Pasting Operation

To intercept the pasting operation we just add a WM_PASTE custom message handler in TEditEx. So declare a method like below. Private scope is okay.

 

type
  TEditEx=class(StdCtrls.TEdit)
  ...
  private
    procedure HandleWMPASTE(var AMsg: TWMPaste); message WM_PASTE;
  ...
  end;

 

And the implementation.
 

procedure TEditEx.HandleWMPASTE(var AMsg: TWMPaste);
var
  S: string;
begin
  if FCharsLimittingOption.IsLimitChars then
  begin
    S := Clipboard.AsText;
    if not IsAcceptable(S) then
      raise EUnacceptableChar.Create('Pasting unacceptable character');
  end;

  inherited;
end;


 

Demo Project


I have created a demo project along with a package for easy installation of TEditEx. It's attached at the end of this tutorial. Go download the zip attachment and extract to location you like, open ProjectGroup1.bpg in Delphi IDE, then install LimitedCharsTEdit_ package.

After that you can open LimitedCharsTEdit_Demo.exe project, compile, and run it. And give me feedback :)

Cheers!


Attached File  TrueLimitedCharsEdit.zip   209.69KB   153 downloads (fixed missing LimitedCharsEdit.pas file)


Edited by LuthfiHakim, 19 April 2013 - 05:23 AM.

  • 0