unit CurrEdit; (************************************************************************** This is my first custom control, so please be merciful. I needed a simple currency edit field, so below is my attempt. It has pretty good behavior and I have posted it up to encourage others to share their code as well. Essentially, the CurrencyEdit field is a modified memo field. I have put in keyboard restrictions, so the user cannot enter invalid characters. When the user leaves the field, the number is reformatted to display appropriately. You can left-, center-, or right-justify the field, and you can also specify its display format - see the FormatFloat command. The field value is stored in a property called Value so you should read and write to that in your program. This field is of type Extended. If you like this control you can feel free to use it, however, if you modify it, I would like you to send me whatever you did to it. If you send me your CIS ID, I will send you copies of my custom controls that I develop in the future. Please feel free to send me anything you are working on as well. Perhaps we can spark ideas! Robert Vivrette, Owner Prime Time Programming PO Box 5018 Walnut Creek, CA 94596-1018 Fax: (510) 939-3775 CIS: 76416,1373 Net: RobertV@ix.netcom.com Thanks to Massimo Ottavini, Thorsten Suhr, Bob Osborn, Mark Erbaugh, Ralf Gosch, Julian Zagorodnev, and Grant R. Boggs for their enhancements! Please look for this and other components in the "Unofficial Newsletter of Delphi Users" posted on the Borland Delphi forum on Compuserve (GO DELPHI) in the "Delphi IDE" file section. **************************************************************************) interface uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Menus, Forms, Dialogs, StdCtrls; type TCurrencyEdit = class(TCustomMemo) private DispFormat: string; FieldValue: Extended; FDecimalPlaces : Word; FPosColor : TColor; FNegColor : TColor; procedure SetFormat(A: string); procedure SetFieldValue(A: Extended); procedure SetDecimalPlaces(A: Word); procedure SetPosColor(A: TColor); procedure SetNegColor(A: TColor); procedure CMEnter(var Message: TCMEnter); message CM_ENTER; procedure CMExit(var Message: TCMExit); message CM_EXIT; procedure FormatText; procedure UnFormatText; protected procedure KeyPress(var Key: Char); override; procedure CreateParams(var Params: TCreateParams); override; public constructor Create(AOwner: TComponent); override; published property Alignment default taRightJustify; property AutoSize default True; property BorderStyle; property Color; property Ctl3D; property DecimalPlaces: Word read FDecimalPlaces write SetDecimalPlaces default 2; property DisplayFormat: string read DispFormat write SetFormat; property DragCursor; property DragMode; property Enabled; property Font; property HideSelection; property MaxLength; property NegColor: TColor read FNegColor write SetNegColor default clRed; property ParentColor; property ParentCtl3D; property ParentFont; property ParentShowHint; property PopupMenu; property PosColor: TColor read FPosColor write SetPosColor default clBlack; property ReadOnly; property ShowHint; property TabOrder; property Value: Extended read FieldValue write SetFieldValue; property Visible; property OnChange; property OnClick; property OnDblClick; property OnDragDrop; property OnDragOver; property OnEndDrag; property OnEnter; property OnExit; property OnKeyDown; property OnKeyPress; property OnKeyUp; property OnMouseDown; property OnMouseMove; property OnMouseUp; end; procedure Register; implementation procedure Register; begin RegisterComponents('Additional', [TCurrencyEdit]); end; constructor TCurrencyEdit.Create(AOwner: TComponent); begin inherited Create(AOwner); AutoSize := False; Alignment := taRightJustify; Width := 121; Height := 25; DispFormat := '$,0.00;($,0.00)'; FieldValue := 0.0; FDecimalPlaces := 2; FPosColor := Font.Color; FNegColor := clRed; AutoSelect := False; {WantReturns := False;} WordWrap := False; FormatText; end; procedure TCurrencyEdit.SetFormat(A: String); begin if DispFormat <> A then begin DispFormat:= A; FormatText; end; end; procedure TCurrencyEdit.SetFieldValue(A: Extended); begin if FieldValue <> A then begin FieldValue := A; FormatText; end; end; procedure TCurrencyEdit.SetDecimalPlaces(A: Word); begin if DecimalPlaces <> A then begin DecimalPlaces := A; FormatText; end; end; procedure TCurrencyEdit.SetPosColor(A: TColor); begin if FPosColor <> A then begin FPosColor := A; FormatText; end; end; procedure TCurrencyEdit.SetNegColor(A: TColor); begin if FNegColor <> A then begin FNegColor := A; FormatText; end; end; procedure TCurrencyEdit.UnFormatText; var TmpText : String; Tmp : Byte; IsNeg : Boolean; begin IsNeg := (Pos('-',Text) > 0) or (Pos('(',Text) > 0); TmpText := ''; For Tmp := 1 to Length(Text) do if Text[Tmp] in ['0'..'9',DecimalSeparator] then TmpText := TmpText + Text[Tmp]; try If TmpText='' Then TmpText := '0.00'; FieldValue := StrToFloat(TmpText); if IsNeg then FieldValue := -FieldValue; except MessageBeep(mb_IconAsterisk); end; end; procedure TCurrencyEdit.FormatText; begin Text := FormatFloat(DispFormat,FieldValue); if FieldValue < 0 then Font.Color := NegColor else Font.Color := PosColor; end; procedure TCurrencyEdit.CMEnter(var Message: TCMEnter); begin SelectAll; inherited; end; procedure TCurrencyEdit.CMExit(var Message: TCMExit); begin UnformatText; FormatText; Inherited; end; procedure TCurrencyEdit.KeyPress(var Key: Char); Var S : String; frmParent : TForm; btnDefault : TButton; i : integer; wID : Word; LParam : LongRec; begin {#8 is for Del and Backspace keys.} if Not (Key in ['0'..'9','.','-', #8, #13]) Then Key := #0; case Key of #13 : begin frmParent := GetParentForm(Self); UnformatText; {find default button on the parent form if any} btnDefault := nil; for i := 0 to frmParent.ControlCount -1 do if frmParent.Controls[i] is TButton then if (frmParent.Controls[i] as TButton).Default then btnDefault := (frmParent.Controls[i] as TButton); {if there's a default button, then make the parent form think it was pressed} if btnDefault <> nil then begin wID := GetWindowWord(btnDefault.Handle, GWW_ID); LParam.Lo := btnDefault.Handle; LParam.Hi := BN_CLICKED; SendMessage(frmParent.Handle, WM_COMMAND, wID, longint(LParam) ); end; Key := #0; end; { allow only one dot in the number } '.' : if ( Pos('.',Text) >0 ) then Key := #0; { allow only one '-' in the number and only in the first position: } '-' : if ( Pos('-',Text) >0 ) or ( SelStart > 0 ) then Key := #0; else { make sure no other character appears before the '-' } if ( Pos('-',Text) >0 ) and ( SelStart = 0 ) and (SelLength=0) then Key := #0; end; if Key <> Char(vk_Back) then begin {S is a model of Text if we accept the keystroke. Use SelStart and SelLength to find the cursor (insert) position.} S := Copy(Text,1,SelStart)+Key+Copy(Text,SelStart+SelLength+1,Length(Text)); if ((Pos(DecimalSeparator, S) > 0) and (Length(S) - Pos(DecimalSeparator, S) > FDecimalPlaces)) {too many decimal places} or ((Key = '-') and (Pos('-', Text) <> 0)) {only one minus...} or (Pos('-', S) > 1) {... and only at beginning} then Key := #0; end; if Key <> #0 then inherited KeyPress(Key); end; procedure TCurrencyEdit.CreateParams(var Params: TCreateParams); var lStyle : longint; begin inherited CreateParams(Params); case Alignment of taLeftJustify : lStyle := ES_LEFT; taRightJustify : lStyle := ES_RIGHT; taCenter : lStyle := ES_CENTER; end; Params.Style := Params.Style or lStyle; end; end.