在Delphi中一个派生类如何调用前两重祖先的虚函数?

jzy 2000-03-26 12:05:00

具体情况是这样的:
TDrawGrid有一虚函数DrawCell
而TDrawGrid的派生类TStringGrid又重载DrawCell如下:

procedure TStringGrid.DrawCell(ACol, ARow: Longint; ARect: TRect;
AState: TGridDrawState);
begin
if DefaultDrawing then
Canvas.TextRect(ARect, ARect.Left+2, ARect.Top+2, Cells[ACol, ARow]);
inherited DrawCell(ACol, ARow, ARect, AState);
end;

也就是说TStringGrid的DrawCell调用了其祖先TDrawGrid的虚函数DrawCell,

现在我用TStringGrid派生一个类TStringGrid1,并且重载DrawCell
(其实我的目的是要把TStringGrid的DrawCell在TStringGrid1中声明成public):

type TStringGrid1 = class (TStringGrid)
public
procedure DrawCell(ACol, ARow: Longint; ARect: TRect;
AState: TGridDrawState);override;

end;


procedure TStringGrid1.DrawCell(ACol, ARow: Longint; ARect: TRect;
AState: TGridDrawState);
begin
inherited DrawCell(ACol, ARow, ARect, AState);
end;

但是这样行不通,运行的时候提示堆栈溢出错误。
我也试过不重载DrawCell,另外声明一个public的成员,在该成员中调用DrawCell,则无论是直接调用,还是用inherited声明调用,结果都提示堆栈溢出错误。

请教各位大虾,我该如何声明,才能够正确调用DrawCell呢?
...全文
196 5 打赏 收藏 转发到动态 举报
写回复
用AI写文章
5 条回复
切换为时间正序
请发表友善的回复…
发表回复
Jean 2000-03-27
  • 打赏
  • 举报
回复
你怎么在OnDrawCell时又去DrawCell,岂不死掉!
kxy 2000-03-26
  • 打赏
  • 举报
回复
我试了一下,并没有错误。
贴些代码上来。你是如何使用TStrignGrid1的.
yangfan 2000-03-26
  • 打赏
  • 举报
回复
你认为必须要能够正确的调用到TDrawGrid(也就是TString1的两重祖先)的
虚函数DrawCell才行,我想那还是不行的

死循环问题就出在下面两句
OnDrawCell := StringGrid1DrawCell;
然后你在StringGrid1DrawCell里调用了DrawCell。

看如下源代码
procedure TDrawGrid.DrawCell(ACol, ARow: Longint; ARect: TRect;
AState: TGridDrawState);
var
Hold: Integer;
begin
if Assigned(FOnDrawCell) then
begin
if UseRightToLeftAlignment then
begin
ARect.Left := ClientWidth - ARect.Left;
ARect.Right := ClientWidth - ARect.Right;
Hold := ARect.Left;
ARect.Left := ARect.Right;
ARect.Right := Hold;
ChangeGridOrientation(False);
end;
FOnDrawCell(Self, ACol, ARow, ARect, AState);
if UseRightToLeftAlignment then ChangeGridOrientation(True);
end;
end;

当你调用DrawCell是,程序最终会走到TDrawGrid.DrawCell里,然后由于你
分配了FOnDrawCell=StringGrid1DrawCell;所以进入了死循环。你的解救办法
只能是自己在StringGrid1DrawCell里去画,我想不会很麻烦吧。

另外一个问题
(其实我的目的是要把TStringGrid的DrawCell在TStringGrid1中声明成public)
如果你想在一个unit中调用一个存在于另一个unit中的类的protected成员时,
只学要从新定义一个类就可以,他的protected成员函数不用重写成public,如
TStringGrid1 = class(TStringGrid) ;就行了。
这样,在这个unit里,你的TStringGrid1 的实体就可以直接调用TStringGrid
的protected成员函数了,其实这是不符合面向对象编程的,但是Delphi可以
这样做,说不上是缺点还是优点,反正当你用的着时用上去会感到很爽,xixi!


kxy 2000-03-26
  • 打赏
  • 举报
回复
不要这样干,在TStringGrid的OnDrawCell中自己画。
虚函数是没有实体的。
或者看看DBGrid的source, 可能有启发。
jzy 2000-03-26
  • 打赏
  • 举报
回复
声明这个类当然没有错误,但是如果调用这个函数就会出错了。
在StringGrid1中inherited DrawCell(ACol, ARow, ARect, AState);
调用的并不是TDrawGrid的成员DrawCell,而是调用的TStringGrid的成员(也就是自己本身),如是形成了一个递归死循环(呵呵,不知道是不是这样说,因为是只递了却没有归^_^),则无论把堆栈设多大,都会导致堆栈溢出!

我的代码如下:
unit StrGrid;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
Grids, Db, DBTables, ToolWin, ComCtrls, StdCtrls, Buttons;

type
TStringGrid1 = class(TStringGrid)
procedure DrawCell(ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);override;
end;

TForm1 = class(TForm)
ToolBar1: TToolBar;
Table1: TTable;
BitBtn1: TBitBtn;
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure BitBtn1Click(Sender: TObject);
procedure StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
procedure datafresh;
{ Public declarations }
end;

var
Form1: TForm1;
StringGrid1:TStringGrid1;
var n:integer;

implementation

{$R *.DFM}

procedure TStringGrid1.DrawCell(ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
begin

inherited DrawCell(ACol, ARow, Rect, State);
end;


procedure TForm1.FormCreate(Sender: TObject);
begin
n:=0;
StringGrid1:=TStringGrid1.Create(Self);
STringGrid1.Parent:=Self;
with STringGrid1 do
begin
Left := 0;
Top := 29;
Width := 536;
Height := 319;
Align := alClient;
DefaultColWidth := 40;
DefaultRowHeight := 18;
FixedCols := 2;
Font.Charset := GB2312_CHARSET;
Font.Color := clWindowText;
Font.Height := -13;
Font.Name := '宋体';
Font.Style := [];
ParentFont := False;
TabOrder := 1;
OnDrawCell := StringGrid1DrawCell;
end;

ShortDateFormat:='yyyy-m-d';
datafresh;
end;

procedure tform1.datafresh;
var i,j,k:integer;
begin
table1.open;
table1.First;
k:=table1.FieldCount div 2;
stringgrid1.ColCount:=k+2;
stringgrid1.RowCount:=table1.RecordCount*2+1;
stringgrid1.ColWidths[0]:=130;
stringgrid1.ColWidths[1]:=38;
for i:= 2 to table1.FieldCount+2 do
stringgrid1.Cells[i,0]:='设备'+inttostr(i-1);
for i:= 0 to table1.RecordCount-1 do
begin
stringgrid1.Cells[0,i*2+1]:=table1.Fields[0].AsString;
stringgrid1.cells[1,i*2+1]:='重力:';
stringgrid1.cells[1,i*2+2]:='水平:';
for j:=1 to k do
stringgrid1.Cells[j+1,i*2+1]:=table1.Fields[j].AsString;
for j:=1 to k do
stringgrid1.Cells[j+1,i*2+2]:=table1.Fields[j+k].AsString;
table1.Next;
end;
table1.Close;
end;

procedure TForm1.BitBtn1Click(Sender: TObject);
begin
datafresh;
end;

procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
var value:real;
begin
n:=n+1;
if (ACol>=2) and ((ARow mod 2)=1) then
begin
value:=StrToFloat(stringgrid1.Cells[ACol,ARow]);
if (value<2.0) or (value>4.0) then
begin
StringGrid1.Canvas.Font.Color:=clRed;
StringGrid1.DrawCell(ACol,ARow,Rect,State);
end;
end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
button1.Caption:=IntToStr(n);
end;

end.


其中设置个n是为了检测究竟产生了多少次事件响应

如果但是如果把StringGrid1DrawCell中调用DrawCell那一行去掉,就不会有错误,而且n值为网格数,但是调用了DrawCell后,堆栈溢出后,n成了天文数字,所以肯定是导致了一个递归的死循环,我认为必须要能够正确的调用到TDrawGrid(也就是TString1的两重祖先)的虚函数DrawCell才行!

请各位大侠指教!

5,388

社区成员

发帖
与我相关
我的任务
社区描述
Delphi 开发及应用
社区管理员
  • VCL组件开发及应用社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧