如何将多个 TcxDBTextEdit 与存储在单个数据库字段中的格式化数据一起使用?
How can I use multiple TcxDBTextEdit with formatted data stored in a single DB field?
我需要在一个数据库字段中存储两个值(是的,我同意这是不好的做法,但这是无法更改的遗留数据库)。数据存储为 string1#4string2
.
需要使用两个单独的 TcxDBTextEdit
控件编辑数据。但是我怎样才能将它们连接到单个数据库字段,以便我可以在一个中编辑 string1
而在另一个中编辑 string2
?
我尝试将两个计算的 (fkCalculated
) 字段添加到 TADOQuery
,extracting/joining 它们在 OnGetText
/OnSetText
和 reading/writing 到 TStringField
,但没有成功。
所以我尝试创建一个包含两个字段的 TdxMemData
组件并使用它们代替计算字段,但它仍然不起作用。
我怎样才能做到这一点(不改变数据库结构)?
下面的示例项目可以满足您的要求。
更新 下面的代码替换了我原来贴的代码,避免了使用
支持 fkInternalCalc 字段的数据集类型 (TClientDataSet)。它将
使用 TAdoQuery。
虽然把一个字符串字段解析成两个子字段在原理上没有难度
并在您的 gui 中显示它们以进行编辑,使用直接的方法来执行此操作的问题
a TAdoQuery是它只支持fkCalculated计算字段和db-aware gui控件
将这些视为用户不可修改。
我不确定为什么存在这个限制,但我想这与以下事实有关
Delphi 的 db-aware 控件最初是为 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 添加一个插入器 class 来工作
删除 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 set-up 有问题但是代码应该
与 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;
我需要在一个数据库字段中存储两个值(是的,我同意这是不好的做法,但这是无法更改的遗留数据库)。数据存储为 string1#4string2
.
需要使用两个单独的 TcxDBTextEdit
控件编辑数据。但是我怎样才能将它们连接到单个数据库字段,以便我可以在一个中编辑 string1
而在另一个中编辑 string2
?
我尝试将两个计算的 (fkCalculated
) 字段添加到 TADOQuery
,extracting/joining 它们在 OnGetText
/OnSetText
和 reading/writing 到 TStringField
,但没有成功。
所以我尝试创建一个包含两个字段的 TdxMemData
组件并使用它们代替计算字段,但它仍然不起作用。
我怎样才能做到这一点(不改变数据库结构)?
下面的示例项目可以满足您的要求。
更新 下面的代码替换了我原来贴的代码,避免了使用 支持 fkInternalCalc 字段的数据集类型 (TClientDataSet)。它将 使用 TAdoQuery。
虽然把一个字符串字段解析成两个子字段在原理上没有难度 并在您的 gui 中显示它们以进行编辑,使用直接的方法来执行此操作的问题 a TAdoQuery是它只支持fkCalculated计算字段和db-aware gui控件 将这些视为用户不可修改。
我不确定为什么存在这个限制,但我想这与以下事实有关 Delphi 的 db-aware 控件最初是为 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 添加一个插入器 class 来工作
删除 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 set-up 有问题但是代码应该 与 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;