我需要限制用户可以键入ftFloat
字段的值的小数位数。
var
Dst : TClientDataSet;
Dsc : TDataSource;
Fld : TNumericField;
Edt : TDBEdit;
begin
//dataset
Dst := TClientDataSet.Create(Self);
Dst.FieldDefs.Add('TEST', ftFloat);
Dst.CreateDataSet();
Dst.Active := True;
Fld := Dst.Fields[0] as TNumericField;
Dst.Append();
Fld.AsFloat := 1234.56;
Dst.Post();
//field
Fld.DisplayFormat := '0,.##'; //2 optional decimals, with thousands separator
Fld.EditFormat := '0.##'; //2 optional decimals, withhout thousands separator
//datasource
Dsc := TDataSource.Create(Self);
Dsc.DataSet := Dst;
//control
Edt := TDBEdit.Create(Self);
Edt.DataSource := Dsc;
Edt.DataField := Fld.FieldName;
Edt.Top := 5;
Edt.Left := 5;
Edt.Parent := Self;
end;
在示例中,在输入1234,5678
后,TDBEdit
控件显示1234,56
,但字段的值为1234,5678
。
根据this answer的建议,我已尝试使用EditMask
属性。
Fld.EditMask := '9' + DecimalSeparator + '99;1; ';
不幸的是,这种方法引入了几个问题:
答案 0 :(得分:1)
您可以在将数字发布到数据集之前删除数字,而不是避免键入字段的额外数字。
剥离"额外" TDataset.OnBeforePost事件中的数字,或者更好地使用TDatasource的OnDataChange事件。 (伪代码,未测试)
procedure TSomeClass.OnDataChange(aField:TField)
begin
if Assigned(aField) and (aField.FieldName='TEST') and not aField.IsNull then
aField.AsFloat:=round(aField.AsFloat*100)/100.0;
end;
答案 1 :(得分:1)
由于我没有在标准VCL控件中发现任何内容,我的方法是拥有一个TDBEdit
后代,可以为其分配所需的DecimalPlaces
,然后可以禁止用户输入超过配置的内容。
这与基础数据类型无关,但对于ftFloat
,它会尝试转换结果值,例如,多次decimalcperator。
这使用KeyPress
来消除会使当前值无效的不需要的密钥,或者添加太多小数位,或者ftFloat
无法通过TryStrToFloat
进行转换。
然后使用样本的示例是:
//control
Edt := TDecimalPlacesDBEdit.Create(Self);
Edt.DataSource := Dsc;
Edt.DataField := Fld.FieldName;
Edt.Top := 5;
Edt.Left := 5;
Edt.Parent := Self;
Edt.DecimalPlaces := 2;
以下是新单位的实施方法:
unit Unit1;
interface
uses
Vcl.DBCtrls;
type
TDecimalPlacesDBEdit = class(TDBEdit)
private
FDecimalPlaces: Integer;
function IsValidChar(Key: Char): Boolean;
protected
procedure KeyPress(var Key: Char); override;
public
property DecimalPlaces: Integer read FDecimalPlaces write FDecimalPlaces;
end;
implementation
uses
System.SysUtils,
Data.DB,
Winapi.Windows;
{ TDecimalPlacesDBEdit }
function TDecimalPlacesDBEdit.IsValidChar(Key: Char): Boolean;
function IsValidText(const S: string): Boolean;
var
ADecPos, AStartPos: Integer;
V: Double;
begin
Result := False;
ADecPos := Pos(FormatSettings.DecimalSeparator, S);
if ADecPos > 0 then
begin
AStartPos := Pos('E', UpperCase(S));
if AStartPos > ADecPos then
ADecPos := AStartPos - ADecPos - 1
else
ADecPos := Length(S) - ADecPos;
if ADecPos > DecimalPlaces then
Exit;
end;
if Assigned(Field) and (Field.DataType in [ftFloat{, ftSingle, ftExtended}]) then
Result := TryStrToFloat(S, V)
else
Result := True;
end;
var
AEndPos, AStartPos: Integer;
S: string;
begin
Result := DecimalPlaces = 0;
if not Result then
begin
S := Text;
AStartPos := SelStart;
AEndPos := SelStart + SelLength;
// Prepare current Text as if the user typed his key, then check if still valid.
Delete(S, SelStart + 1, AEndPos - AStartPos);
Insert(Key, S, AStartPos + 1);
Result := IsValidText(S);
end;
end;
procedure TDecimalPlacesDBEdit.KeyPress(var Key: Char);
begin
inherited KeyPress(Key);
if (Key >= #32) and not IsValidChar(Key) then
begin
MessageBeep(0);
Key := #0;
end;
end;
end.