DBGrid停止当前行移动

时间:2014-08-24 16:58:56

标签: delphi delphi-5 dbgrid

使用d5,TDBGrid,SQLite3和ZEOS。数据库有2000个项目,一个列是“活动”作为布尔值,第二个列是“ItemName”作为文本,而IndexFiledNames是“ItemName”

OnDblclick切换“活动”开/关,所有工作都按预期进行数据。主动从True变为False再返回。

但是,如果我双击DBGrid的最后一个可见行,要切换活动状态 - 在切换之后,DBGrid将该项目行移动到网格的垂直中心行位置。这对于他们只是双击跳过的行的用户来说非常混乱。

如何阻止网格将该行移动到中间?对于DGBGrid的最后可见行上的所有项目都会发生这种情况。

{编辑}已撤销的项目是尝试减少问题 - 无效。

procedure TfrmMain.dbgridItemsDblClick(Sender: TObject);
begin
  puItemsSelectedClick(Self);
end;

procedure TfrmMain.puItemsSelectedClick(Sender: TObject);
//var
//  CurrItem : String;
//  CurrIndx : String;
begin
  if dm.tblItems.RecordCount = 0 then
  begin
    myShowMsg('There are no Items in the Items List');
    Exit;
  end;
//  CurrItem:=dm.tblItems.FieldByName(fldItemGroupShop).AsString;
//  CurrIndx:=dm.tblItems.IndexFieldNames;
  dm.tblItems.DisableControls;
  try
//    dm.tblItems.IndexFieldNames:='';
    dm.tblItems.Edit;
    dm.tblItems.FieldByName(fldSelected).AsBoolean:=
      not(dm.tblItems.FieldByName(fldSelected).AsBoolean);
    dm.tblItems.Post;
//    dm.tblItems.IndexFieldNames:=CurrIndx;
//    dm.tblItems.Locate(fldItemGroupShop,CurrItem,[]);
  finally
    dm.tblItems.EnableControls;
  end;
end;

2 个答案:

答案 0 :(得分:0)

DBGrid的当前行号和显示行数是受保护的属性, 所以你需要在你的代码中使用“类cracker”类型声明,如下所示:

type
  TMyDBGrid = Class(TDBGrid);

function TForm1.GetGridRow: Integer;
begin
  Result := TmyDBGrid(DBGrid1).Row;
end;

function TForm1.GridRowCount : Integer;
begin
  Result := TmyDBGrid(DBGrid1).RowCount;
end;

完成此操作后,在表单上放置一个TEdit和TButton,以输入一个小于当前行的新网格行号。然后尝试以下例程:

procedure TForm1.SetGridRow(NewRow : Integer);
var
  GridRows,
  OldRow,
  MoveDataSetBy,
  MovedBy : Integer;
  DataSet : TDataSet;
  Possible : Boolean;
  ScrollUp : Boolean;
begin
  OldRow := GetGridRow;
  if NewRow = OldRow then
    Exit;

  ScrollUp := NewRow < OldRow;

  DataSet := dBGrid1.DataSource.DataSet;

  GridRows := TmyDBGrid(DBGrid1).RowCount;
  { TODO : Test the case where the DataSet doesn't have enough rows to fill the grid}
  { TODO : Check why grid reports one more row than it displays.
   Meanwhile ... }
  GridRows := GridRows - 1;

  // First check whether the NewRow value is sensible
  Possible := (NewRow >= 1) and (NewRow <= GridRows);
  if not Possible then exit;

  try
    if ScrollUp then begin

    //  First scroll the dataset forwards enough to bring
    //  a number of new records into view
    MoveDataSetBy := GridRows - NewRow;
    MovedBy := DataSet.MoveBy(MoveDataSetBy);
    Shortfall := MoveDataSetBy - MovedBy;
    if Shortfall = 0 then begin
      //  Now scroll the dataset backwards to get back
      //  to the record we were on
      MoveDataSetBy := -GridRows + NewRow;
      MovedBy := DataSet.MoveBy(MoveDataSetBy);
    end
    else
      MovedBy := DataSet.MoveBy(-MovedBy);
  end
  else begin
    MoveDataSetBy :=  -(NewRow - 1);
    MovedBy := DataSet.MoveBy(MoveDataSetBy);
    //  We need to know if the DS cursor was able to move far enough
    //  back as we've asked or was prevented by reaching BOF
    Shortfall := MoveDataSetBy - MovedBy;
    if Shortfall = 0 then begin
      // The DS cursor succeeded on moving the requested distance
      MoveDataSetBy := NewRow - 1;
      MovedBy := DataSet.MoveBy(MoveDataSetBy);
    end
    else
      //  it failed, so we need to return to the record we started on
      //  but this won't necessarily return us the same grid row number
      MovedBy := DataSet.MoveBy(-MovedBy);
    finally
      DBGrid1.Invalidate;
    end;

我之前的建议是,通过“TmyDBGrid(DBGrid1).Row:= NewRow;”直接分配到网格行。是基于错误的回忆,因为事实上似乎没有什么用处。

“if ScrollUp”之后的算法由于我们不依赖于有意义的RecNo而变得复杂。这涉及到检查数据集光标是否可以在我们想要移动网格行的方向相反的方向上移动足够的数量,以相对于网格中的行滚动DS光标,没有击中EOF或BOF - 如果发生任何一个,我们只需将DS光标移回原位,放弃尝试滚动网格。

对于ScrollUp,逻辑是:

  • 首先将数据集光标移动到网格中的最后一行
  • 然后通过新旧Row值之间的差异向前移动一些。
  • 然后向后移动一个等于网格中行数减去新行值的数量。

如果所有成功,则当前行将移动到NewRow值请求的网格位置。 当然,代码结合了前两个步骤。起初,我认为这段代码是无意义的,因为用于DataSet.MoveBy()的值的代数和为零。事实上, 这不是废话,只是有点反直觉。当然距离加起来为零,因为我们想要回到我们的记录;完成DataSet.MoveBy()的目的就是摇动当前记录中的网格,然后返回到它。顺便说一句,为什么在移动当前记录然后返回到它时通常没有任何意义,即DataSet.GetBookmark / GotBookmark / FreeBookmark,实际上使用它们会破坏代码的预期效果。

我正在使用ClientDataSet,顺便说一句,而不是ZEOS,但这应该没有任何区别。

顺便说一句,本地DataSet变量是访问网格的数据集而不使用Delphi的地狱“With ...”构造。

顺便提一下,你对“Rows div 2”的评论提醒我:我不认为这是告诉数据集的网格,ISTR是与网格关联的数据链路,它告诉数据集它应该分配缓冲区的记录数。然后,在TDataSet.Resync中,您会注意到

if rmCenter in Mode then
  Count := (FBufferCount - 1) div 2 else
  Count := FActiveRecord;

然后看一下如何在日常工作中使用Count;你的理论可能会被发现。也许在“if cmCenter in Mode”中设置一个断点,看看它是否从你的网格运行起来调用。

Btw#2,即使此代码没有帮助,本文可能会http://delphi.about.com/od/usedbvcl/l/aa011004a.htm

答案 1 :(得分:0)

我不确定我的处境是否与您一样,但是如果您要解决烦人的网格居中(例如,如果您对用户单击做出反应,并且需要先到达记录或在下面记录,然后正确地返回) ,使用它:

var oldActiverecord:=FDataLink.ActiveRecord;  
DataSet.DisableControls;
oldrecno:=Dataset.RecNo;//remember current recno
Dataset.RecNo:=SomeAnotherRecNoWhichYouNeedToGoTo;
//do what you like with this record
...
//then return to current
Dataset.RecNo:=oldrecno;//in this moment grid will center
var MoveDataSetBy:=oldActiverecord-FDataLink.ActiveRecord;//how much?
if MoveDataSetBy<>0 then begin
  DataSet.MoveBy(-MoveDataSetBy); //get back
  DataSet.Resync([rmCenter]); //center there
  DataSet.MoveBy(+MoveDataSetBy);//move cursor where we was initially
end;
DataSet.EnableControls;