如何解决我在哪里可以拦截派生 TDBGrid 中的行更改?
如果网格正在绘制其活动行,我想知道是否覆盖了 DrawColumnCell。
我想保留一个 ActiveRecno 私有变量来检查 DrawColumnCell 是否正在绘制该行。我尝试拦截数据源的 DataChange 以跟踪该 ActiveRecno。
TMyDBGrid = class(TDBGrid)
protected
OnDataChange_Original: TDataChangeEvent;
procedure TrackPosition(Sender: TObject; Field: TField);
procedure DrawColumnCell(const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState); override;
public
ActiveRecno: integer;
constructor Create(AOwner: TComponent): override;
...
...
implementation
constructor TMyDBGrid.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
OnDataChange_Original := nil;
if Assigned(DataSource) then begin
OnDataChange_Original := Datasource.OnDataChange;
Datasource.OnDataChange := TrackPosition;
end;
end;
procedure TMyDBGrid.TrackPosition(Sender: TObject; Field: TField);
begin
ActiveRecno := Datasource.DataSet.RecNo;
if Assigned(OnDataChange_Original) then OnDataChange_Original(Sender,Field);
end;
procedure TMyDBGrid.DrawColumnCell(const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);
var ActiveRow: boolean;
begin
ActiveRow := (Self.ActiveRecno = Self.DataSource.Dataset.Recno);
...
...
inherited DrawColumnCell(Rect,DataCol,Column,State);
end;
但 ActiveRecno 始终为 0,使 ActiveRow 始终为 False。那是因为在构造函数中Datasource仍然是nil,所以我从来没有设置TrackPosition来保留ActiveRecno。
在哪里可以为该事件设置处理程序?SetDataSource 过程是私有的,因此我无法覆盖它。
你是否推荐我另一种方法来跟踪活动行,或者在 DrawColumnCell 中检测要绘制的行是否是活动行?。
谢谢。
解决方法
我认为你想要的很简单,除了当前行 DBGrid 和 OnDrawrDataCell 事件中正在绘制的行都可以在事件内轻松访问。
幸运的是,使用如下所示的内插器 TDBGrid 类可以非常简单地解决这些问题。
中介层 TDBGrid 类只是将 TCustomGrid 的 Row 属性公开为 ActiveRow
OnDrawDataCell
事件是从 TCustomDBGrid.DrawCell 调用的,它是虚拟的,因此可以被覆盖
在中介层类中。如下所示,覆盖版本首先复制使用的行号(ARow 参数)
在 TCustomDBGrid.DrawCell 中放入 FRowBeingDrawn 字段,然后调用继承的 DrawDataCell,后者又调用 OnDrawDataCell 处理程序。由于这个处理程序
看到中介层类,网格的 ActiveRow 和 RowBeingDrawn 都可以在内部访问
OnDrawDataCell
事件。
type
TDBGrid = class(DBGrids.TDBGrid)
private,FRowBeingDrawn : Integer;
function GetActiveRow: Integer;
protected
procedure DrawCell(ACol,ARow: Longint; ARect: TRect; AState: TGridDrawState); override;
property RowBeingDrawn : Integer read FRowBeingDrawn write FRowBeingDrawn;
property ActiveRow : Integer read GetActiveRow;
end;
TForm1 = class(TForm)
DBGrid1: TDBGrid;
ClientDataSet1: TClientDataSet;
DataSource1: TDataSource;
ComboBox1: TComboBox;
DBNavigator1: TDBNavigator;
procedure FormCreate(Sender: TObject);
procedure DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
DataCol: Integer; Column: TColumn; State: TGridDrawState);
end;
[...]
procedure TForm1.FormCreate(Sender: TObject);
var
AField : TField;
begin
AField := TIntegerField.Create(Self);
AField.FieldKind := fkData;
AField.FieldName := 'ID';
AField.DataSet := ClientDataSet1;
AField := TStringField.Create(Self);
AField.FieldKind := fkData;
AField.FieldName := 'AValue';
AField.DataSet := ClientDataSet1;
ClientDataSet1.CreateDataSet;
ClientDataSet1.InsertRecord([1,'One']);
ClientDataSet1.InsertRecord([2,'Two']);
ClientDataSet1.InsertRecord([3,'Three']);
ClientDataSet1.InsertRecord([4,'Four']);
ClientDataSet1.InsertRecord([5,'Five']);
DBGrid1.DefaultDrawing := True;
end;
procedure TForm1.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
DataCol: Integer; Column: TColumn; State: TGridDrawState);
begin
if (Sender as TDBGrid).RowBeingDrawn = (Sender as TDBGrid).Row then
Caption := IntToStr((Sender as TDBGrid).RowBeingDrawn);
DBGrid1.DefaultDrawDataCell(Rect,Column.Field,State);
end;
procedure TDBGrid.DrawCell(ACol,ARow: Integer; ARect: TRect;
AState: TGridDrawState);
begin
RowBeingDrawn := ARow;
try
inherited;
finally
RowBeingDrawn := -1;
end;
end;
function TDBGrid.GetActiveRow: Integer;
begin
Result := Row;
end;
end.
内插器类当然可以包含在一个单独的单元中,当然,前提是它出现在 DBGrids 之后的使用单元的 Uses 列表中。
需要注意的一个小问题是,上面的代码没有考虑网格的标题行是否可见,如果不可见,则需要稍作调整。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。