如何将多个TcxDBTextEdit与格式化的数据一起存储在单个DB字段中?

时间:2019-06-28 09:37:11

标签: delphi devexpress ado tadoquery tdxmemdata

我需要在一个数据库字段中存储两个值(是的,我同意这是不好的做法,但这是无法更改的旧数据库)。数据存储为string1#4string2

需要使用两个单独的TcxDBTextEdit控件来编辑数据。但是,如何将它们连接到单个数据库字段,以便可以在其中一个编辑string1而在另一个编辑string2

我尝试将两个计算(fkCalculated)字段添加到TADOQuery,提取/合并它们在OnGetText / OnSetText中的值,并读/写到{ {1}},但没有用。

因此,我尝试使用两个字段创建一个TStringField组件,并使用它们代替计算字段,但是仍然无法正常工作。

如何实现(不更改数据库结构)?

1 个答案:

答案 0 :(得分:1)

下面的示例项目可以满足您的需求。

更新下面的代码替换了我最初发布的代码,并避免了使用 支持fkInternalCalc字段的数据集类型(TClientDataSet)的形式。它 使用TAdoQuery。

尽管原则上将字符串字段解析为两个子字段没有困难 并在您的gui中显示它们以进行编辑,通过简单的方法可以解决此问题 TAdoQuery是它仅支持fkCalculated计算字段和db-gui gui控件 将其视为用户不可修改的内容。

我不确定为什么存在此限制,但我认为这与以下事实有关: Delphi的数据库感知控件最初是为BDE开发的(在以前的情况下是这样) 添加了fkInternalCalc以支持TClientDataSet)。 DB.Pas中强制执行的代码 限制在DB.Pas中:

function TField.GetCanModify: Boolean;
begin
  if FieldNo > 0 then
    if DataSet.State <> dsSetKey then
      Result := not ReadOnly and DataSet.CanModify else
      Result := IsIndexField
  else
    Result := False;
end;

下面的代码通过为TStringField添加一个插入器类来工作, 删除对FieldKind为fkCalculated的字符串字段的限制 不是只读的并且属于可修改的数据集 (尽管我认为可以取消后者的限制)。的 中介程序TStringField会像这样覆盖GetCanModify

function TStringField.GetCanModify: Boolean;
begin
  if (FieldKind = fkCalculated) and DataSet.CanModify and not ReadOnly then
    Result := True
  else
  if DataSet.State <> dsSetKey then
    Result := not ReadOnly and DataSet.CanModify else
    Result := IsIndexField
end;

下面是示例项目的完整代码。请注意,我使用常规 TDBEdits,因为我当前的Devex设置有问题,但是代码应该 与TcxDBEdit一起正常工作。

代码:

type
  TStringField = class(db.TStringField)
  protected
    function GetCanModify : Boolean; override;
  end;

type
  TForm1 = class(TForm)
    DBGrid1: TDBGrid;
    DBNavigator1: TDBNavigator;
    DataSource1: TDataSource;
    DBEdit1: TDBEdit;
    DBEdit2: TDBEdit;
    ADOQuery1: TADOQuery;
    cxDBMaskEdit1: TcxDBMaskEdit;
    DBEdit3: TDBEdit;
    btnDataLinks: TButton;
    ADOConnection1: TADOConnection;
    ADOQuery1ID: TIntegerField;
    ADOQuery1Field1: TWideStringField;
    ADOQuery1Field2: TWideStringField;
    ADOQuery1SubField1: TStringField;
    ADOQuery1SubField2: TStringField;
    procedure FormCreate(Sender: TObject);
    procedure ADOQuery1BeforePost(DataSet: TDataSet);
    procedure ADOQuery1CalcFields(DataSet: TDataSet);
  private
    procedure UpdateSubFields(DataSet : TDataSet);
    procedure UpdateField1(DataSet: TDataSet);
  end;

[...]
const
  scSeparator = '#4';   // could be a literal #4 instead

procedure TForm1.UpdateField1(DataSet : TDataSet);
var
  S : String;
begin
  if DataSet.FieldByName('SubField1').IsNull or DataSet.FieldByName('SubField2').IsNull then exit;

  S := DataSet.FieldByName('SubField1').AsString + scSeparator +
    DataSet.FieldByName('SubField2').AsString;
  S := Trim(S);
  if Length(S) > DataSet.FieldByName('Field1').Size then
    raise exception.Create('tthe combined size of the subfields is too long');

  DataSet.FieldByName('Field1').AsString := S;
end;

procedure TForm1.UpdateSubFields(DataSet : TDataSet);
var
  S,
  SF1,
  SF2 : String;
  P,
  SF2Start : Integer;
begin
  S := DataSet.FieldByName('Field1').AsString;
  P := Pos(scSeparator, S);
  SF1 := Copy(S, 1, P-1);
  SF1 := Trim(SF1);
  SF2Start :=  P + Length(scSeparator);
  SF2 := Copy(S, Sf2Start, Length(S));
  SF2 := Trim(SF2);

  DataSet.FieldByName('SubField1').AsString := SF1;
  DataSet.FieldByName('SubField2').AsString := SF2;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  AdoQuery1.Open;
end;

procedure TForm1.CDS1CalcFields(DataSet: TDataSet);
begin
  UpdateSubFields(DataSet);
end;

function TStringField.GetCanModify: Boolean;
begin
  if (FieldKind = fkCalculated) and DataSet.CanModify and not ReadOnly then
    Result := True
  else
  if DataSet.State <> dsSetKey then
    Result := not ReadOnly and DataSet.CanModify else
    Result := IsIndexField
end;

procedure TForm1.ADOQuery1BeforePost(DataSet: TDataSet);
begin
  UpdateField1(AdoQuery1);
end;

procedure TForm1.ADOQuery1CalcFields(DataSet: TDataSet);
begin
  UpdateSubFields(DataSet);
end;