在DBGrid上显示查询结果时加入行号问题。(100分)

  • 主题发起人 主题发起人 andrewang
  • 开始时间 开始时间
A

andrewang

Unregistered / Unconfirmed
GUEST, unregistred user!
请问如何在DBGrid上加入一列,以便显示查询结果集的行号。
我使用Delphi 3,BDE连接,Query.RecNo返回值均为-1。
 
使用书签Bookmark
GetBookMark
GotoBookMark
FreeBookMark
 
用什么数据库,如果是SQL SERVER等大型数据库的话它们都不支持行号的概念,
我想设一个计算字段来计算行号是一个好办法.
 
softprince的回答不明白是什么意思。
能否请liwei将计算字段的具体语法写出来。我已经试过很多次了,没办法。
我使用informix数据库,不知那位仁兄有资料,请帮我查一下,
从数据库上是否可以返回查询结果的行号。
 
你真的很需要行号吗?
从数据库系统的角度来看,数据是被看成是集合的,TTable、TQuery、TStoredProc 都是
TDataSet 的后代,我们知道集合元素是没有序的。
 
我真的很想要!
我想给这个问题加分,不知道该怎么加。
我走遍了大大小小的DELPHI网站和新闻组,还没有得到一个满意的答案。
 
我以前的做法:
建一临时库,带有行号字段,将查询到的数据放到该临时库,再从头到尾一条条将行号
加一,好笨的做法!


 
我也提了相同的问题,可是美人回答。
 
Table1.RecNo的sql数据库(如oracle)永远是-1

下面贴一段郭镇松老弟的代码(来获得recno):
>>to jqw :
>> 我知道 recno 在sql数据库中不对,但也可以自己去做这个属性的。
用 moveby 来实现,比如,得到当前记录行:
oldrec := table1.moveby(-maxint);
table1.moveby(oldrec);
这样表的记录指针不变,oldrec就是对应的recno值了。
再如设置recno值:
table1.first;
table1.moveby(rec); // rec为需要设置的行号。

 
wind2000 的方法,如果你将行号字段的数据类型设为自动增量,好象就不需要
你手工将行号一条条增1了。
但是,你的查询结果插入临时表时,支持排序吗?
我这么做的时候,因为插入临时表的查询结果不支持ORDER BY子句,所以放弃了。
重申一下,我需要在DBGRID中显示行号。


 
从TCustomDBGrid继承一个类:

TMyDBGrid = class(TCustomDBGrid)
protected
procedure DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState); override;
procedure WMVScroll(var Message: TWMVScroll); message WM_VSCROLL;
public
ColNo: Integer;//显示序号的列号
RecNo: Integer;//第一可见行行号
end;

然后,比如点击某Button进行查询:

procedure TForm1.Button1Click(Sender: TObject);
var
I: Integer;
Column: TColumn;
begin
for I := MyDBGrid.Columns.Count - 1 downto 0 do MyDBGrid.Columns.Free;
Query1.Open;
Column := TColumn.Create(MyDBGrid.Columns);
Column.Title.Caption := '序号';
MyDBGrid.ColNo := 1;
MyDBGrid.RecNo := 0;
for I := 0 to Query1.FieldCount - 1 do begin
Column := TColumn.Create(MyDBGrid.Columns);
Column.Field := Query1.Fields;
end;
end;

在DrawCell里显示序号:

procedure TMyDBGrid.DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState);
var
s: String;
R: TRect;
begin
inherited;
if DataSource.DataSet.Active and (ACol = ColNo) and (ARow > 0) then begin
s := IntToStr(RecNo + ARow);
R := ARect;
InflateRect(R, -2, -2);
DrawText(Canvas.Handle, PChar(s), Length(s), R, DT_RIGHT)
end;
end;

难点在于控制第一可见行的行号:

procedure TMyDBGrid.WMVScroll(var Message: TWMVScroll);
begin
inherited;
with Message do
case ScrollCode of
SB_LINEUP: Dec(RecNo);
SB_LINEDOWN: Inc(RecNo);
SB_PAGEUP: Dec(RecNo, VisibleRowCount);
SB_PAGEDOWN: Inc(RecNo, VisibleRowCount);
SB_THUMBPOSITION:
case Pos of
0: RecNo := 0;
1: Dec(RecNo, VisibleRowCount);
2: Exit;
3: Inc(RecNo, VisibleRowCount);
4: RecNo := DataSource.DataSet.RecordCount;
end;
SB_BOTTOM: RecNo := DataSource.DataSet.RecordCount;
SB_TOP: RecNo := 0;
end;
if RecNo < 0 then RecNo := 0;
if RecNo > DataSource.DataSet.RecordCount - VisibleRowCount then
RecNo := DataSource.DataSet.RecordCount - VisibleRowCount;
Refresh;
end;

对以上程序进行了简单测试,效果还行(我用的是SQL SERVER的查询)。
但是最后一段对RecNo的控制还不是很有把握,主要是因为RecordCount
不一定可靠,实在不行你可以先用一个SELECT Count(*)查询把记录数
取出来记在一个变量里。

——再不行你可以看看TCustomDBGrid的源码。
 
dq , 你的方法是我最想要的。
我还没有写过VCL,很想试一下,但有许多问题还不明白。
如果我继承Tcustomdbgrid的话,是否要重写Tdbgrid的很多属性和方法?
你能否详细描述一下,就这个问题我应该如何做?不胜感谢。
 
to andrewang:
>>如果我继承Tcustomdbgrid的话,是否要重写Tdbgrid的很多属性和方法?
不需要的,就象我写的那个TMyDBGrid就行,你只需要把需要重载的方法重写一遍,其它的完全
交给基类去处理。你可以先把我的程序拷下来试一下,只要把相关的查询换成你实际用的就行。

事实上,自己写的VCL和普通VCL没有任何区别,如果非要说出点区别的话可能就是:在Form里
放置的VCL控件不需要你创建和释放而已。比如我写的这个例子,你可以在Form的private部分
定义一个变量MyDBGrid: TMyDBGrid;(前提是将TMyDBGrid的定义,即我写的第一段程序,放在TForm1的定义之前)。
然后你要做的就是在Form1的OnCreate里这样写:

procedure TForm1.FormCreate(Sender: TObject);
begin
MyDBGrid := TMyDBGrid.Create(Self);
with MyDBGrid do begin
Parent := Self;//这个很重要,不然是看不到的
Align := alClient;//根据你的Form的布局可以自己调整,可以设置Top,Left,Width,Height等。
DataSource := DataSource1;//这就需要手动的设置了
//......其它的属性和普通DBGrid一样的设就行了
end;
end;

然后别忘了在Form1的Destroy里写:

procedure TForm1.FormDestroy(Sender: TObject);
begin
MyDBGrid.Free;
end;

其它的就一切自动进行了。
 
补充一句:TDBGrid只是公布了TCustomDBGrid的一些属性,它本身没有添加任何新的方法,
你看一下TDBGrid的源码就知道了。
 
dq, 你的方法经过测试说明是正确的,这100分当之无愧。谢谢你的帮助。
另外,因为我要在多处用到这个新的类,同时希望在IDE环境中对其属性和事件进行编辑,
所以我希望能将它做成一个可视的元件,你是否能做进一步的指导。
我已经试着做了一个元件,但放到form上时会提示一个访问错误。


 
出现错误是因为控件的方法里没有对控件状态(ComponentState,集合类型的)进行判断,
一般情况当ComponentState里包含csLoading(或是csDesigning)时不应该做某些事情,
就象这里对DataSet的操作等;所以应加一些判断。

我想了一下,做成可视控件的话,最大的难点在于加入序号列的时机不好把握——你必须
在控件本身对数据源并不了解的情况下判断数据源打开和关闭的时间;所以最好给控件
添加一个public方法,让主程序主动通知控件添加或清除相关的Column。但这样做就不能
在设计阶段设置固定的Column属性(Title.Caption等等)了:),不知道你需不需要这样做,
如果需要可以改对TQuery的固定字段操作,实在不行就又得在程序里写了。
 
谢谢dq的不厌其烦。
除了要加序号,其他我希望象dbgrid那样操作。
也感谢其余几位,你们的答案至少给了我一些有益的提示,谢谢。
 

Similar threads

S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
D
回复
0
查看
753
DelphiTeacher的专栏
D
D
回复
0
查看
659
DelphiTeacher的专栏
D
D
回复
0
查看
653
DelphiTeacher的专栏
D
后退
顶部