Совокупность нескольких элементов управления пользовательского интерфейса, привязанных к одному столбцу базы данных - PullRequest
2 голосов
/ 04 апреля 2020

В настоящее время у меня есть TDBRadioGroup, привязанная к столбцу CHAR в базе данных, используя свойство Values, чтобы указать значение, хранящееся в столбце базы данных для каждой радиокнопки. Теперь у нас есть требование добавить 2 новые группы радиосвязи рядом с существующей, которая изменила бы значение. Другими словами, мне нужно 3 элемента управления в форме, чтобы они действовали как один в конфигурации, учитывающей данные, для определения сохраненного значения. Простым примером может быть, если каждая радиогруппа имеет односимвольное значение и в базе данных вы объединяете их в трехсимвольную строку. Наше картирование более сложное, чем это, но это общая идея. Что было бы хорошим способом сделать это? Если кто-то скажет мне, что привязки в реальном времени - это путь к go, это было бы полезно узнать, так как это помогло бы мне убедить руководство обновить Delphi. Мы застряли на XE.

1 Ответ

4 голосов
/ 04 апреля 2020

Интересно д!

Надеюсь, я вас правильно понял. Предположим, что существует форма с 3 радиогруппами, по одной на линию, например:

* AB C
D * EF
GH * I

где звездочки указывают положения выбранных кнопок и предполагают, что эти настройки преобразуются в значение поля AEI в строке набора данных. Если щелкнуть G-запись 3-го RG, значение поля набора данных должно стать GEI. Это то, что, как я полагаю, вы ищете.

Приведенный ниже код является «доказательством концепции», реализующей вышеуказанную функциональность.

Для этого используются 3 стандарта (не-db- в курсе) TRadioGroups и класс адаптера TDBRadioGroupAdaptor, который заставляет их работать как db-осведомленные радиогруппы. TDBRadioGroupAdaptor использует потомок, TRGFieldDataLink, стандартного TFieldDataLink (см. DBCtrls.Pas) для взаимодействия с набором данных. Я использовал TClientDataSet, чтобы проект мог быть полностью автономным, и я избегал использования обобщений, поскольку вы не указали Delphi версию.

Большая часть функциональности, связанной с db, содержится в TRGFieldDataLink, который служит для взаимодействия набора данных и RadioGroups и отличается от стандартного TFieldDataLink тем, что обрабатывает значение поля как состоящее из N (3 в примере кода, но поддерживает произвольное число) подполей, каждое из которых имеет их собственная радиогруппа. Как и в случае с реализацией функциональности с поддержкой db, код для TDBRadioGroupAdaptor и TRGFieldDataLink довольно скучный, так как большая его часть необходима, но не очень интересна. О единственных, которые интересны тем, что они делают колеса go вокруг, являются

function TRGFieldDataLink.GetGroupString : String;

// Returns a string from the ItemIndexes of the RadioGroups

procedure TRGFieldDataLink.GetGroupValues;

//  Sets the DataSet field from the RadioGroup ItemIndexes

, которые, я надеюсь, говорят сами за себя из встроенных комментариев.

Поскольку это только Предполагается, что это доказательство концепции, реализация неполная, без поддержки fi-клавиатуры, но ее можно легко добавить, имитируя исходный код стандартной TDBRadioGroup.

Очевидно, что нечто подобное можно использовать для обрабатывать подполя поля набора данных как группу строк, независимо редактируемых в группе TEdits.

Также очевидно, что было бы возможно превратить TDBRadioGroupAdaptor в полноценный составной компонент, который включает в себя собственные радиогруппы, но Я оставил это как упражнение для читателя.

Код (предупреждение: долго!)

type

  TDBRadioGroupAdaptor = class;

  TRGFieldDataLink = class(TFieldDataLink)
  private
    FAdaptor: TDBRadioGroupAdaptor;
    FRecordChanging : Boolean;
    procedure GetGroupValues;
    function GetGroupString: String;
    procedure SetGroupValues(AValue: String);
  public
    constructor Create(AAdaptor : TDBRadioGroupAdaptor);
    destructor Destroy; override;
    procedure RecordChanged(Field : TField); override;
    procedure UpdateData; override;
    property Adaptor : TDBRadioGroupAdaptor read FAdaptor write FAdaptor;
  end;

  TDBRadioGroupAdaptor = class
  private
    FDataLink : TRGFieldDataLink;
    FRadioGroups : TList;
    procedure SetDataSource(Value : TDataSource);
    function GetDataSource : TDataSource;
    function GetRadioGroup(Index: Integer): TRadioGroup;
    procedure ItemClicked(Sender : TObject);
    procedure SetFieldName(const Value: string);
    function GetFieldName : String;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Add(ARadioGroup : TRadioGroup);
    property DataSource : TDataSource read GetDataSource write SetDataSource;
    property FieldName : string read GetFieldName write SetFieldName;
    property RadioGroup[Index : Integer] : TRadioGroup read GetRadioGroup;
  end;

type
  TForm1 = class(TForm)
    DBGrid1: TDBGrid;
    ClientDataSet1: TClientDataSet;
    DataSource1: TDataSource;
    DBNavigator1: TDBNavigator;
    Button1: TButton;
    RadioGroup1: TRadioGroup;
    RadioGroup2: TRadioGroup;
    RadioGroup3: TRadioGroup;
    DBEdit1: TDBEdit;
    DBRadioGroup1: TDBRadioGroup;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
  protected
  public
    Adaptor : TDBRadioGroupAdaptor;
  end;

[...]

{ TRGFieldDataLink }

constructor TRGFieldDataLink.Create(AAdaptor : TDBRadioGroupAdaptor);
begin
  inherited Create;
  Adaptor := AAdaptor;
end;

destructor TRGFieldDataLink.Destroy;
begin
  inherited;
end;

procedure TRGFieldDataLink.SetGroupValues(AValue : String);

//  Sets the ItemIndexes of the RadioGroups by matching each character of AValue
//  to the contents of their Items

var
  i,
  Index : Integer;
  S : String;
begin
  if AValue = '' then Exit; //  To avoid error when CreateDataSet is called
  for i := 0 to Adaptor.FRadioGroups.Count - 1 do begin
    S := AValue[i + 1];
    Index := Adaptor.RadioGroup[i].Items.IndexOf(S);
    Adaptor.RadioGroup[i].ItemIndex := Index;
  end;
end;

procedure TRGFieldDataLink.RecordChanged(Field : TField);

//  called when the dataset goes to a new record, e.g. during scrolling
//  and sets the RadioGroups' ItemIndexes from the dataset data
var
  FieldValue : String;
begin

  Assert(DataSet <> Nil);

  if FRecordChanging then exit; // just in case, avoid re-entrancy

  try
    FRecordChanging := True;
    if Field = Nil then
      Field := DataSet.FieldByName(FieldName);  //  Yukky way of setting Field
    FieldValue :=  Field.AsString;
    SetGroupValues(FieldValue);
  finally
    FRecordChanging := False;
  end;
end;

function TRGFieldDataLink.GetGroupString : String;

// Returns a string from the ItemIndexes of the RadioGroups

var
  i : Integer;
  S : String;
begin
  Result := '';
  for i := 0 to Adaptor.FRadioGroups.Count - 1 do begin
    S := Adaptor.RadioGroup[i].Items[Adaptor.RadioGroup[i].ItemIndex];
    Result := Result +  S [1];
  end;
end;

procedure TRGFieldDataLink.GetGroupValues;

//  Sets the DataSet field from the RadioGroup ItemIndexes

var
  FieldValue,
  S : String;
begin
  Assert(DataSet <> Nil);

  S := Field.AsString;
  FieldValue := GetGroupString;

  Field.AsString := FieldValue;
end;

procedure TRGFieldDataLink.UpdateData;

//  Called by RTL to update the dataset record from the RadioGroups

begin
  GetGroupValues;
end;

{ TDBRadioGroupAdaptor }

procedure TDBRadioGroupAdaptor.Add(ARadioGroup: TRadioGroup);
begin
 FRadioGroups.Add(ARadioGroup);
 ARadioGroup.OnClick := ItemClicked;
end;

constructor TDBRadioGroupAdaptor.Create;
begin
  inherited;
  FRadioGroups := TList.Create;
  FDataLink := TRGFieldDataLink.Create(Self);
end;

destructor TDBRadioGroupAdaptor.Destroy;
begin
  FDataLink.Free;
  FRadioGroups.Free;
  inherited Destroy;
end;

function TDBRadioGroupAdaptor.GetDataSource: TDataSource;
begin
  Result := FDataLink.DataSource;
end;

procedure TDBRadioGroupAdaptor.SetDataSource(Value: TDataSource);
begin
  FDataLink.DataSource := Value;
end;

function TDBRadioGroupAdaptor.GetRadioGroup(Index: Integer): TRadioGroup;
begin
  Result := TRadioGroup(FRadioGroups[Index]);
end;

procedure TDBRadioGroupAdaptor.ItemClicked(Sender: TObject);

//  Responds to one of the RadioGroups being clicked to put the DataSet into Edit state
//  and updates the DataSet field from the ItemIndexes of the RadioGroups

var
  S : String;
begin
  if not FDataLink.FRecordChanging then begin
    S := FDataLink.GetGroupString;
    FDataLink.Edit;
    FDataLink.SetGroupValues(S);
  end;
end;

procedure TDBRadioGroupAdaptor.SetFieldName(const Value: string);
begin
  FDataLink.FieldName := Value;
end;

function TDBRadioGroupAdaptor.GetFieldName: string;
begin
  Result := FDataLink.FieldName;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  Field : TField;
begin

  //  Create 2 fields in the CDS

  Field := TIntegerField.Create(Self);
  Field.FieldName := 'ID';
  Field.FieldKind := fkData;
  Field.DataSet := ClientDataSet1;

  Field := TStringField.Create(Self);
  Field.FieldName := 'Value';
  Field.Size := 40;
  Field.FieldKind := fkData;
  Field.DataSet := ClientDataSet1;


  RadioGroup1.Items.Add('A');
  RadioGroup1.Items.Add('B');
  RadioGroup1.Items.Add('C');

  RadioGroup2.Items.Add('D');
  RadioGroup2.Items.Add('E');
  RadioGroup2.Items.Add('F');

  RadioGroup3.Items.Add('G');
  RadioGroup3.Items.Add('H');
  RadioGroup3.Items.Add('I');

  Adaptor := TDBRadioGroupAdaptor.Create;
  Adaptor.Add(RadioGroup1);
  Adaptor.Add(RadioGroup2);
  Adaptor.Add(RadioGroup3);
  Adaptor.DataSource := DataSource1;
  Adaptor.FieldName := 'Value';

  //  Next, set up the CDS
  ClientDataSet1.CreateDataSet;

  ClientDataSet1.InsertRecord([1, 'AEI']);
  ClientDataSet1.InsertRecord([2, 'BDG']);
  ClientDataSet1.InsertRecord([3, 'ADG']);

end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  Adaptor.Free;
end;
...