研发在 DBGrid 上完美地显示序号的方法(11分)

  • 主题发起人 主题发起人 kinneng
  • 开始时间 开始时间
K

kinneng

Unregistered / Unconfirmed
GUEST, unregistred user!
研发在 DBGrid 上完美地显示序号的方法

这个真是一个难题,虽然网上也有很多写明能够在 DBGrid 上显示序号的文章,结果但都是失
败的,老外的控件都没这功能,不要浪费时间找了。

1、使用 RecNo,但它是编号,不是序号,删数据之后,就不连续了,ADO 的 RecNo 例外。
2、使用 AutoInc 自动递增字段,同上删数据之后,也会不连续。
3、使用 CalcField 计算字段,做在控件里面,强加一个计算字段给用家。
4、增大用户数据集控件的缓冲器,将数据全部下载,变成内存表来计算序号,这是不
可取,为了避免影响用户的数据集控件,动态新建一个数据集控件来下载数据的方
法,就简直不知怎么形容。
5、用一个整数字段,每当增删除数据之后,历遍重新编一次号。
6、监视滚动条,但不是什么变化都触发滚动条事件。
7、改用 StringGrid 或者 ListView

1、2、6 是失败的。4、5 虽然可以取得正确序号,但消耗较大,效率较低。我认为只有 3 计算
字段这个方法消耗较少,而且正确,比较之下5的方法是很蠢的。7 在回避问题。

有些网友觉得显示序号很容易,就是用上面的其中一种,那别怪我不放在眼内,上面的 1~6 方
法有很多人不管出错没出错,效率如何就这么用了,也有商品软件用 7 方法,这些方法在论坛内
都有帖子和源代码,还炒来炒去。

要显示序号,先了解其难点,论坛的讨论认为主要是修改、插入或者添加数据的时候,RecNo
会变 -1,如果就这个简单,那就不是问题,问题是 RecNo 根本不能用。

那么冷静分析一下,除了 RecNo 外还有什么位置信息可以获得,表格本身有什么规律,规律
真是有的,例如:

1、数据总条数等于 0 的时候,表格第一行序号为 0,TopRowID:=0,既然它事实存在,就
不应该放过它,这条成立的话,Bof和Eof是什么状态,也不用管。

2、无论任何状态下,数据条数等于少于表格行数时,表格第一行序号恒为 1,其它的不管。
代码就是 if Dataset.RecordCount <= RowCount then TopRowID:=1;

3、Insert 插入数据状态下,行数增加,但表格序号不变,TopRowID 不变,不管它。

4、Append 添加数据状态下,先符合第2条,数据现在条数 +1 等于少于表格行数时,表格
第一行序号为 1,否则因为表格自动上滚一行,第一行序号要 +1。Inc(TopRowID);

5、光标垂直移动,翻页会触发滚动事件,滚动事件的 Distance 参数+第一行序号,就等于
滚动翻页后的新序号。TopRowID:=TopRowID+Distance;


数据集 First,Last 等操作提供 Bof 和 Eof 来定位。

6、Bof 等于真的时候,表格第一行序号恒为 1。
7、Eof 等于真的时候,表格第一行序号为数据总条数-表格的行数。

有些状态可以不管它

8、Edit 状态等不影响表格行数
9、Delete 之后,表格变化则会发生滚行,已经处理了。

有些确是难题,例如

10、RowMoved 行移动事件过程中会发生滚行,但由于 DBGrid 是继承 TCustomGrid 的,
而 TCustomGrid 彻底拦断了这个事件,所以无法正确计算序号,只好修改鼠标处理程
序,不让它发生滚行,没理由改 TCustomGrid 的,它会影响全世界的。

今晚先发掘到这里,有人顶再研究。
 
显示序号是个麻烦的事情。我一般不用,有非要用的必要吗?
 
关键是客户要用。
[:D]
 
看以前的帖子,温柔一刀大侠有两个方法,一个是写SQL语句,还有一个是.....忘了
 
写SQL方法,就算是第8个失败的方法吧,因为用户用Table控件,就失败了。
 
继承dbgrid,重写drawcell,按row来确定
 
继承drawcell
if (ACol <= 0) then
begin
Canvas.Brush.Color := FixedColor;
Canvas.FillRect(ARect);
if ARow>0 then
Canvas.TextRect(ARect, 1, ARect.Top + 5, IntToStr(Arow));
end else
inherited
//序号的列宽继承SetColumnAttributes,改写ColWidths[0]
 
Arow是表格的行号,不是数据的序号,滚动翻页之后,数字不会没跟着变,这个答案跟
我要研究的东西,相距甚远,完全不沾边。
 
也不是完全不沾边,只是忽视了滚动的问题。
var oldactive:Integer;
function gettoprow:Integer;
begin
oldactive:=DataLink.ActiveRecord;
try
DataLink.ActiveRecord:=1;
Result:=datalink.DataSet.RecNo;
finally
DataLink.ActiveRecord:=oldactive;
end;
end;
begin
if (ACol <= 0) then
begin
Canvas.Brush.Color := FixedColor;
Canvas.FillRect(ARect);

if ARow=1 then
Ftoprow:=gettoprow;
if ARow>0 then
Canvas.TextRect(ARect, 1, ARect.Top + 5, IntToStr(Arow+Ftoprow-2));
end else
inherited
end;
即可解决滚动问题,这只是很粗略的代码,一些小问题没没有处理。
 
修正了的代码
var oldactive,Ftoprow,i:Integer;
function gettoprow:Integer;
begin
oldactive:=DataLink.ActiveRecord;
try
DataLink.ActiveRecord:=0;
if datalink.DataSet.RecNo=-1 then
DataLink.ActiveRecord:=1;
Result:=datalink.DataSet.RecNo;
finally
DataLink.ActiveRecord:=oldactive;
end;
end;
begin
if (ACol <= 0) then
begin
Canvas.Brush.Color := FixedColor;
Canvas.FillRect(ARect);

Ftoprow:=gettoprow;

Ftoprow:=Ftoprow-1;
if ARow>0 then
Canvas.TextRect(ARect, 1, ARect.Top + 5, IntToStr(Arow+Ftoprow));
end else
inherited
end;
 
可这个方法是注定失败的,原因在顶贴的第1条关于RecNo
 
另外function gettoprow:Integer;不必这么写,
function gettoprow:Integer;
begin
Result:=datalink.DataSet.RecNo -DataLink.ActiveRecord;
end;
如何?
 
用sql server数据库是没问题的,不管是BDE还是ADO连接。如果是本地数据库,稍改一下就行
 
我们现在就是用RecNO实现的,也没什么问题呀。
 
每种连接组件返回的Recno都有点不一样,如果只是显示,可直接使用,但insert时效果就不一样了。
 
另外function gettoprow:Integer;不必这么写,
function gettoprow:Integer;
begin
Result:=datalink.DataSet.RecNo -DataLink.ActiveRecord;
end;
如何?
--------------------------------------------------------------------------
我那样写是为了在insert时仍正常显示。最好的解决方案其实就是用clientdataset,这样,不管什么数据库,recno都统一了。
 
楼上的方法连接到删过数据的 BDE 数据库就失败了,实际上连接很多种数据库
上都是失败的。我要研发在 DBGrid 上完美显示序号的方法,绝对不能够强制
用户使用数据库、数据集控件的范围,否则这样 DBGrid 不但不完美,还变得
比原来更弱智,顶贴那 7 种方法都不是好方法,包括楼上的方法,我都是全部
研究过。
 
如果不想用到RECNO,那用TScrollInfo也是一个办法,此结构记录了scroll的信息,结合ARow就可以计算出当前行的序号了。
 
自己写这个很麻烦,写出来效率也不高.
有些数据库驱动自己支持 row_number(),把这个作为SQL强制标准
就不用我们这些程序员这么费劲了[:)]
 
后退
顶部