Optimized Progress Dialogues in C/AL

by Dec 20, 2018C/AL Language

Home 9 Development 9 C/AL Language 9 Optimized Progress Dialogues in C/AL

In the previous articles (see here) we discussed how to build standard progress dialogue in C/AL language. However, as mentioned at the end of the article, this way is definitely not applicable to a production environment due to the performance issues. In this article, we will look at how to build dialogues more efficiently with lower (and usually acceptable) impact on performance…

Let’s look at our example from the previous article.

 Local variables: 
   DialogVar (Dialog)
   MyRecord (Record of any table)
 --------------------
 Code:
   DialogVar.OPEN('My own text');
   REPEAT
     DialogVar.UPDATE();
   UNTIL MyRecord.NEXT < 1
   DialogVar.CLOSE();

What are the biggest drawbacks of this design? Definitely, one of them is performance. However, usually, the user needs (or want) to see more information about the progress. Using this code, the user sees only static text “My own text” and has no information about the current state of progress. Let’s look at both issues in more detail.

Progress Information

To show more details about current progress, we have two possible ways. One of them is to show custom string values (for example, document number, customer number, …). Another possibility is to show progress in percentage to indicate how many cycles (in comparison to the total number of cycles) has been done/remains.

To specify what should be shown and how the progress should be displayed, we use placeholders. Placeholders are special characters that are placed in the text we want to show in the position, where the value should be shown. The number of placeholder characters specifies the length of the variable field.

We have two types of placeholders. For custom string values, we use “#”, for percentage, we use “@”. In one text label, more values can be shown. To achieve it, the placeholders must be indexed. The index must be defined as a second character just after the first placeholder.

For example:

  • #1##########
    • First variable field that shows custom strings.
  • @2@@@@@@@
    • Second variable field that shows percentage progress dialogue.
    • The percentages are defined as values 0 to 9999. That means that to display a percentage progress indicator properly, we need to normalize the value to this range.

Once we have text with placeholders defined, we can change our UPDATE function usage and add parameters. As we discussed in the first article, the first parameter is the index of placeholder and the second one value. Let’s look at the example below.

 Local variables: 
   DialogVar (Dialog)
   MyRecord (Record of any table)
   Counter (Integer)
   TotalCounter (Integer)
 --------------------
 Code:
   Counter := 1;
   TotalCounter := MyRecord.COUNT();
   DialogVar.OPEN('Current doc. no #1####### \ Current progress @2@@@@@@');
   REPEAT
     DialogVar.UPDATE(1, MyRecord."Document No.");
     DialogVar.UPDATE(2, (Counter / TotalCounter * 10000) DIV 1);

     Counter += 1;
   UNTIL MyRecord.NEXT < 1
   DialogVar.CLOSE();

As mentioned above, the value of the percentage indicator has to be normalized to range 0 to 9999. In the example above, we did it using “(Counter / TotalCounter * 10000) DIV 1”. This statement calculates how many cycles have been done (in per cent), multiple it by 10000 to normalize it within 0-9999 and cast it as an integer value.

Performance

The biggest issue from performance point of view is the complexity of UPDATE method. Even if the indicator should shown the same value, the system still needs to update the UI. The update of UI makes UI responsible (so the user does not see unresponding application) but it is not necessary to update the dialog each time the function is run.

So, we have two ways how to solve it. We can update the dialog only each Xth run (for example every 100th run). I used it in the past, however, I found that it is not ideal solution. The biggest drawback is that for tasks where one run took a longer time, the application can still be unresponding.

Better approach, in my opinion, is to update UI every second. See example below.

 Local variables: 
   DialogVar (Dialog)
   MyRecord (Record of any table)
   Counter (Integer)
   TotalCounter (Integer)
   LastUpdateAt (DateTime)
 --------------------
 Code:
   Counter := 1;
   TotalCounter := MyRecord.COUNT();
   DialogVar.OPEN('Current doc. no #1####### \ Current progress @2@@@@@@');
   REPEAT
     IF LastUpdateAt < (CURRENTDATETIME() - 1000) THEN BEGIN
       LastUpdateAt := CURRENTDATETIME();
       DialogVar.UPDATE(1, MyRecord."Document No.");
       DialogVar.UPDATE(2, (Counter / TotalCounter * 10000) DIV 1);
     END;

     Counter += 1;
   UNTIL MyRecord.NEXT < 1
   DialogVar.CLOSE();

Recent Articles from the category

C/AL + AL: SetAutoCalcFields

SetAutoCalcFields is very similar method to CalcFields method. The only difference is that CalcField is run on the records already loaded from the database. On the other hand, SetAutoCalcFields is set before the records are loaded from the database. Although it can be...

read more

C/AL + AL: CalcFields

CalcFields is a method to calculate the FlowFields in a record. Flowfields are special fields (virtual) that are not stored physically in the database/table. Standard fields are loaded once the record is retrieved from the database. However, as the flowfields are...

read more

Progress Dialogues in C/AL

One of the most neglected things I found on projects I have reviewed is progress dialogue for tasks, that run for more than a seconds. Although it is not always easy to estimate how long the task will run on production data, it is necessary to use progress dialogue as...

read more

Sign Up for News

Certifications

Highest certification
Microsoft Data Management and
also in D365 Business Central

Microsoft Certified: Dynamics 365 Business Central Functional Consultant Associate

See other certifications here