Using Memo BLOB to Enable Huge Text

AUTHOR NOTE: 2021-02-10 – There is a Dynamics 365 Business Central Repo showing how to handle this in Business Central. 

https://github.com/SpareBrainedIdeas/Demo-BigText


I’ll try to update the blog with a new post walking through it, but it’s very functionally the same as below:

Generally speaking, enabling huge text fields in NAV is bad design because it encourages dumping unstructured data into the system. Reason Codes is an example of adding useful, structured data to the system.

But, every once in a while, there’s a legitimate need to write up a large chunk of text. The Notes functionality isn’t bad for that, but try adding that to a Report or a FastTab.

When that need comes along, you can do it natively now using the built-in RTC tools. I’m using a case scenario for a company that performed contractual maintenance tasks for their customers and they needed a free-text place to enter information about the work done each site visit that would then print on a report.

Their interface for it looks like so:

Work Description is their internal notes. Work Performed is their potentially customer facing information. We’ll talk about that. Just to show it off, here’s how it looks as you enter chunks of data:

Table and Functions

First, you need a place for the data to live.

We’ll create it as a BLOB, but there’s a handy SubType we’ll want to set called Memo.

For universal access to the functions we’ll need, two public Functions are just the thing.

SetWorkPerformed sets the value of the blob from a Text (size unlimited) variable using TempBlob’s handy “WriteAsText” function.

GetWorkPerformed does the reverse, returning the raw BLOB text as Text using the TempBlob’s ReadAsText function. That’s it, though you’ll notice we have a couple things in there dealing with Line Feeds and Text Encoding, so you may have to adjust those based on your regional/platform needs.

For easy splicing into your own text objects, here the two functions are in C/AL Text Object format:

    PROCEDURE SetWorkPerformed@1220060013(NewWorkPerformed@1000 : Text);
    VAR
      TempBlob@1001 : TEMPORARY Record 99008535;
    BEGIN
      CLEAR("Work Performed");
      IF NewWorkPerformed = '' THEN
        EXIT;
      TempBlob.Blob := "Work Performed";
      TempBlob.WriteAsText(NewWorkPerformed,TEXTENCODING::Windows);
      "Work Performed" := TempBlob.Blob;
      MODIFY;
    END;

    PROCEDURE GetWorkPerformed@1220060012() : Text;
    VAR
      TempBlob@1000 : TEMPORARY Record 99008535;
      CR@1004 : Text[1];
    BEGIN
      CALCFIELDS("Work Performed");
      IF NOT "Work Performed".HASVALUE THEN
        EXIT('');
      CR[1] := 10;
      TempBlob.Blob := "Work Performed";
      EXIT(TempBlob.ReadAsText(CR,TEXTENCODING::Windows));
    END;

Page

Now, the page is pretty simple.

We need Global Text Variables:

We also need to add the controls to the Page:

To get the layout I showed above, I use nested Groups to add the Captions above the Field, rather then to the left, widening the control to a more TextArea style appearance. Then, the Variable fields get a couple key Properties:

  • MultiLine: Yes
  • ShowCaption: No

We also need our equivalent of Setters and Getters. You can refactor this to Eventing if you want – this customer was on a version that had limited support for it.

First, we have to fetch the values to the Variable. When the page loads a record, we populate the Text variables with our Record’s function.

Then on the Field itself, during OnValidate(), we need to write the info back:

Report

Same as the Page. Use your Getter to load a Text variable, then add that to the Report’s Data model. That simple.

Bear in mind there will still be limits to this field – it’s a BLOB, so you have to jump through hoops to work with the contents. If someone asks if you can filter on it, the answer is “No*”. You could provide a really, really slow MARKED(TRUE); search function that could scan through them, but start with no. If it’s something that needs to be filtered or searched on, it should be structured data.

Hopefully this helps some of you.

5 Comments

  1. Where is the SetWorkPerformed and GetWorkPerformed, this post it not very helpful without those functions

    • Entirely correct! Something must have nixed a couple of key images during a WordPress update I did a little while back.

      I’ve updated the two key missing areas, probably in a better way than I had before.

      Hope that helps!

      • Forgive me for being naive, but I am new to C/AL and NAV development.
        Where are these procedures supposed to go?

        Thanks for the quick update

        • Hmm, Ok, hard to explain in a Blog Comment – limited formatting – but I’ll give it a whirl.

          Each Procedure in this case is a Global Function on the Table. The things in Parenthesis on those lines are the Parameters (C/AL Locals) with their types. The items under the VAR section are the Local Variables for those functions (with their type info). The BEGIN / ENDs in these sections are the code.

          But – word of caution here, since you’re new to development: In NAV (not the modern Business Central versions), to access Code on tables, you have to have either a Partner Development license or work in a company that purchased the relevant development license granules from Microsoft (through your NAV Partner).

          You might also want to check out: https://nav-skills.com/webinars/ since they’re running a wide array of free development training webinars.

  2. Hi Jeremy.. Thanks for sharing the info. I’m using this logic in Sales Line and facing an issue in displaying it in the report. When there is a Line feeding, only the content before line feeding is displayed in the report and rest all is not displayed. Can you share your thoughts to overcome this issue please.

Comments are closed.