在Delphi中,如何从另一个窗体刷新DBGrid?

5

我是一个有用的助手,可以帮你进行翻译。

我刚开始学习Delphi,遇到了一个问题。我想从另一个窗体刷新DBGrid。我的代码如下:

Form1:

unit uForm1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, DB, ADODB, Grids, DBGrids, StdCtrls;

type
  TForm1 = class(TForm)
    btnAdd: TButton;
    grid: TDBGrid;
    ADOQuery1: TADOQuery;
    DataSource1: TDataSource;
    procedure btnAdd(Sender: TObject);
  private
  public
       constructor Create(Owner:TComponent); overload;
  end;

var
  form1: TForm1;

implementation

uses uForm2, uDbOperations;

{$R *.dfm}

procedure TForm1.btnAddClick(Sender: TObject);
var
  frm : TForm2;
begin
  frm := TForm2.Create(form1);
  frm.ShowModal;
  frm.Free;
end;

表单2:

unit uForm2;
procedure TForm2.btnAddClick(Sender: TObject);
var
  query : string;
begin
       query := 'Insert into Employees(Name) Values('''+txtName.Text+''');';
       DbOperations.InsertOrUpdate(query, ADOQuery1);
        ModalResult := mrCancel;
        //And this here I'd refresh grid on form1           
end;
end.

在form2添加记录后,如何刷新form1中的dbgrid?我尝试了很多方法,但都没有成功。我知道在C#中需要在构造函数中传递引用,但是在Delphi中该怎么做呢?

1
如果DBGrid与DataSource1相连,则在关闭模态窗体后只需执行DataSource1.DataSet.Refresh - Val Marinov
@TLama,我编辑了我的评论。我看不到谁是DBGrid的数据源。 - Val Marinov
2
@Val,你说得对!我已经删除了我的评论。它们将是单独的组件,这很好(尽管它们都有相同的名称,这不好)。所以,我只需添加一个集中数据集组件,您可以使用数据模块而不是表单。还要考虑用户输入x'; DROP TABLE Employees;到您的txtName控件时会发生什么。并使用try..finally块确保资源将被释放(在您的代码中为frm实例)。而且返回mrCancel看起来很奇怪。 - TLama
@TLama,非常感谢!我使用了数据模块,它很好用!至于txtName中的删除表格,如何保护?在finally块中,我添加了“form2.Free”,但出现了错误(访问冲突...)。这是怎么回事?我使用mrCancel来关闭模态框。 - Pablo
@Pablo 正确构建查询的方法是使用参数。有关制作参数化查询的示例,请参见:http://docwiki.embarcadero.com/CodeExamples/en/ADOQuery_(Delphi) - J...
1
@TLama 你的评论是对这个问题的精彩回应。同时,在评论中还有一件非常有趣的事情:SQL注入 http://en.wikipedia.org/wiki/SQL_injection 。请将其作为答案发布。 - Val Marinov
3个回答

4
使用您的模式,我会像这样做:

使用您的模式,我会这样做:

procedure TForm1.btnAddClick(Sender: TObject);
var
  frm : TForm2;
begin
  frm := TForm2.Create(form1);
  try
    if frm.ShowModal = mrOK then
    begin
      Grid.Datasource.Dataset.Refresh;
      // some databases require open/close to refresh
      // in this case Grid.Datasource.Dataset.close;
      //              Grid.Datasource.Dataset.Open;
    end;
  finally
    frm.Free;
  end
end;

procedure TForm2.btnAddClick(Sender: TObject);
var
  query : string;
begin
  query := 'Insert into Employees(Name) Values('''+txtName.Text+''');';
  DbOperations.InsertOrUpdate(query, ADOQuery1);
  ModalResult := mrOK;
  // let a cancel button on Form2 perform ModalResult = mrCancel  
  // to avoid unnecessary dataset refreshes..      
end;

1

解决从其他单位执行代码的问题的更一般方法是向TForm2添加事件。

 TForm2 = class(TForm)
 private
   FOnDataInserted : TNotifyEvent;
 public
   property OnDataInserted : TNotifyEvent read FOnDataInserted write FOnDataInserted;
 end;

将此事件实现为:

procedure TForm2.btnAddClick(Sender: TObject);
var
  query : string;
begin
  query := 'Insert into Employees(Name) Values('''+txtName.Text+''');';
  DbOperations.InsertOrUpdate(query, ADOQuery1);
  ModalResult := mrCancel;
  //And this here I'd refresh grid on form1
  If Assigned(FOnDatabaseInsert) then FOnDatabaseInsert(self);                
end;

现在,为了将方法分配给事件,在 TForm1 中声明一个适当的方法来处理它:
procedure TForm1.Form2DataInserted(Sender: TObject);
begin
  // do something, update grid, etc...
end;

在创建表单时:

procedure TForm1.btnAddClick(Sender: TObject);
var
  frm : TForm2;
begin
  frm := TForm2.Create(form1);
  try
    frm.OnDataInserted := Form2DataInserted;
    frm.ShowModal;
  finally
    frm.Free;
  end;
end;

该模型允许您在代码的任何时刻执行任何类型的回调。这是一种依赖注入的形式,因为拥有组件提供了方法 - TForm2不包含对TForm1的任何引用,因此该方法保留了单元的隔离性。
正如其他评论中所指出的那样,这可能不是您特定问题的理想解决方案,但它确实回答了直接的问题,并提供了一种在类和单元边界之间清晰地调用代码的方法。

0

这是一种简单的黑客方式,但不是我会采取的方法。

 (FOwner as TForm1).grid.refresh

推荐的方法是在创建Form2之后注册一个FreeNotification
Forms.FreeNotification(Self).

Form2 被销毁时,这将向 Form1 发送一个通知方法。在 Form1 中,您需要添加一个事件。

protected
  procedure Notification(AComponent: TComponent; Operation: TOperation); override;

正在实现中

procedure TForm1.Notification(AComponent: TComponent; Operation: TOperation);
begin
  inherited;
  if (AComponent is TForm) and (Operation = opRemove) then
  begin
    grid.Refresh;
  end;
end; 

注意,我还没有使用过它,但它应该可以正常工作。

2
如果您假设grid是一个TDBGrid,那么refresh方法只会强制网格控件重新绘制(这与刷新底层数据源无关)。而且通知方式不好,因为您将在释放窗体时刷新,例如,即使用户只是取消了对话框。 - TLama

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接