Form Communication Mechanism Questions

MTCMTC Member Posts: 159
edited 2007-04-24 in NAV Tips & Tricks
I'm not putting this into the tricks and tips forum (If a moderator deems it to be useful or new, please move it) because:

A) I don't know if it is a known mechanism or not.

B) It's an implementation of an idea, it would need a bit of work to make it more generic.

C) It's an idea that need developing.

However, over time I have seen many people asking about communication between forms (I saw one the other day which got me thinking and lead to this post), and only come across the Single Instance Code Unit and Timer event workarounds. This lead me to this idea where through a "proxy" codeunit object and a form that launches other forms, a basic method for one form (subform or not) to invoke a function in another form can be realised without single instance codeunits or timer events.

Thank the lord for the "this" pointer in C++.

As I say, I do not know if this has ever been proposed before or is well known or not (I've never seen it), but it is based on a quite important and well known quirk Navision seems to have; that a codeunit instance passed to a function is not a copy but the same instance and it can be assigned to a variable (something you cannot do with a form object). This is the closest thing we have in Navision to a pointer/reference to a core object, and if used correctly can be used as a generic pointer/reference to any object. This codeunit quirk is explained here: http://dynamicsuser.net/forums/thread/61736.aspx

It works like this:

1) A single codeunit acts a a communication mechanism in my example it's 50000 - "FormCommunicator". This codeunit calls functions in forms. To make the communication work with a form, you simply have to add a little code in the codeunit to do this. At the moment it only works with one function, but you could make it work with many, even form specific ones. As I said, this is a demonstration of an idea.

2) An invisible form that launches the codeunit that launches the forms. We need this to actually create the codeunit instance. I have chosen a form to do this over another codeunit to enable runs instead of only modal runs. This form in my example is 50010 - FormLauncher. What it does it create the FormCommunicator and passes a reference to itself before it is run. It also has code in the OnHyperlink trigger that allows the Navigation pain to allow it to launch different forms (this mechanism can be seen here: http://dynamicsuser.net/forums/thread/61728.aspx) and a simple function that does the same if another object has an instance of this object.

3) The forms you want to communicate with each other need to have two new functions added in this example (DoIt - which is the parent doing something the child form wants it to do and SetCurrRef). DoIt is called from the codeunit, SetCurrRef accepts a reference to the codeunit. A parent form also need to have code added in the OnOpenForm trigger to pass the codeunit reference to the child and code added to the OnCloseForm trigger to clear the variable holding the codeunit reference. The subform only needs code to clear the reference variable in OnCloseForm.

That's it.

Here is an example of use:
OBJECT Form 50000 Test
{
  OBJECT-PROPERTIES
  {
    Date=06/04/07;
    Time=17:04:35;
    Modified=Yes;
    Version List=;
  }
  PROPERTIES
  {
    Width=3300;
    Height=2420;
    OnOpenForm=BEGIN
                 CurrForm.Sub.FORM.SetCuRef(commcuref);
               END;

    OnCloseForm=BEGIN
                  CLEAR(commcuref);
                END;

  }
  CONTROLS
  {
    { 1000000000;TextBox;110  ;110  ;3080 ;880  ;FontName=Courier;
                                                 FontSize=18;
                                                 FontBold=Yes;
                                                 SourceExpr=count }
    { 1000000001;SubForm;110  ;1100 ;3080 ;1210 ;Name=Sub;
                                                 Border=No;
                                                 SubFormID=Form50005 }
  }
  CODE
  {
    VAR
      count@1000000000 : Integer;
      commcuref@1000000001 : Codeunit 50000;

    PROCEDURE DoIt@1000000000();
    BEGIN
      count += 1;
      CurrForm.UPDATE(FALSE);
    END;

    PROCEDURE SetCuRef@1000000002(curef@1000000000 : Codeunit 50000);
    BEGIN
      commcuref := curef;
    END;

    BEGIN
    END.
  }
}

OBJECT Form 50001 Test2
{
  OBJECT-PROPERTIES
  {
    Date=06/04/07;
    Time=17:05:00;
    Modified=Yes;
    Version List=;
  }
  PROPERTIES
  {
    Width=3300;
    Height=2420;
    OnOpenForm=BEGIN
                 CurrForm.Sub.FORM.SetCuRef(commcuref);
               END;

    OnCloseForm=BEGIN
                  CLEAR(commcuref);
                END;

  }
  CONTROLS
  {
    { 1000000000;TextBox;110  ;1430 ;3080 ;880  ;FontName=Courier;
                                                 FontSize=18;
                                                 FontBold=Yes;
                                                 SourceExpr=count }
    { 1000000001;SubForm;110  ;110  ;3080 ;1210 ;Name=Sub;
                                                 Border=No;
                                                 SubFormID=Form50005 }
  }
  CODE
  {
    VAR
      count@1000000000 : Integer;
      commcuref@1000000001 : Codeunit 50000;

    PROCEDURE DoIt@1000000000();
    BEGIN
      count -= 1;
      CurrForm.UPDATE(FALSE);
    END;

    PROCEDURE SetCuRef@1000000002(curef@1000000000 : Codeunit 50000);
    BEGIN
      commcuref := curef;
    END;

    BEGIN
    END.
  }
}

OBJECT Form 50002 Test3
{
  OBJECT-PROPERTIES
  {
    Date=06/04/07;
    Time=17:16:02;
    Modified=Yes;
    Version List=;
  }
  PROPERTIES
  {
    Width=3300;
    Height=1760;
    OnCloseForm=BEGIN
                  CLEAR(commcuref);
                END;

  }
  CONTROLS
  {
    { 1000000000;TextBox;110  ;110  ;3080 ;880  ;FontName=Courier;
                                                 FontSize=18;
                                                 FontBold=Yes;
                                                 SourceExpr=count }
    { 1000000002;CommandButton;110;1100;3080;550;Name=Run;
                                                 OnPush=BEGIN
                                                          OtherForm.SetCuRef(commcuref);
                                                          OtherForm.RUN;
                                                        END;
                                                         }
  }
  CODE
  {
    VAR
      count@1000000000 : Integer;
      commcuref@1000000001 : Codeunit 50000;
      OtherForm@1000000002 : Form 50005;

    PROCEDURE DoIt@1000000000();
    BEGIN
      count += 1;
      CurrForm.UPDATE(FALSE);
    END;

    PROCEDURE SetCuRef@1000000002(curef@1000000000 : Codeunit 50000);
    BEGIN
      commcuref := curef;
    END;

    BEGIN
    END.
  }
}

OBJECT Form 50005 SubTest
{
  OBJECT-PROPERTIES
  {
    Date=06/04/07;
    Time=16:56:15;
    Modified=Yes;
    Version List=;
  }
  PROPERTIES
  {
    Width=3080;
    Height=1100;
    OnCloseForm=BEGIN
                  CLEAR(commcuref);
                END;

  }
  CONTROLS
  {
    { 1000000000;CommandButton;0;0  ;3080 ;1100 ;Name=Update;
                                                 FontName=Aerial;
                                                 FontSize=18;
                                                 FontBold=Yes;
                                                 OnPush=BEGIN
                                                          commcuref.DoIt();
                                                        END;
                                                         }
  }
  CODE
  {
    VAR
      commcuref@1000000000 : Codeunit 50000;

    PROCEDURE SetCuRef@1000000002(curef@1000000000 : Codeunit 50000);
    BEGIN
      commcuref := curef;
    END;

    BEGIN
    END.
  }
}

OBJECT Form 50010 FormLauncher
{
  OBJECT-PROPERTIES
  {
    Date=06/04/07;
    Time=16:30:56;
    Modified=Yes;
    Version List=;
  }
  PROPERTIES
  {
    Width=440;
    Height=330;
    Visible=No;
    OnInit=BEGIN
             RunFormNo := 1;
           END;

    OnOpenForm=BEGIN
                 formcu.SetCuRef(formcu);
                 formcu.RunForm(RunFormNo);
                 formcu.RUN;
                 CLEAR(formcu);
                 CurrForm.CLOSE;
               END;

    OnHyperlink=VAR
                  EPos@1000000001 : Integer;
                BEGIN
                  EPos := STRPOS(URL,'VARIABLE=') + 9;
                  EVALUATE(RunFormNo,COPYSTR(URL,EPos,(STRLEN(URL) - EPos) + 1));
                END;

  }
  CONTROLS
  {
  }
  CODE
  {
    VAR
      formcu@1000000000 : Codeunit 50000;
      RunFormNo@1000000001 : Integer;

    PROCEDURE RunForm@1000000001(FormNo@1000000000 : Integer);
    BEGIN
      RunFormNo := FormNo;
    END;

    BEGIN
    END.
  }
}

OBJECT Codeunit 50000 FormCommunicator
{
  OBJECT-PROPERTIES
  {
    Date=06/04/07;
    Time=16:51:57;
    Modified=Yes;
    Version List=;
  }
  PROPERTIES
  {
    SingleInstance=No;
    OnRun=BEGIN
            CASE RunFormNo OF
                1: BEGIN
                    tform.SetCuRef(commcuref);
                    tform.RUNMODAL();
                   END;
                2: BEGIN
                    tform2.SetCuRef(commcuref);
                    tform2.RUNMODAL();
                   END;
                3: BEGIN
                    tform3.SetCuRef(commcuref);
                    tform3.RUNMODAL();
                   END;
            END;
          END;

  }
  CODE
  {
    VAR
      tform@1000000001 : Form 50000;
      tform2@1000000003 : Form 50001;
      tform3@1000000004 : Form 50002;
      RunFormNo@1000000002 : Integer;
      commcuref@1000000000 : Codeunit 50000;

    PROCEDURE SetCuRef@1000000002(curef@1000000000 : Codeunit 50000);
    BEGIN
      commcuref := curef;
    END;

    PROCEDURE DoIt@1000000000();
    BEGIN
      CASE RunFormNo OF
          1: tform.DoIt();
          2: tform2.DoIt();
          3: tform3.DoIt();
      END;
    END;

    PROCEDURE RunForm@1000000001(FormNo@1000000000 : Integer);
    BEGIN
      RunFormNo := FormNo;
    END;

    BEGIN
    END.
  }
}

OBJECT MenuSuite 90 Company
{
  OBJECT-PROPERTIES
  {
    Date=06/04/07;
    Time=17:07:15;
    Modified=Yes;
    Version List=;
  }
  PROPERTIES
  {
  }
  MENUNODES
  {
    {                ;[{28B406BE-DD89-43EC-B2B9-6BB90DC2583B}] ;NextNodeID=[{E1094594-64FB-4AEC-9963-3C4363A6F43B}] }
    { MenuShortcut   ;[{E1094594-64FB-4AEC-9963-3C4363A6F43B}] ;Name=Test;
                                                                CaptionML=ENU=Run Form One;
                                                                MemberOfMenu=[{19A352FE-D90D-424E-B85F-CF3B8E98CF0E}];
                                                                ParentNodeID=[{19A352FE-D90D-424E-B85F-CF3B8E98CF0E}];
                                                                ShortcutURL=navision:/ /client/run?target=Form%2050010%26MYVARIABLE=1;
                                                                Visible=Yes;
                                                                NextNodeID=[{334E0A3B-9E47-4D5C-A8FC-B5E22546DD65}] }
    { MenuShortcut   ;[{334E0A3B-9E47-4D5C-A8FC-B5E22546DD65}] ;Name=Test2;
                                                                CaptionML=ENU=Run Form Two;
                                                                MemberOfMenu=[{19A352FE-D90D-424E-B85F-CF3B8E98CF0E}];
                                                                ParentNodeID=[{19A352FE-D90D-424E-B85F-CF3B8E98CF0E}];
                                                                ShortcutURL=navision:/ /client/run?target=Form%2050010%26MYVARIABLE=2;
                                                                Visible=Yes;
                                                                NextNodeID=[{6BAF36FB-1A5F-4FD3-AE6E-EE837F5CBB99}] }
    { MenuShortcut   ;[{6BAF36FB-1A5F-4FD3-AE6E-EE837F5CBB99}] ;Name=Test2;
                                                                CaptionML=ENU=Run Form Three;
                                                                MemberOfMenu=[{19A352FE-D90D-424E-B85F-CF3B8E98CF0E}];
                                                                ParentNodeID=[{19A352FE-D90D-424E-B85F-CF3B8E98CF0E}];
                                                                ShortcutURL=navision:/ /client/run?target=Form%2050010%26MYVARIABLE=3;
                                                                Visible=Yes;
                                                                NextNodeID=[{00000000-0000-0000-0000-000000000000}] }
  }
}

I have added three shortcuts in the Administration menu of the navigation pane:

1) Run Form One - Simply runs form 50000 (a text box using a variable as a counter) using form 50005 (An update button) as a subform. Whenever the "Update" button in the subform is clicked on, form 50000 adds one to the value in the text box.

2) Run Form Two - Simply runs form 50001 (a text box using a variable as a counter) using form 50005 (An update button) as a subform. Whenever the "Update" button in the subform is clicked on, form 50000 subtracts one to the value in the text box.

3) Run Form Three - Runs form 50003, which has the text box with the counter but no subform. Clicking on the run button with launch form 50005 as a different form (you can run multiple instances) which will update the 50003 to add one to its number.

From the object designer, you can run form 500010 multiple times, each works independently, you can only launch them one at a time from a menu in the navigation pane.


Is this useful? As I said, it's only a basic implementation of a basic idea, it's not supposed to be a plug-in solution. It's real world use would require a bit of thought.

Does anyone have any suggestions? They would me most appreciated.

I'm sure with a little work this mechanism could turn out to be quite powerful.

NB. It's coded in 4.00.03

Hope it's useful. 8-[

Comments

  • MTCMTC Member Posts: 159
    Oh, you need to put a space between the two slashes before you import:

    "navision://"

    to

    "navision:/ /"

    otherwise it takes it as a comment. Then delete the space in the Nav Pane Designer.

    I've added the space into the original code text export.
  • MTCMTC Member Posts: 159
    Surprised nobody seems to be even remotely interested in this. :-k

    This question has been asked so many times, with the previous solutions very poor indeed.

    With this you can not only get a sub-form to directly update its parent/header at will but also build seriously powerful user interfaces.

    Ah well, maybe I should get a moderator to delete this thread and keep this little gem all to myself. :lol:
  • krikikriki Member, Moderator Posts: 9,094
    Hooo, give us a little time to investigate it.
    With the Easter-weekend, I don't think a lot of people were still thinking about Navision or at least not eager to dive into your code (I tried Friday but quickly decided to look at it again AFTER the weekend :roll: )
    Now I got the overall idea.

    It does solve the ontimer problem for sending data from the subform to the mainform.
    The negative is that it includes some programming.

    For improving your system: wouldn't it be better to use a single instance codeunit? This you initialize at the open of a company and it remains open from then on. So when you need to open a form, you can just call a function in the codeunit from another object that after the call you can close again.
    Regards,Alain Krikilion
    No PM,please use the forum. || May the <SOLVED>-attribute be in your title!


  • krikikriki Member, Moderator Posts: 9,094
    [Topic moved from Navision forum to Navision Tips & Tricks forum]
    Regards,Alain Krikilion
    No PM,please use the forum. || May the <SOLVED>-attribute be in your title!


  • MTCMTC Member Posts: 159
    kriki wrote:
    For improving your system: wouldn't it be better to use a single instance codeunit? This you initialize at the open of a company and it remains open from then on. So when you need to open a form, you can just call a function in the codeunit from another object that after the call you can close again.

    You could do, yes, I posted this to try to get some feedback on the best way to configure the system. As I said, it's just an idea I had on how to exploit the passing of a codeunit used as a wrapper reference to forms.

    I've been playing with it a little more, you can get subforms to directly update other subforms, it's quite powerful. I'd just like people to have a play with it and give their opinions on the best way to configure it.

    The only problem with it that I cannot work out at the moment (the way I have it configured) is that when running, for some reason, you cannot hide or show the Navigation Panel, but you can when the form is closed. :-k

    If anyone has an idea of why this maybe the case, it would be much appreciated.
  • krikikriki Member, Moderator Posts: 9,094
    MTC wrote:
    kriki wrote:
    The only problem with it that I cannot work out at the moment (the way I have it configured) is that when running, for some reason, you cannot hide or show the Navigation Panel, but you can when the form is closed. :-k
    I also noticed that at some moments the Navigation Panel is blocked. I haven't yet understood the mechanism of this.
    Regards,Alain Krikilion
    No PM,please use the forum. || May the <SOLVED>-attribute be in your title!


  • MTCMTC Member Posts: 159
    The strange thing is, it exhibits this behaviour even when FormLauncher is run from the Object Designer.

    It doesn't lock the object designer, it doesn't lock the Navigation Panel, it just disables the ability to show/hide the Navigation Panel.

    I will continue to try and find the solution. It has something to do with the object FormLauncher.
  • krikikriki Member, Moderator Posts: 9,094
    In some other post (don't remember where), I read that might be because of a RUNMODAL-statement.
    Regards,Alain Krikilion
    No PM,please use the forum. || May the <SOLVED>-attribute be in your title!


  • MTCMTC Member Posts: 159
    Yes, it is. Any form running modally stops the user from hiding or showing the Navigation Pane.
Sign In or Register to comment.