Jump to content
CRO_Tomislav

Share a data between two units in dynamic loaded BPL.

Recommended Posts

I need a help.
If I make a project with one form and two units a showed basic code is working ok.

unit Prva;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.Mask, Vcl.ExtCtrls,
  Vcl.DBCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

uses
 Druga;
{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
 Druga.Demo;
end;

end. 
unit Druga;

interface
 uses
  Dialogs;

function Demo:boolean;

implementation
uses
 Prva;

function demo: boolean;
begin
  ShowMessage(Form1.Edit1.Text);
end;


end.

If I press a button on Form1 it run function in Druga.pas and show message with a text filled in Edit1 component.

The problem is If I try to do the same thing with a Form and units which are in dynamic loaded bpl package. It doesn’t work.
It seams that it can’t found a Form or component by its name…

How to solve this?

 

THX in advance

Share this post


Link to post

I would decouple the action by having a handler.

 

unit Druga;

interface
 uses
  Dialogs;

function Demo:boolean;

type
  TOnShowMessage = reference to procedure;
var
  OnShowMessage: TOnShowMessage;

implementation
// No circular ref here

function demo: boolean;
begin
  if Assigned(OnShowMessage)
  then OnShowMessage
  else raise Exception.Create('Someone forgot to set the OnShowMessage handler');
end;

initalization
  OnShowMessage := nil;
end.

In Prva, I'd connected the handler in f.x. FormCreate;

 

procedure TForm1.FormCreate(sender: TObject);
begin
  Druga.OnShowMessage(Self.MyShowMessage);
end;

procedure TForm1.MyShowMessage;
begin
  ShowMessage(Form1.Edit1.Text);
end;

This avoids the circular unit ref, and since you initialize the handler from the main form, it shouldn't matter if the bpl is dynamically loaded?

If you want to, you can of course also add parameters to the handler to display a text generated in unit Druga or have multiple handlers for different uses.

 

 

Share this post


Link to post

This is intrested but; 

Exactly my goal is: I inherited a project in Delphi which has a huge units with a lot of code. Mostly of that code is used to create some XML files. Idea is to separate from this unit a code which is generate this XML files to separate units. But for do that I need to access from this (sub)units to Form and grab query results to populate this XML files or even modify and run a querys

As this is doesn’t work "ProfileID.Text := Form1.Query_User.FieldByName('userID').AsString;" in a (sub)unit; do You have some idea what would be elegant solution for this?

Share this post


Link to post

(Sub)Unit2 doesn't know for Form1 in code: ProfileID.Text := Form1.Query_User.FieldByName('userID').AsString;

As my form is opened from package by menue item in Main form by: 

procedure TForm_Maom.Button1Click(Sender: TObject);
var
 AClass :TFormClass;
begin
if not IsMDIChildOpen(Form_Glavna,'Form1') then
  begin
   if PackageModule <> 0 then
    begin
     AClass := TFormClass(GetClass('TForm1'));
     if AClass <> nil then TComponentClass(AClass).Create(Self);
    end;
  end;
end;

 

Share this post


Link to post

One thing about XML is on a treeview each node needs a unique caption so as tabSheets or the parent of control wanted are added a number is added to the node's caption as the forms are added.  

 

Second thing is the Control could have data reference added.  

 

type
  // On the UI controls side carry some data references for convenience
  // the data side could use Dependance Injection to tie in UI controls
  TDataButton = class(TButton)
    Form: TControl;
    Strs: TStrings;
end;

What's neat about MDI you can create a new childform inside a begin..end.  To access the form elsewhere use Application.Screen 

Share this post


Link to post
15 minutes ago, Pat Foley said:

One thing about XML is on a treeview each node needs a unique caption so as tabSheets or the parent of control wanted are added a number is added to the node's caption as the forms are added.  

 

Second thing is the Control could have data reference added.  

 


type
  // On the UI controls side carry some data references for convenience
  // the data side could use Dependance Injection to tie in UI controls
  TDataButton = class(TButton)
    Form: TControl;
    Strs: TStrings;
end;

What's neat about MDI you can create a new childform inside a begin..end.  To access the form elsewhere use Application.Screen 

XML is not a problem, Accessing to Quer1 on MDI Form1 is a problem...

Can You provide me an example for Application.Screen to access a Query1 on a MDI form Form1?

Share this post


Link to post

Isolate your business logic from the UI. Use an intermediate class to pass the data around.

  • Like 1

Share this post


Link to post

 This should yield control asked for. 

 

procedure TTaskMaster.findControl(AtaskDetail: TInstruction; var AC: TControl);
var
  ff, ii : integer;
  sForm, sControl: string;
begin
  with AtaskDetail do
  begin
    if Control <> nil then
      AC := Control
    else
    begin
      sForm := controlForm + ' not found.';
      sControl := controlName + ' not found.';
      //screens move about on windows list top window usually 0 :(
      for ff := 0 to Screen.FormCount - 1 do
      if Screen.Forms[ff].name = controlform then
      begin
        sform := controlForm;
        with Screen.Forms[ff] do begin
          for ii := 0 to componentcount - 1 do
          if Components[ii].Name = ControlName then
          begin
            sControl := controlName + ' comps ' + ii.ToString;
            Control := Components[ii] as Tcontrol;
            //onBS(False, format('Item %d Found %s',[ii,controlName]));
            break;
          end
          //else//if name
          //run := false;
          //  onBS(False, format('Item %d not found %s',[ii,  controlName]));
        end;// with screen forms
      end;  //if screen.forms
      onBS(False, sForm + ' ' + sControl);
    end;// else
  end;//with taskdetail
end;

What TInstruction is

  TInstruction = class  //position in Db
    Atom: PAtom;
    Control: TControl;
    ControlForm: string;   //3
    ControlName: string;   //2
    Description: string;//6
    //Nu: integer;       //0
    Kind: string;     //1  was integer
    KindNU: integer;
    referenceValue: Double;//5
    tagName: string;    //4
    tagValue: Double;   //no data in table
  end;

 

Share this post


Link to post

You might to study and use this to move cursor to control being selected.  I want to add it to IDE <F6> or <Ctrl>.  function it only jumps to selection without any animation 🙂  

procedure TTaskmaster.moveMouse(AtaskDetail: TInstruction);
var
  x, y, MouseXError, MouseYError: integer;
  ControlCenter: Tpoint;
  AC: TControl;
begin
  findControl(AtaskDetail, AC);
  if not assigned(AC) then exit;

  if assigned(AC.Parent) then
    AC.Parent.Show;
  AC.Show;

  x := AC.ClientOrigin.X + AC.width div 2;
  y := AC.ClientOrigin.y + AC.height div 2;
  begin
    MouseXError := round(1.12 *(x - mouse.CursorPos.X));
        MouseYError := round(1.12 * (y - mouse.cursorPos.y));
//            MouseXError := min(MouseXError, 10 * sign(MouseXError));
//                MouseYError := min(MouseYError,  10 * sign(MouseYError));
    controlCenter.X := mouse.CursorPos.x + MouseXError;
    controlCenter.Y := Mouse.CursorPos.y + mouseYError;
    mouse.CursorPos := ControlCenter;
  end;
  repeatTask := (abs(MouseXError) > 7) or (abs(MouseYError) > 7);
end;

 

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×