【问题标题】:DBGrid stop current Row movingDBGrid 停止当前行移动
【发布时间】:2014-08-24 16:58:55
【问题描述】:

使用 d5、TDBGrid、SQLite3 和 ZEOS。数据库有 2000 个项目,一个 Column 是“Active”作为 Boolean,第二个 Column 是“ItemName”作为 Text,IndexFiledNames 是“ItemName”

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

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

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

{EDIT} 已删除的项目是试图减少问题 - 没有奏效。

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;

【问题讨论】:

  • IIRC,认为这样做的原因是 TDBGrid 有一个内置的偏差(在它的编码方式上)试图将当前行保持在网格的垂直中心附近。保存对当前行的更改时,会触发数据集通知控件其光标已滚动,这会提示网格尝试将行重新居中,这就是它从底部移开的原因。但是,话虽如此,我刚刚尝试了您所描述的内容,并且该行并没有从底部移开,所以您能否将 DBGrid1DblClick 中的代码添加到您的 q 中。
  • @MartynA 谢谢马丁,但我认为那里的一切都很无害。 :) 当你的不动时,你在使用 D5 吗?它可能是早期版本的“功能”。我有企业版,所以我有源代码,但不知道从哪里开始寻找停止行为。我也尝试了几个 dbgrid-Components,但它们似乎都基于 TDBGrid 并且都做同样的事情。
  • 有什么方法可以保存当前的可见行号或位置,然后在编辑后强制 DBGrid 重新绘制它吗?
  • 使用 D7,但如果网格的行为与 D5 不同,我会感到惊讶。是的,有一种强制行值的方法,我正在研究它,但它不仅仅是设置一个新值。如果可以的话,我应该发布我目前得到的答案并在以后填写吗?
  • 右侧第一个“相关”的相关/重复(Delphi - 恢复 DBGrid 中的实际行)

标签: delphi delphi-5 dbgrid


【解决方案1】:

DBGrid 的当前行数和显示行数是受保护的属性, 所以你的代码中需要一个“类破解者”类型声明,如下所示:

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()s 的值的代数和为零。实际上, 这不是废话,只是有点反直觉。当然,距离加起来为零,因为我们想回到我们是一的记录;执行 DataSet.MoveBy()s 的目的是松开网格对当前记录的控制,然后返回它。这就是为什么在移出当前记录然后返回到它时,我通常做的事情毫无意义,即 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 的;你的理论可能是正确的。也许在“如果 cmCenter 处于模式”上放置一个断点,看看它是否从您的网格起作用的位置被调用。

顺便说一句#2,即使这段代码没有帮助,这篇文章也可能http://delphi.about.com/od/usedbvcl/l/aa011004a.htm

【讨论】:

  • 谢谢,我会搞砸的。这有点超出我的知识领域,但反复试验总是对我有好处。我期待您可以添加的任何内容。如果您发布更多内容,我将推迟“答案”。谢谢。
  • 靠我自己一事无成。 :)似乎唯一的时间才是选定行不移动的时间是在可见行中间的1或2行内。上下都有。几乎其他任何地方都将最后一行移回中心。没有可靠的 RecNo,除非我在不同时间都需要没有索引和/或没有过滤。 :( 我在网上发现了一些其他帖子同样的痛苦,但还没有任何效果。如果我取消代码中的内容,它会变得更好,但仍然不是 100% 可靠。谢谢。
  • 我确实有一个 AutoInc。如果我可以为 VisibleRow 1 保存 AutoInc,在编辑之后是否有一些方法可以强制它从 VisibleRow 1 处保存的 AutoInc 开始重新绘制数据集?使用禁用/尝试/启用应该没有可见的移动。即使在过滤或索引时,它也应该在 VisibleRow 1 处保持相同的 AutoInc,因为我只是在更改记录中字段的状态。用户无法在选定列上建立索引,因此不应更改项目序列。不知道该怎么做,但只是遛狗和思考时间。 :)
  • 谢谢,我会试试的。我很确定它不是 Zeos,因为我现在已经使用 Interbase 和 BDE 进行了尝试。我也尝试了几个第 3 方 DBGrid 组件,都一样,可能是因为它们基于 TDBGrid。我什至只使用 BDE 和股票 TDBgrid 精简到一个新的应用程序,它做到了。似乎是 D5 Borland 组件的一个特性。我试过在 XP、win7、win8 和 win8.1 上运行都一样。我查看了 TDBGrid 源中的“Rows div 2”等,看看是否能找到任何将焦点定位到中间行的东西。没有。感谢您的帮助。
  • 我刚刚在 D5 中尝试了我的项目,但无法重现您的问题。如果您可以在项目中使用 TClientDataSet,请将副本放在 pastebin.com 上并发布链接。哦,顺便说一句,我已经删除了我的大部分 cmets,因为我之前参与了另一组移动聊天。
【解决方案2】:

我不确定我的情况是否像您的情况,但是如果您想修复烦人的网格居中问题(例如,如果您对用户点击做出反应并且需要先到达记录或记录在下面然后正确返回) ,使用这个:

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;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-04
    • 1970-01-01
    相关资源
    最近更新 更多