【问题标题】:Aggregate of multiple UI controls bound to one database column绑定到一个数据库列的多个 UI 控件的聚合
【发布时间】:2020-04-03 21:21:54
【问题描述】:

我目前有一个 TDBRadioGroup 绑定到数据库上的一个 CHAR 列,使用 Values 属性为每个单选按钮指定存储在数据库列中的值。现在我们需要在现有的无线电组旁边添加 2 个新的无线电组来修改值。换句话说,为了确定存储的值,我基本上需要表单上的 3 个控件作为数据感知配置中的一个。一个简单的示例可能是,如果每个单选组都有一个单字符值,并且在数据库中将它们连接成一个 3 字符的字符串。我们的映射比这更复杂,但这是一般的想法。什么是这样做的好方法?如果有人告诉我实时绑定将是可行的方法,那将是很高兴知道,因为这将帮助我说服管理层升级 Delphi。我们被困在 XE 上。

【问题讨论】:

  • 有 3rd 方库支持独立编辑 IP4 点分隔地址的数字之类的东西,但我个人不知道任何支持无线电组的东西,如您所描述的:我不知道不要认为基于自定义 TFieldDataLink 派生来滚动您自己的自定义组件以将数据与 3 个无线电组连接起来太难了。

标签: database user-interface delphi delphi-xe


【解决方案1】:

有趣的问题!

我希望我正确地理解了你。假设有一个包含 3 个无线电组的表单,每行一个,像这样

*A B C
D *E F
GH *I

星号表示所选按钮的位置,假设 这些设置转换为数据集中一行中的字段值 AEI。 如果点击第 3 个 RG 的 G 条目,则数据集字段值应变为 盖伊。这就是我假设您正在寻找的东西。

下面的代码是实现上述功能的“概念证明”。

它使用 3 个标准(非 db-aware)TRAdioGroups 和一个适配器类 TDBRadioGroupAdaptor 使这些作为 db-aware RadioGroups 运行。这 TDBRadioGroupAdaptor 使用标准 TFieldDataLink 的后代 TRGFieldDataLink(参见 DBCtrls.Pas) 与数据集交互。我使用了一个 TClientDataSet 所以项目可以完全 自包含,我避免使用泛型,因为您没有指定 Delphi 版本。

大多数 db 感知功能都包含在 TRGFieldDataLink 中,它 用于连接数据集和 RadioGroup,与标准不同 TFieldDataLink 将字段值视为由 N 组成(代码示例中为 3,但它支持任意数字) 每个子字段都有自己的 RadioGroup。与实施 db-aware 一样 功能,TDBRadioGroupAdaptor 和 TRGFieldDataLink 的代码相当 啰嗦,因为大部分是必要的,但不是很有趣。关于唯一 有趣的是,它们使轮子四处转动,是

function TRGFieldDataLink.GetGroupString : String;

// Returns a string from the ItemIndexes of the RadioGroups

procedure TRGFieldDataLink.GetGroupValues;

//  Sets the DataSet field from the RadioGroup ItemIndexes

我希望嵌入的 cmets 是不言自明的。

因为它只是一个概念验证,所以实现是不完整的, 缺乏 f.i.键盘支持,但可以通过模仿 标准 TDBRadioGroup 的源代码。

显然,可以使用类似的方法将数据集字段的子字段视为 在一组 TEdit 中可独立编辑的一组字符串。

同样显然,可以将 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;

【讨论】:

  • 那么,这对您有帮助吗?
  • 这个解决方案看起来很棒,但我们没想到相对于用户的利益而言,它付出了这么多努力。我们决定采用更简单的 UI 设计。
  • 好吧,自定义组件通常涉及大量编码(在这种情况下为 3 小时)。如果你能接受这个答案会很好,这样未来的读者就会知道它对你的 q 的回答令你满意。
  • 我认为接受这个答案是不合适的,因为我实际上并没有运行代码来验证它是否有效。并不是说我怀疑它会。看起来不错,很有道理,
猜你喜欢
  • 1970-01-01
  • 2020-03-21
  • 2011-03-08
  • 1970-01-01
  • 2012-03-11
  • 2010-10-16
  • 1970-01-01
  • 2011-07-02
  • 2020-02-04
相关资源
最近更新 更多