更新 cxGrid 中的字段表现得很奇怪

Updating field in cxGrid acting strange

我有一个功能可以更新在

的答案的帮助下制作的 cxGrid

但它有时表现得有点奇怪。如果我使用 cxGrid 打开表单并单击列标题而不执行任何其他操作,记录将更新为正常。但是如果 'selectorbar' 从顶部移开,则标记的记录不会更新。 我确定这是一个需要更改的属性,但是哪个

变量 fSelected 在 FormShow 中设置为 False,这样用户也可以取消选择记录。

procedure TfrmContactsSelect.colContactSelectedHeaderClick(Sender: TObject);
var
  i: Integer;
  Index: Integer;
  BookMark : TBookMark;
  Contact: variant;
begin
  if fMulti = True then
    begin
     Screen.Cursor := crHourGlass;
     fSelected := not fSelected;
      BookMark := qryContacts.GetBookmark;
      qryContacts.DisableControls;
      try
        for i := 0 to grdContactsView1.DataController.FilteredRecordCount - 1 do
          begin
            Index := grdContactsView1.DataController.FilteredRecordIndex[i];
            Contact := grdContactsView1.DataController.Values[Index, 4];
            if grdContactsView1.DataController.LocateByKey(Contact) then
              begin
                qryContacts.Edit;
                qryContacts.FieldByName('fldcontact_selected').AsBoolean := fSelected;
                qryContacts.Post;
              end;
          end;
      finally
        qryContacts.EnableControls;
        qryContacts.GotoBookmark(BookMark);
        qryContacts.FreeBookmark(BookMark);
      end;
      Screen.Cursor := crDefault;
    end;
end;

Delphi XE7、DevExpress 14.2.2、用于数据库访问的 UniDAC 5.5.12

评论: 根据 MartynA

的回答和输入,我最终得到了以下解决方案
procedure TfrmContactsSelect.colContactSelectedHeaderClick(Sender: TObject);
var
  i: Integer;
  Index: Integer;
  MarkedRecord: variant;
  CurrentRecord: variant;
begin
  if fMulti = True then
    begin
      Screen.Cursor := crHourGlass;
      fSelected := not fSelected;

      Index := grdContactsView1.DataController.FocusedRecordIndex;
      MarkedRecord := grdContactsView1.DataController.Values[Index, colContactGuid.ID];

      try
        for i := 0 to grdContactsView1.DataController.FilteredRecordCount - 1 do
          begin
            Index := grdContactsView1.DataController.FilteredRecordIndex[i];
            CurrentRecord := grdContactsView1.DataController.Values[Index, colContactGuid.ID];
            if grdContactsView1.DataController.LocateByKey(CurrentRecord) then
              begin
                grdContactsView1.DataController.Edit;
                grdContactsView1.DataController.SetEditValue(colContactSelected.ID, fSelected, evsText);
                grdContactsView1.DataController.Post;
              end;
          end;
      finally
        grdContactsView1.DataController.LocateByKey(MarkedRecord);
      end;

      Screen.Cursor := crDefault;
    end;
end;

我可以使用我在对您的其他问题的回答中发布的示例项目来重现您的问题。

试试这个:将 TMemo 添加到您的表单,并在 'if grdContactsView1.DataController.LocateByKey(Contact) then' 块内,将行唯一数据字段的值和选定数据字段的值写入备忘录。

Then, what I get when some row other than the top row is selected is that one row is listed twice in the memo, with Selected both false and true, and one of the rows in the filter isn't listed根本没有,我认为这可以解释您所看到的行为。如果我随后注释掉 .Edit .. .Post 行,它会正确列出过滤器中的所有行。

很明显,在迭代 DBTableView 的 FilteredRecordIndex 属性 的块内更改 Selected 字段是导致问题的原因。

就我个人而言,我发现通过 DB-aware 控件修改代码中的数据集行有点违背常理(因为您通常最终会与控件的 DB-awareness 作斗争),但在这种情况下,通过 cxGrid 的 DBTableView 进行处理很简单。

procedure TForm1.ProcessFilteredRecords;
var
  PrevV,
  V : Variant;
  i,
  Index: Integer;
  S : String;
begin

  //  First, pick up a reference to the current record
  //  so that we can return to it afterwards
  Index := cxGrid1DBTableView1.DataController.FocusedRecordIndex;
  PrevV := cxGrid1DBTableView1.DataController.Values[Index, 0];

  try
    for i := 0 to cxGrid1DBTableView1.DataController.FilteredRecordCount - 1 do begin
      Index := cxGrid1DBTableView1.DataController.FilteredRecordIndex[i];
      V := cxGrid1DBTableView1.DataController.Values[Index, 0];
      if cxGrid1DBTableView1.DataController.LocateByKey(V) then begin
        cxGrid1DBTableView1.DataController.Edit;
        // 2 is the index of my Selected column in the grid
        if cxGrid1DBTableView1.DataController.SetEditValue(2, True, evsText) then
          Caption := 'OK'
        else
          Caption := 'Failed';
        cxGrid1DBTableView1.DataController.Post;
      end;
    end;
  finally
    if cxGrid1DBTableView1.DataController.LocateByKey(PrevV) then
      Caption := 'OK'
    else
      Caption := 'Failed';
  end;
end;

另一种避免该问题的方法是分两步更改选定状态:

  • 迭代 FilteredRecordIndex 以构建要更改的行列表 - 在您的情况下,这将是一个 guids

  • 列表
  • 然后,迭代行列表并更新它们的选定状态。

代码:

procedure TForm1.ProcessFilteredRecords;
var
  V : Variant;
  i,
  Index: Integer;
  BM : TBookMark;
  S : String;
  TL : TStringList;
begin
  Memo1.Lines.Clear;
  TL := TStringList.Create;
  try
    for i := 0 to cxGrid1DBTableView1.DataController.FilteredRecordCount - 1 do begin
      Index := cxGrid1DBTableView1.DataController.FilteredRecordIndex[i];
      V := cxGrid1DBTableView1.DataController.Values[Index, 0];

      if cxGrid1DBTableView1.DataController.LocateByKey(V) then begin
        if CDS1.FieldByName('Selected').AsBoolean then
          S := 'True'
        else
          S := 'False';
        S := CDS1.FieldByName('Name').AsString + ' ' + S;
        Memo1.Lines.Add(S);
        TL.Add(CDS1.FieldByName('Guid').AsString);
      end;
    end;

    try
      BM := CDS1.GetBookMark;
      CDS1.DisableControls;
      for i := 0 to TL.Count - 1 do begin
        if CDS1.Locate('guid', TL[i], []) then begin
          CDS1.Edit;
          CDS1.FieldByName('Selected').AsBoolean := True;
          CDS1.Post;
        end
      end;
    finally
      CDS1.EnableControls;
      CDS1.GotoBookmark(BM);
      CDS1.FreeBookmark(BM);
    end;
  finally
    TL.Free;
  end;
end;

像你一样,我期待改变一个 属性 或两个 cxGrid 可能无需任何代码就可以避免这个问题,但我还没有找到任何可以解决的问题。