Home

Tutorial : Het maken van een numeriek edit control

TNumEdit

Download de source van dit component hier : DownloadTNumEdit
Of op de componenten page van deze site.

type
  TNumEdit = class(TCustomEdit)
 private
    { Private declarations }
   public
    { Public declarations }
  published
    { Published declarations }
  end;

1. Wat is de bedoeling van het component TNumEdit

TNumEdit is een edit control die alleen numerieke waarden toestaat.
Naast numerieke waarden kan ook het decimal teken(1x) en het min teken(1x) ingevoerd worden.
De invoer van de numerieke waarden is vrij, d.w.z dat er in eerste instantie geen format opgelegd wordt
in de vorm van bijvoorbeeld het aantal decimalen.
Het component biedt echter wel de mogelijkheid om de ingevoerde waarde af te ronden op een opgegeven
aantal decimalen. De formattering (afronden) van het getal wordt gedaan in de method DoExit.
Wanneer de formattering in de Change method zou plaatsvinden zou dit ongewenst gedrag geven omdat dan
bij elke toetsaanslag de formattering zou plaatsvinden waarbij de cursor positie verplaatst. De Change method is echter wel toegevoegd om
waarden die middels code in de NumEdit box worden aangeboden automatisch te af te ronden.

2.Gebruikte technieken

Nieuwe published properties

Naast de ge-erfde properties van TCustomEdit zijn er een aantal nieuwe gepubliceerd te weten :

Alle properties kunnen zowel in design- als runtime geset worden.

Override methods

De volgende methods zijn 'override' van de basisclasse TCustomEdit :

KeyPress

De property keypressed skipped het onchange event.
Middels de functie : Pos(Key, Text) > 0  wordt bepaald of de string, want dat is het nog steeds, reeds een decimaal separator of
een '-'teken bevat. Pos geeft de positie van het gezochte karakter terug, of 0 indien de karakter niet voorkomt.
Middels if (Key = '-') and (Selstart <> 0) then wordt gecheckt of het '-' teken vooraan in de string staat.
Indien het decimaal teken wordt getoetst als deze er al is wordt de cursor verplaatst naar één positie achter de komma,
en wordt tevens het decimale gedeelte automatisch geselecteerd voor optimale input.

procedure TNumEdit.KeyPress(var Key: Char);
begin
  KeyPressed := true;	   // Skip the Change Method
  Inherited KeyPress(Key); // For the user OnKeyPressevent

  //Check for Numeric and backspaces '-' and decimalseparator ','/'.'
  if not (Key in [#8, '0'..'9', '-', DecimalSeparator]) then
  begin
    KeyPressed := False;
    key:=#0;     // Simulate a none keypress
    beep;
  end else  //Check for existing ofdecimalsepatator and '-'
  if ((Key = DecimalSeparator) or (key = '-')) and (Pos(Key, Text) > 0) then
  begin
    If Key = DecimalSeparator then
    begin
      SelStart := Pos(Key, Text);// Position Cursor behind decimalseparator 
      SelLength := Length(Text); // and select decimal part of string
    end;
    key:=#0;
  end else // If '-' then first check position at front
  if (Key = '-') and (Selstart <> 0) then
  begin
    key:=#0;
    beep;
  end;
end;

DoExit / DoEnter

In deze methods wordt het afronden aangestuurd.

procedure TNumEdit.DoEnter;  // Perform rounding when entering
begin
  If AutoRounding then // Only when AutoRounding is enabled
    if (Text <> '') and Round then
      Text := RoundedText(Text);
end;

procedure TNumEdit.DoExit;   // Perform rounding when exiting
begin
  if (Text <> '') and Round then
    Text := RoundedText(Text);
end;

Change

De change method verzorgt de afronding als er geen toets is ingedrukt (KeyPressed false)

De afronding van de waarde

Het afronden wordt gerealiseerd middels de procedure FmtStr.

Function TNumEdit.RoundedText(aText : String) : String; //Perform rounding the text
begin
  FmtStr(Result,'%0.'+ IntToStr(RoundingDecimals) +'f',[StrToFLoat(Text)]);
  KeyPressed := false;
end;


Alignment

Numerieke waarden worden meestal rechts uitgelijnd.
Het probleem met een edit box is dat deze standaard geen mogelijkheid biedt om de Alignment in te stellen.
Na enig doorworstelen van de vcl vond ik dat bijv. TMemo (is in feite een multiline edit) wel deze mogelijkheid biedt.
Aan de hand van deze code heb ik e.e.a in TNumEdit gebouwd.
In het kort komt het er op neer dat het component een published alignment property moet hebben van het type TAlignment.
Middels de override method createparams kan de gewenste style toegevoegd worden :

procedure TNumEdit.CreateParams(var Params: TCreateParams);
const
  Alignments: array[Boolean, TAlignment] of DWORD =
    ((ES_LEFT, ES_RIGHT, ES_CENTER),(ES_RIGHT, ES_LEFT, ES_CENTER));// Windows parameters
begin
  inherited CreateParams(Params);// Don't forget to inherit
  with Params do
  begin
    Style := Style or ES_MULTILINE or
      Alignments[UseRightToLeftAlignment, FAlignment];// Set the style for alignment
  end;
end;

 

De volledige code NumEdit.Pas ziet er als volgt uit :

////////////////////////////////////////////////////////////////////////
// TNumEdit Control version 1.0                              08-01-2000
// Input  : 0..9, -(1x) and the DecimalSeparator (1x)
// This control can also round the value by #decimals
//
// (c) BeenSoft   Http://surf.to/beensoft     Mail : beensoft@yahoo.com
////////////////////////////////////////////////////////////////////////

unit NumEdit;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TNumEdit = class(TCustomEdit)
  private
    { Private declarations }
    FAutoRounding  : Boolean;
    FRound         : Boolean;                       // Rounding
    FRoundingDecimals : Integer;                    // Number of decimals for rounding
    FKeyPressed    : Boolean;                       // Keypressed
    FAlignment     : TAlignment;
    Function RoundedText(aText : String) : String;  // Perform the rounding
    procedure KeyPress(var Key: Char); override;
    procedure DoExit; override;
    procedure DoEnter; override;
    procedure Change; override;
  public
    { Public declarations }
    Constructor Create (AOwner : TComponent); override;
    procedure CreateParams(var Params: TCreateParams); override;
    procedure SetAlignment(Value: TAlignment);
    property KeyPressed : boolean read FKeyPressed write FKeyPressed default False;
    property Alignment: TAlignment read FAlignment write SetAlignment default taLeftJustify;
   published
    { Published declarations }
    //new properties
    property AutoRounding : boolean read FAutoRounding write FAutoRounding default False;
    property RoundingDecimals : Integer read FRoundingDecimals write FRoundingDecimals;
    property Round : boolean read FRound write FRound default false;
    //publishing properties declared in TCustomEdit 
    property Anchors;
    property AutoSelect;
    property AutoSize;
    property BiDiMode;
    property BorderStyle;
    property CharCase;
    property Color;
    property Constraints;
    property Ctl3D;
    property DragCursor;
    property DragKind;
    property DragMode;
    property Enabled;
    property Font;
    property HideSelection;
    property ImeMode;
    property ImeName;
    property MaxLength;
    property OEMConvert;
    property ParentBiDiMode;
    property ParentColor;
    property ParentCtl3D;
    property ParentFont;
    property ParentShowHint;
    property PasswordChar;
    property PopupMenu;
    property ReadOnly;
    property ShowHint;
    property TabOrder;
    property TabStop;
    property Text;
    property Visible;
    property OnChange;
    property OnClick;
    property OnDblClick;
    property OnDragDrop;
    property OnDragOver;
    property OnEndDock;
    property OnEndDrag;
    property OnEnter;
    property OnExit;
    property OnKeyDown;
    property OnKeyPress;
    property OnKeyUp;
    property OnMouseDown;
    property OnMouseMove;
    property OnMouseUp;
    property OnStartDock;
    property OnStartDrag;
  end;

procedure Register;

implementation

Constructor TNumEdit.Create (AOwner : TComponent);
begin
  Inherited Create(AOwner);
  SetAlignment(taRightJustify);
  Text := '0';
end;

procedure TNumEdit.CreateParams(var Params: TCreateParams);
const
  Alignments: array[Boolean, TAlignment] of DWORD =
    ((ES_LEFT, ES_RIGHT, ES_CENTER),(ES_RIGHT, ES_LEFT, ES_CENTER));
begin
  inherited CreateParams(Params);
  with Params do
  begin
    Style := Style or ES_MULTILINE or
      Alignments[UseRightToLeftAlignment, FAlignment];
  end;
end;

procedure TNumEdit.SetAlignment(Value: TAlignment); //Set the Alignment
begin
  if FAlignment <> Value then
  begin
    FAlignment := Value;
    RecreateWnd;   //Force a redraw of the control
  end;
end;

Function TNumEdit.RoundedText(aText : String) : String; //Perform rounding the text
begin
  FmtStr(Result,'%0.'+ IntToStr(RoundingDecimals) +'f',[StrToFLoat(Text)]);
  KeyPressed := false;
end;

procedure TNumEdit.Change;
begin
  if not KeyPressed then
  begin
    if (Text <> '') and Round then
      Text := RoundedText(Text);
  end
end;

procedure TNumEdit.DoEnter;  // Perform rounding when entering
begin
  If AutoRounding then
    if (Text <> '') and Round then
      Text := RoundedText(Text);
end;

procedure TNumEdit.DoExit;   // Perform rounding when exiting
begin
  if (Text <> '') and Round then
    Text := RoundedText(Text);
end;

procedure TNumEdit.KeyPress(var Key: Char);
begin
  KeyPressed := true;
  Inherited KeyPress(Key); // For user events

  //Check for Numeric and backspaces
  if not (Key in [#8, '0'..'9', '-', DecimalSeparator]) then
  begin
    KeyPressed := False;
    key:=#0;
    beep;
  end else  //Check for existing ofdecimalsepatator and '-'
  if ((Key = DecimalSeparator) or (key = '-')) and (Pos(Key, Text) > 0) then
  begin
    If Key = DecimalSeparator then
    begin
      SelStart := Pos(Key, Text);
      SelLength := Length(Text);
    end;
    key:=#0;
  end else // If '-' then first check position at front
  if (Key = '-') and (Selstart <> 0) then
  begin
    key:=#0;
    beep;
  end;
end;

procedure Register;
begin
  RegisterComponents('Beensoft', [TNumEdit]);
end;


end.

arrows.gif (215 bytes)Top