2 C/AL questions

FishermanFisherman Member Posts: 456
I have two questions that I haven't been able to find answers to.

1. Given a FieldRef that has a RELATION <> 0, is it possible to either find or dynamically run the default form for the table defined by the relation? For example, if I got a fieldref to the "Item No." field in Item Ledger Entry, is there some way that I can dynamically run the Item Card, as it is the default form for the Item table?

2. Given a subform, how can I allow users to select specific records within that subform and run some action that is hosted in the parent form (like a codeunit or report)? How do I determine which records they selected?

Comments

  • gerdhuebnergerdhuebner Member Posts: 155
    edited 2009-06-17
    1. If you speak of a "default form", how is it defined? The only form IDs, to my knowledge, which may be defined in a table object are through its properties LookUpFormID and DrillDownFormID. This would mean in the case of the Item table, the "default form" would be "Item List". As far as I know, there is no (easy) way to get this (or these) properties extracted from the table. (One possible way could be to stream the table object's BLOB and analyse the fob structure in order to extract the property). The only way to run a form dynamically may be with the HYPERLINK command (provided, you could retrieve the corresponding form ID).
    See also http://www.mibuso.com/forum/viewtopic.php?f=23&t=35084

    2. One way (and possibly the only one) could be to implement the OnTimer trigger in the main form which can periodically check if there is some request initiated by the subform.
  • DenSterDenSter Member Posts: 8,304
    1 - To run the table's default list form (the one specified in the table's LookupFormID property), you call FORM.RUN with form ID 0 (zero), and the rec variable. What I don't know is whether that works for RecRef variables. With 'Item' as a record variable based on the item table, you'd call its standard form like this:
    FORM.RUN(0,Item);
    
    2 - You specify the action on the parent form, and you call a method on the subform. You'd have to create a function on the subform that captures and returns the selected records (hint: SETSELECTIONFILTER, look it up in the C/SIDE Reference Guide), and you can call this function from the main form. It's pretty difficult to explain in here like this, but the standard NAV document forms have tons of good examples to steal from. For an example, take a look at the Line button on the standard Sales Order form, and how it interacts with the subform.
  • FishermanFisherman Member Posts: 456
    Den -

    That's awesome. I really appreciate the help there.

    I'll look at the SETSELECTIONFILTER() method. I tried running through the help, but sometimes, when you don't know where to start, it's like a needle in the proverbial haystack.
  • DenSterDenSter Member Posts: 8,304
    No problem, always glad to be able to help :mrgreen:. If I read you right you're one of the people in here that enjoys figuring these things out with a push in the right direction. Let us know if/when you figure out a solution.
  • kinekine Member Posts: 12,562
    DenSter wrote:
    1 - To run the table's default list form (the one specified in the table's LookupFormID property), you call FORM.RUN with form ID 0 (zero), and the rec variable. What I don't know is whether that works for RecRef variables. With 'Item' as a record variable based on the item table, you'd call its standard form like this:
    FORM.RUN(0,Item);
    

    Unfortunately the way with RecordRef is not working... I already requested this but there is no possibility to pass RecordRef to form through standard way, else this will be very good solution for many cases...
    Kamil Sacek
    MVP - Dynamics NAV
    My BLOG
    NAVERTICA a.s.
  • FishermanFisherman Member Posts: 456
    Yeah - I figured that out yesterday.

    I wish NAV provided more support for on-the-fly object typing. The FieldRef and RecordRef are really useful, up to this point.

    I hit the wall on this yesterday, when I remembered that to use the form specified by the LookupFormID as a lookup, I would have to pass in a record variable to capture the lookup result. That doesn't work if I don't know ahead of time what type of record variable it might be.

    I was hoping to do something like this:
    IF (FORM.RUNMODAL(0,RecordRef) = ACTION::LOOKUPOK) THEN...
    

    but it doesn't work... :(
  • FishermanFisherman Member Posts: 456
    Just so you guys can know what I'm working on...

    My company needs to be able to produce barcodes out of NAV, but the problem is that we do a lot of work for external companies, and each customer could potentially have different barcode formats.

    We're looking at a product called Bartender, which functions as a barcode printing/communication server, and are looking to integrate with it through either TCP/IP socket connections or MSMQ and a middle-ware component in .Net.

    I've written a series of tables and a codeunit to function as a barcode printing engine that will take an input record, run it through a series of value/filter-based rules, evaluate which type of "usage" is involved (Production order for customer X, shipment for customer Y, etc...), and run a codeunit that is defined (by ID) as part of that "usage". The system has a table that contains all of the fields that we currently use in barcoding (item number, vendor, lot. serial, etc...), and the Usage codeunits are responsible for taking the source data and mapping it into this table. When the user wants to print the barcode, it takes those transformed records and matches them up against another table, which defines which of those fields will map into which of the bartender barcode fields, creates an XML document, and streams the XML across the port to bartender to print.

    The reason I'm asking these questions is that these rules allow us to select a field in the table that is defined for that usage and set either a static value that will be used to evaluate, or a filter value (If that rule evaluates to true, then it moves on to the next rule, if not, it moves to the next usage/ruleset). I'm using FieldRefs and RecordRefs pretty heavily to perform some validation on the data when the rules are set up (can't enter a character as the check value for a field that is defined as decimal, etc...), and even to present option values on the fly (I had to use STRMENU() to show the defined option values that are available).

    I was going to try to dynamically show parent records for fields that were relational, but maybe I'll have to forgo that.

    Thanks.
  • kinekine Member Posts: 12,562
    I already did something "similar" (selecting values from tables on dynamics base) and I already somewhere described the solution on this forum. But it worked only with tables which have Primary key with one field, and when I want to show form with PK and description field from the table. In this case you can use form over some temp. table having 2 fields and you can map this temporarily to any table through recordref by using the OnNextRecord and OnFindRecord triggers.
    Kamil Sacek
    MVP - Dynamics NAV
    My BLOG
    NAVERTICA a.s.
  • FishermanFisherman Member Posts: 456
    Den -

    I wonder if you'd be willing to answer one more for me.

    I've tried this in the subform:
    GetSelectedRecords(VAR Record "Some Table")
    CurrForm.SETSELECTIONFILTER(Record);
    Record.FIND('-');
    

    and then, in the header form, I call it like so:
    CurrForm.Subform.FORM.GetSelectedRecords(RecordToPass);
    

    I'm using generic names here, but you get the point.

    When this function is called when NO selections have been made it still returns 1 record, which I don't understand. If I haven't selected a record, then why am I getting one out of the filter? Do you see anything glaringly wrong here, or am I misunderstanding/misusing this function?
  • FishermanFisherman Member Posts: 456
    Den -

    Nevermind. The help just misled me here a bit.

    I placed a call to "Record.MARKEDONLY(TRUE);" after the Record.FIND('-'); and it is working as expected.

    Thanks again!
  • gerdhuebnergerdhuebner Member Posts: 155
    Never mind.
    MARKEDONLY and SETSELECTIONFILTER are two commands that have nothing in common.
    With MARKEDONLY(TRUE) you will get all records that have been previously marked. To mark a record, you can press Ctrl+F1, e.g., or you can do it from C/AL with Record.MARK(TRUE). Marked records appear with a characteristic dot to the left of the record in a tabular form:

    With SETSELECTIONFILTER you will get all records that have been previously selected. You can select a record / several records basically the same way, you highlight text in a text editor, e.g. (holding down the mouse button and draw the mouse over the records, additionally holding down the Shift or Ctrl-Button to select a block of records, or several records, respectively. As a result, the selected records always appear with a blue background (except one case: the current record is always selected, even if it is not blue):
  • DenSterDenSter Member Posts: 8,304
    Fisherman wrote:
    When this function is called when NO selections have been made it still returns 1 record, which I don't understand. If I haven't selected a record, then why am I getting one out of the filter? Do you see anything glaringly wrong here, or am I misunderstanding/misusing this function?
    NAV considers the record that the cursor is in as 'selected' even when you don't highlight the whole record, and that's why SETSELECTIONFILTER always returns at least one record.
  • FishermanFisherman Member Posts: 456
    Never mind.
    MARKEDONLY and SETSELECTIONFILTER are two commands that have nothing in common.
    With MARKEDONLY(TRUE) you will get all records that have been previously marked. To mark a record, you can press Ctrl+F1, e.g., or you can do it from C/AL with Record.MARK(TRUE). Marked records appear with a characteristic dot to the left of the record in a tabular form:

    With SETSELECTIONFILTER you will get all records that have been previously selected. You can select a record / several records basically the same way, you highlight text in a text editor, e.g. (holding down the mouse button and draw the mouse over the records, additionally holding down the Shift or Ctrl-Button to select a block of records, or several records, respectively. As a result, the selected records always appear with a blue background (except one case: the current record is always selected, even if it is not blue):

    gerd -

    Not sure I follow you. This is from the NAV C/SIDE online help...
    SETSELECTIONFILTER (Form)
    Use this function to have the system note the records the user has selected on the form, mark those records in the table specified, and set the filter to "marked only".
    
    CurrForm.SETSELECTIONFILTER(Record)
    Record
    
    Data type: record
    
    A specific record or records.
    
    Comments
    If either all or no records are selected, marks will not be used. 
    

    The way I'm reading this, it is saying that SETSELECTIONFILTER results in MARKED records based on what was selected on-screen.
  • gerdhuebnergerdhuebner Member Posts: 155
    Fisherman wrote:
    ...The way I'm reading this, it is saying that SETSELECTIONFILTER results in MARKED records based on what was selected on-screen.
    Yes, I aggree. What I wanted to say basically is that it makes a difference, using MARKEDONLY or (xor) SETSELECTIONFILTER in the two pictures above. In the first picture, where two records are marked, MARKEDONLY(TRUE) will filter the two marked records, while SETSELCTIONFILTER will filter only the current record. In the second picture, MARKEDONLY(TRUE) (without using SETSELECTIONFILTER before) will filter nothing and SETSELECTIONFILTER will filter the two selected records. On the other hand, if you are using SETSELECTIONFILTER, you don't need a following MARKEDONLY(TRUE) anymore, because this is done (according to online help) automatically. Therefore I was a little bit disturbed about your solution, where you said:
    Fisherman wrote:
    I placed a call to "Record.MARKEDONLY(TRUE);" after the Record.FIND('-'); and it is working as expected.
    Here is a little sample for testing (without the use of MARKEDONLY), which uses Form 50000 and 50001:
    OBJECT Form 50000 Test Main
    {
      OBJECT-PROPERTIES
      {
        Date=18.06.09;
        Time=08:54:55;
        Modified=Yes;
        Version List=;
      }
      PROPERTIES
      {
        Width=9790;
        Height=7810;
        Editable=No;
        SourceTable=Table36;
      }
      CONTROLS
      {
        { 1   ;Frame        ;220  ;220  ;9350 ;880  ;HorzGlue=Both;
                                                     VertGlue=Both;
                                                     ShowCaption=No }
        { 2   ;TextBox      ;3850 ;440  ;2750 ;440  ;ParentControl=1;
                                                     InFrame=Yes;
                                                     SourceExpr="No." }
        { 3   ;Label        ;440  ;440  ;3300 ;440  ;ParentControl=2 }
        { 4   ;CommandButton;7370 ;7040 ;2200 ;550  ;HorzGlue=Right;
                                                     VertGlue=Bottom;
                                                     PushAction=FormHelp }
        { 1000000000;SubForm;220  ;1320 ;9350 ;5500 ;Name=SubFormSalesLine;
                                                     HorzGlue=Both;
                                                     VertGlue=Both;
                                                     SubFormID=Form50001;
                                                     SubFormView=SORTING(Document Type,Document No.,Line No.);
                                                     SubFormLink=Document Type=FIELD(Document Type),
                                                                 Document No.=FIELD(No.) }
        { 1000000001;MenuButton;4950;7040;2200;550  ;HorzGlue=Right;
                                                     VertGlue=Bottom;
                                                     CaptionML=DEU=F&unction;
                                                     Menu=MENUITEMS
                                                     {
                                                       { ID=1000000002;
                                                         CaptionML=DEU=&Check Selection;
                                                         OnPush=BEGIN
                                                                  CurrForm.SubFormSalesLine.FORM.GetSelectionFilter(SalesLine);
                                                                  MESSAGE('%1 record(s) selected.',SalesLine.COUNT);
                                                                END;
                                                                 }
                                                     }
                                                      }
      }
      CODE
      {
        VAR
          SalesLine@1000000000 : Record 37;
    
        BEGIN
        END.
      }
    }
    
    OBJECT Form 50001 Test Sub
    {
      OBJECT-PROPERTIES
      {
        Date=18.06.09;
        Time=08:55:13;
        Modified=Yes;
        Version List=;
      }
      PROPERTIES
      {
        Width=9350;
        Height=5500;
        Editable=No;
        TableBoxID=1000000000;
        SourceTable=Table37;
      }
      CONTROLS
      {
        { 1000000000;TableBox;0   ;0    ;9350 ;5500 ;HorzGlue=Both;
                                                     VertGlue=Both }
        { 1000000001;TextBox;0    ;0    ;1700 ;0    ;ParentControl=1000000000;
                                                     InColumn=Yes;
                                                     SourceExpr="Line No." }
        { 1000000002;Label  ;0    ;0    ;0    ;0    ;ParentControl=1000000001;
                                                     InColumnHeading=Yes }
        { 1000000003;TextBox;1535 ;2200 ;550  ;440  ;ParentControl=1000000000;
                                                     InColumn=Yes;
                                                     SourceExpr=Type }
        { 1000000004;Label  ;0    ;0    ;0    ;0    ;ParentControl=1000000003;
                                                     InColumnHeading=Yes }
        { 1000000005;TextBox;2249 ;2200 ;2310 ;440  ;HorzGlue=Both;
                                                     ParentControl=1000000000;
                                                     InColumn=Yes;
                                                     SourceExpr="No." }
        { 1000000006;Label  ;0    ;0    ;0    ;0    ;ParentControl=1000000005;
                                                     InColumnHeading=Yes }
        { 1000000007;TextBox;4895 ;2750 ;2200 ;440  ;ParentControl=1000000000;
                                                     InColumn=Yes;
                                                     SourceExpr=Quantity }
        { 1000000008;Label  ;0    ;0    ;0    ;0    ;ParentControl=1000000007;
                                                     InColumnHeading=Yes }
      }
      CODE
      {
    
        PROCEDURE GetSelectionFilter@1000000000(VAR p_SalesLine@1000000000 : Record 37);
        BEGIN
          CurrForm.SETSELECTIONFILTER(p_SalesLine);
        END;
    
        BEGIN
        END.
      }
    }
    
  • FishermanFisherman Member Posts: 456
    gerd -

    Thanks for the sample. This is almost the exact setup that I originally had, and I could see the count of selected records, but the actual record variable was empty when it came back to the caller.

    When I called MARKEDONLY() and FIND('-') after SETSELECTIONFILTER, I was able to see the records, but neither call by itself gave me the desired results.
  • gerdhuebnergerdhuebner Member Posts: 155
    Fisherman wrote:
    ..but the actual record variable was empty when it came back to the caller.
    When I called MARKEDONLY() and FIND('-') after SETSELECTIONFILTER, I was able to see the records, but neither call by itself gave me the desired results.
    Fisherman - It is not surprising, that the record variable is empty after the call, because it is neither filled prior to the call nor within the subform function (only filters and marks are applied there).
    So the FIND is quite necessary (normally it is done in the main form). But - as I try to point out - the MARKEDONLY(TRUE) should be completely unnecessary (please see the example below).
    May be it's version dependent. I tried it within NAV 5.0 (SP1) - What version are you on?
    OBJECT Form 50000 Test Main
    {
      OBJECT-PROPERTIES
      {
        Date=18.06.09;
        Time=15:37:58;
        Modified=Yes;
        Version List=;
      }
      PROPERTIES
      {
        Width=9790;
        Height=7810;
        Editable=No;
        SourceTable=Table36;
      }
      CONTROLS
      {
        { 1   ;Frame        ;220  ;220  ;9350 ;880  ;HorzGlue=Both;
                                                     ShowCaption=No }
        { 2   ;TextBox      ;3850 ;440  ;2750 ;440  ;ParentControl=1;
                                                     InFrame=Yes;
                                                     SourceExpr="No." }
        { 3   ;Label        ;440  ;440  ;3300 ;440  ;ParentControl=2 }
        { 4   ;CommandButton;7370 ;7040 ;2200 ;550  ;HorzGlue=Right;
                                                     VertGlue=Bottom;
                                                     PushAction=FormHelp }
        { 1000000000;SubForm;220  ;1320 ;9350 ;5500 ;Name=SubFormSalesLine;
                                                     HorzGlue=Both;
                                                     VertGlue=Both;
                                                     SubFormID=Form50001;
                                                     SubFormView=SORTING(Document Type,Document No.,Line No.);
                                                     SubFormLink=Document Type=FIELD(Document Type),
                                                                 Document No.=FIELD(No.) }
        { 1000000001;MenuButton;4950;7040;2200;550  ;HorzGlue=Right;
                                                     VertGlue=Bottom;
                                                     CaptionML=DEU=F&unction;
                                                     Menu=MENUITEMS
                                                     {
                                                       { ID=1000000002;
                                                         CaptionML=DEU=&Check Selection;
                                                         OnPush=VAR
                                                                  l_lineNumbers@1000000001 : Text[30];
                                                                BEGIN
                                                                  CurrForm.SubFormSalesLine.FORM.GetSelectionFilter(SalesLine);
    
                                                                  IF SalesLine.FIND('-') THEN
                                                                    REPEAT
                                                                      IF l_lineNumbers <> '' THEN
                                                                        l_lineNumbers := l_lineNumbers + ', ';
                                                                      l_lineNumbers := l_lineNumbers + FORMAT(SalesLine."Line No.");
                                                                    UNTIL SalesLine.NEXT = 0;
    
                                                                  MESSAGE('Line numbers selected: %1',l_lineNumbers);
                                                                END;
                                                                 }
                                                     }
                                                      }
      }
      CODE
      {
        VAR
          SalesLine@1000000000 : Record 37;
    
        BEGIN
        END.
      }
    }
    
    OBJECT Form 50001 Test Sub
    {
      OBJECT-PROPERTIES
      {
        Date=18.06.09;
        Time=08:55:13;
        Modified=Yes;
        Version List=;
      }
      PROPERTIES
      {
        Width=9350;
        Height=5500;
        Editable=No;
        TableBoxID=1000000000;
        SourceTable=Table37;
      }
      CONTROLS
      {
        { 1000000000;TableBox;0   ;0    ;9350 ;5500 ;HorzGlue=Both;
                                                     VertGlue=Both }
        { 1000000001;TextBox;0    ;0    ;1700 ;0    ;ParentControl=1000000000;
                                                     InColumn=Yes;
                                                     SourceExpr="Line No." }
        { 1000000002;Label  ;0    ;0    ;0    ;0    ;ParentControl=1000000001;
                                                     InColumnHeading=Yes }
        { 1000000003;TextBox;1535 ;2200 ;550  ;440  ;ParentControl=1000000000;
                                                     InColumn=Yes;
                                                     SourceExpr=Type }
        { 1000000004;Label  ;0    ;0    ;0    ;0    ;ParentControl=1000000003;
                                                     InColumnHeading=Yes }
        { 1000000005;TextBox;2249 ;2200 ;2310 ;440  ;HorzGlue=Both;
                                                     ParentControl=1000000000;
                                                     InColumn=Yes;
                                                     SourceExpr="No." }
        { 1000000006;Label  ;0    ;0    ;0    ;0    ;ParentControl=1000000005;
                                                     InColumnHeading=Yes }
        { 1000000007;TextBox;4895 ;2750 ;2200 ;440  ;ParentControl=1000000000;
                                                     InColumn=Yes;
                                                     SourceExpr=Quantity }
        { 1000000008;Label  ;0    ;0    ;0    ;0    ;ParentControl=1000000007;
                                                     InColumnHeading=Yes }
      }
      CODE
      {
    
        PROCEDURE GetSelectionFilter@1000000000(VAR p_SalesLine@1000000000 : Record 37);
        BEGIN
          CurrForm.SETSELECTIONFILTER(p_SalesLine);
        END;
    
        BEGIN
        END.
      }
    }
    
Sign In or Register to comment.