Как я могу использовать несколько TcxDBTextEdit с отформатированными данными, хранящимися в одном поле БД? - PullRequest
0 голосов
/ 28 июня 2019

Мне нужно хранить два значения в одном поле базы данных (да, я согласен, что это плохая практика, но это устаревшая база данных, которую нельзя изменить). Данные хранятся как string1#4string2.

Данные необходимо редактировать с помощью двух отдельных TcxDBTextEdit элементов управления. Но как я могу подключить их к одному полю базы данных, чтобы я мог редактировать string1 в одном и string2 в другом?

Я пытался добавить два вычисляемых (fkCalculated) поля в TADOQuery, извлекать / объединять их значения в OnGetText / OnSetText и читать / записывать в TStringField, но это не помогло работа.

Итак, я попытался создать компонент TdxMemData с двумя полями и использовать их вместо вычисляемых полей, но он все еще не работает.

Как мне этого добиться (без изменения структуры базы данных)?

1 Ответ

1 голос
/ 28 июня 2019

Пример проекта ниже делает то, что вам нужно.

Обновление Приведенный ниже код заменяет код, который я первоначально разместил, и избегает использования типа набора данных (TClientDataSet), который поддерживает fkInternalCalcполя.Он будет работать с TAdoQuery.

Хотя в принципе нет ничего сложного в разборе строкового поля на два подполя и наложении их в графическом интерфейсе для редактирования, проблема заключается в простых способах выполнения.это с TAdoQuery заключается в том, что он поддерживает только вычисленные поля fkCalculated, а элементы управления графическим интерфейсом с учетом db обрабатывают их как не изменяемые пользователем.

Я не уверен, почему существует это ограничение, но я предполагаю, что оно связанок тому факту, что элементы управления Delphi с поддержкой db изначально были разработаны для 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, которые не являются ReadOnlyи принадлежат к набору данных, который можно изменить (хотя это последнее ограничение может быть снято, я думаю).Средство вставки 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;
...