Learning
Learning

Reputation: 127

Using variables as variants not workin

Hi I am building a custom label that will accept variants as input rather than using StrToInt and floatToStrf all the time. The code below works fine if the label input is direct ie

Numlabel1.input=234.56;

but when the value is assigned to a variable

 var
  v : double;

 ...

 v := 234.56;
 numLabel.input := v;

It does not work

Here is part of my code. Can anyone point me in the right direction please?

procedure TNumLabel.SetInput(Value : Variant);
var
  s:string;
begin
  FInput := Value;
  if VarIsType(FInput,256) = True then s:=FInput;  //string
  if VarIsType(FInput,17) = True then s:=IntToStr(FInput);  //integer
  if VarIsType(FInput,18) = True then s:=IntToStr(FInput);  //word
  if VarIsType(FInput,6) = True then  //double
    begin
      GetDecimals; //get the number of becimal places user has selected
      if FCurrency = True then s := FloatToStrF(FInput,ffCurrency,7,FDecimals) else
      s:= FloatToStrF(FInput,ffNumber,7,FDecimals);
    end;
  if FPrefix<>'' then Caption:=FPrefix; //header
  if s<>Null then Caption:=Caption+s+' ';
  if FSuffix<>'' then if FInput<>Null then Caption:=Caption+FSuffix;
end;

As requested where is the whole code

unit NumLabel;

interface

uses WinTypes, WinProcs, Messages, SysUtils, Classes, Controls, 
     Forms, Graphics, Stdctrls, Variants, Dialogs, StrUtils, ESBRtns;

type
  TNumLabel = class(TLabel)
    private
        FCurrency : Boolean;
        FInput : Variant;
        FDecimals : Integer;
        FPrefix : string;
        FSuffix : string;
        FLayout : TTextLayout;
        procedure AutoInitialize;
        procedure AutoDestroy;
        function GetCurrency : Boolean;
        procedure SetCurrency(Value : Boolean);
        function GetInput : Variant;
        procedure SetInput(Value : Variant);
        function GetPrefix : string;
        procedure SetPrefix(Value : string);
        function GetSuffix : string;
        procedure SetSuffix(Value : string);        
        function GetDecimals : Integer;
        function GetLayout : TTextLayout;
        procedure SetLayout(Value : TTextLayout);
        procedure SetDecimals(Value : Integer);
        procedure WMSize(var Message: TWMSize); message WM_SIZE;

    protected
      { Protected fields of TNumLabel }

      { Protected methods of TNumLabel }
        procedure Click; override;
        procedure Loaded; override;
        procedure Paint; override;

    public
      procedure ChkPrefix(Astr:string);
      { Public fields and properties of TNumLabel }
      { Public methods of TNumLabel }
        constructor Create(AOwner: TComponent); override;
        destructor Destroy; override;

    published
      { Published properties of TNumLabel }
        property OnClick;
        property OnDblClick;
        property OnDragDrop;
        property OnMouseDown;
        property OnMouseMove;
        property OnMouseUp;
        property Currency : Boolean read GetCurrency write SetCurrency;
        property Prefix : string read GetPrefix write SetPrefix;
        property Suffix : string read GetSuffix write SetSuffix;        
        property Input : Variant read GetInput write SetInput;
        property Decimals : Integer
             read GetDecimals write SetDecimals
             default 2;
        property Layout : TTextLayout read FLayout write FLayout;

  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('Standard', [TNumLabel]);
end;

procedure TNumLabel.AutoInitialize;
begin
  FDecimals := 2;
end;

procedure TNumLabel.AutoDestroy;
begin
end;

function TNumLabel.GetLayout : TTextLayout;
begin
  Result := GetLayout;
end;

procedure TNumLabel.SetLayout(Value : TTextLayout);
begin
  Layout := Value;
end;

function TNumLabel.GetDecimals : Integer;
begin
  Result := FDecimals;
end;

procedure TNumLabel.SetDecimals(Value : Integer);
begin
  FDecimals := Value;
end;

function TNumLabel.GetCurrency : Boolean;
begin
  Result := FCurrency;
end;

procedure TNumLabel.SetCurrency(Value : Boolean);
begin
  FCurrency := Value;
end;

function TNumLabel.GetPrefix : string;
begin
  ChkPrefix(FPrefix);
  Result := FPrefix;
end;

procedure TNumLabel.SetPrefix(Value : string);
begin
  FPrefix := Value;
  GetInput;
  GetSuffix;
  ChkPrefix(FPrefix);
  if FInput<>Null then Caption:=Caption+FInput+' ';
  if FSuffix<>'' then if FInput<>Null then Caption:=Caption+FSuffix;
  Invalidate;
end;

procedure TNumLabel.ChkPrefix(Astr:string);
begin
  if Astr<>'' then
  begin
    if Layout=tlTop then
      begin
        if Pos(#$D#$A,FPrefix) = 0 then FPrefix:=FPrefix +#$D#$A ;
      end
    else if ((RightStr(FPrefix,1)=' ') and (Layout=tlCenter)) then FPrefix:=FPrefix+' ';
  end;
end;

function TNumLabel.GetSuffix : string;
begin
  Result := FSuffix;
end;

procedure TNumLabel.SetSuffix(Value : string);
begin
  FSuffix :=Value;
  GetPrefix;
  GetInput;
  if FPrefix<>'' then Caption:=FPrefix;
  if FInput<>Null then Caption:=Caption+FInput+' ';
  if FSuffix<>'' then if FInput<>Null then Caption:=Caption+FSuffix;
  Invalidate;
end;

function TNumLabel.GetInput : Variant;
begin
  Result := FInput;
end;

procedure TNumLabel.SetInput(Value : Variant);
 var
   s:string;
begin
  FInput := Value;
  if VarIsType(FInput,256) = True then s:=FInput;
  if VarIsType(FInput,17) = True then s:=IntToStr(FInput);
  if VarIsType(FInput,18) = True then s:=IntToStr(FInput);
  if VarIsType(FInput,6) = True then
    begin
      GetDecimals;
      if FCurrency = True then s := FloatToStrF(FInput,ffCurrency,7,FDecimals) else
      s := FloatToStrF(FInput,ffNumber,7,FDecimals);
    end;
  if FPrefix<>'' then Caption:=FPrefix;
  if s<>Null then Caption:=Caption+s+' ';
  if FSuffix<>'' then if FInput<>Null then Caption:=Caption+FSuffix;
end;

procedure TNumLabel.Click;
begin
  inherited Click;
end;

constructor TNumLabel.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  AutoInitialize;
end;

destructor TNumLabel.Destroy;
begin
  AutoDestroy;
  inherited Destroy;
end;

procedure TNumLabel.Loaded;
begin
  inherited Loaded;
end;

procedure TNumLabel.Paint;
begin
  inherited Paint;
end;

procedure TNumLabel.WMSize(var Message: TWMSize);
var
  W, H: Integer;
begin
  inherited;
  W := Width;
  H := Height;
  if (W <> Width) or (H <> Height) then
  inherited SetBounds(Left, Top, W, H);
  Message.Result := 0;
end;
end.

Upvotes: 2

Views: 466

Answers (2)

Tom Brunberg
Tom Brunberg

Reputation: 21033

As I understand it, you want to concatenate with other text, and otherwise too, format the output, partly according to the type of the number.

You were on the right track, just slightly off.

This is the input I used in my testing:

procedure TForm5.Button1Click(Sender: TObject);
var
  v: double;
begin
  numlab.Decimals := 3;
  v := 234.56;
  numlab.Input := v;
end;

In TNumLabel.SetInput(Value: Variant); I made a few changes to simplify. There are functions (in unit System.Variants) that check the group of type, like VarIsOrdinal() that checks for any ordinal type and VarIsFloat() that checks for any floating point type.

The error you had in your code was that you checked for variant type code 6 which stands for varCurrency but tested it against a Double. Always use literal constants, it is much more easy to read the code, and to get it right.

Finally, here's the modified SetInPut() for you to continue on:

procedure TNumLabel.SetInput(Value : Variant);
 var
   s:string;
begin
  FInput := Value;

  // check for string type
  if VarIsType(FInput, VarString) then s := FInput  else
  // check for any ordinal type
  if VarIsOrdinal(FInput) then s := IntToStr(FInput) else
  // check for any float type
  if VarIsFloat(FInput) then s := FloatToStrF(FInput, ffNumber, 7, FDecimals) else
  // none of those
  s := 'Unknown';

//  if VarIsType(FInput,256) = True then s:=FInput;
//  if VarIsType(FInput,17) = True then s:=IntToStr(FInput);
//  if VarIsType(FInput,18) = True then s:=IntToStr(FInput);
//  if VarIsType(FInput,6) = True then
//    begin
//      GetDecimals;
//      if FCurrency = True then s := FloatToStrF(FInput,ffCurrency,7,FDecimals) else
//      s := FloatToStrF(FInput,ffNumber,7,FDecimals);
//    end;

  if FPrefix<>'' then Caption:=FPrefix;
  if s <> '' then Caption:=Caption+s+' ';
  if FSuffix<>'' then if FInput<>Null then Caption:=Caption+FSuffix;
end;

By the way, if you want to show integers also formatted with decimals according to the FDecimals setting, you can feed FInput (with an integer value) to FloatToStr().

Upvotes: 1

Matej
Matej

Reputation: 472

Your assigment

Numlabel1.input := 234.56;

is somewhat arbitrary: It tells the compiler, that this is a floating point value, but not exactly which one. The compiler could select varSingle, varDouble or varCurrency as variant type. For the value 234.56 the compiler selects varCurrency (6), but for other values (for example 234.56789) the compiler selects varDouble (5).

If you use an intermediate variable of type Double or Extended, then the compiler selects varDouble (5) as variant type.

Therefore you either have to add some code for varType varDouble, i.e.

if VarIsType(FInput, 5) = True then { do something }

(and probably also for varType varSingle if VarIsType(FInput, 4) = True then), or you follow the advice of Remy and use the automatic type conversion of variants. i.e.

var
  V : Variant;
  S : string;

V := 123.45;
S := V;

Variants were created exactly for this, so use it.

Upvotes: 1

Related Questions