0

我有一个函数来更新在答案的帮助下制作的 cxGrid循环遍历 cxgrid 上的记录并更新字段/列

但它有时表现得有点奇怪。如果我使用 cxGrid 打开表单并单击列标题而不执行任何其他操作,则记录会更新好。但是如果“选择器栏”从顶部移开,则标记的记录不会更新。我确信这是一个需要更改的属性,但是是哪一个。

变量 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;
4

1 回答 1

1

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

试试这个:在表单中添加一个 TMemo,然后在“if grdContactsView1.DataController.LocateByKey(Contact) then”块中,将行唯一数据字段的值和 Selected 数据字段的值写入备忘录。

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 at all, which I想想你所看到的行为。如果我然后注释掉 .Edit .. .Post 行,它会正确列出过滤器中的所有行。

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

就我个人而言,我发现通过 DB 感知控件修改代码中的数据集行有点违背原则(因为您通常最终会与控件的 DB 感知作斗争),但在这种情况下,这样做很简单通过 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;

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

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

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

代码:

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 的一个或两个属性可能会在没有任何代码的情况下避免该问题,但我无法找到任何可以解决的问题。

于 2015-06-15T10:01:34.043 回答