5

I'm just beginning to learn Delphi and I've a problem. I'd refresh DBGrid from another form. My code:

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;

Form2:

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.

How I can after add record in form2, refresh dbgrid in form1? I've tried many ways, but without success. I know that in C# need pass reference in constructor, but delphi how?

Pablo
  • 173
  • 3
  • 13
  • 1
    Just `DataSource1.DataSet.Refresh` after close modal form if DBgrid is cinnected to DataSoource1. – Val Marinov May 10 '15 at 10:13
  • @TLama i edit my comment. I do not see who is DBGrid DataSource – Val Marinov May 10 '15 at 10:18
  • 2
    @Val, you're right! I've removed my comment. They will be separate components, which is good (though they both have the same name, which is not). So, I'd just add that to *centralize* the dataset components you can use a data module instead of forms. Also consider what happens if the user enters `x'; DROP TABLE Employees;` into your `txtName` control. And use `try..finally` block to ensure the resource will be released (in your code the `frm` instance). And that returning `mrCancel` looks weird. – TLama May 10 '15 at 10:23
  • @TLama, thank you very much! I used a data module and it's works! As for the drop table in txtName, how to protect from this? In block finally I add - "form2.Free", and I've a error (Access violation...). What's wrong? mrCancel I used to close modal. – Pablo May 10 '15 at 10:54
  • @Pablo The correct way to build a query is to use parameters. For an example of making a parameterized query, see : http://docwiki.embarcadero.com/CodeExamples/en/ADOQuery_(Delphi) – J... May 10 '15 at 12:19
  • 1
    @TLama Your comment is a brilliant responce to the question. Also there is a very interesting thing in the comment: SQL Injection http://en.wikipedia.org/wiki/SQL_injection . Please post it as an answer. – Val Marinov May 10 '15 at 12:33

3 Answers3

4

Using your pattern, I would do something like this:

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;
John Easley
  • 1,451
  • 1
  • 10
  • 20
1

A more general method to solving the problem of executing code from other units is simply to add an event to TForm2.

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

Implement this event as :

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;

Now, to assign a method to the event, in TForm1, declare a suitable method to handle it :

procedure TForm1.Form2DataInserted(Sender: TObject);
begin
  // do something, update grid, etc...
end;

And when creating your form :

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

This model allows you to execute any type of callback at any point in your code. This is a form of dependency injection because the owning component provides the method - TForm2 does not contain any references to TForm1 so this method preserves isolation of the units.

As noted by others in comments, this is probably not the ideal solution for your specific problem, but it does answer the direct question and provides a means for calling code cleanly across class and unit boundaries.

J...
  • 28,957
  • 5
  • 61
  • 128
0

The easy hack way, not what I would do

 (FOwner as TForm1).grid.refresh

The recommended method is to register a FreeNotification after you have created Form2

Forms.FreeNotification(Self).

This will send a Notification method to Form1 when Form2 is destroyed. In Form1 you add an event

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

In Implementation

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

Note I havent used it, but it should work.

Rohit Gupta
  • 2,411
  • 11
  • 21
  • 36
  • 2
    If you assume the `grid` to be a `TDBGrid`, then the `refresh` method will just force the grid control to repaint (which has nothing to do with the underlying data source to be refreshed). And the notification way is bad, because you would be refreshing whenever the form is released, e.g. even in case when the user just cancels the dialog. – TLama May 10 '15 at 11:01