Charts without PowerBI!

In this video, I show how to create charts inside Business Central. It has become a standard answer in the channel “ Just use PowerBI ” but Microsoft Dynamics 365 Business Central has a powerful charting tool and it’s pretty easy to use.

https://youtu.be/kRa-CUQchJk

As usual, the source is available on github.com/hougaard


In this video, Erik demonstrates how to create charts directly within Business Central using AL code — no Power BI required. While Power BI is a great tool and often the default recommendation for data visualization, Business Central has a built-in chart control that can produce attractive, interactive charts with just a few lines of code. Erik walks through the entire process from scratch, building a chart that visualizes customer sales and profit data.

Why Build Charts Without Power BI?

It’s become almost a default answer in the Business Central community: whenever you need to visualize something, use Power BI. And while there’s nothing wrong with external tools, in many cases you simply want to visualize data that already exists inside the application you’re working in. Business Central ships with a built-in chart control that’s surprisingly capable and performant — Erik mentions using it in production at his day job at eFocus for a project management dashboard with hundreds of data points, and it runs snappily.

Setting Up the Page

To get started, you need a page that hosts the chart control. Erik creates a Card page, which gives the chart plenty of space. You could also use a CardPart page type if you wanted to embed the chart into a FactBox area on another page.

The key ingredient is a usercontrol that references Microsoft’s built-in Business Chart add-in:

usercontrol(Chart; "Microsoft.Dynamics.Nav.Client.BusinessChart")
{
    ApplicationArea = All;
}

This control comes from Microsoft out of the box — there’s no need to build a custom add-in. When you deploy the page at this point, you’ll get an empty screen, which makes sense since we haven’t fed the chart any data yet.

The Business Chart Buffer

The secret sauce behind the chart is a table called “Business Chart Buffer”. This temporary record acts as the data model for your chart. All the chart configuration — measures, axes, values — flows through this buffer. Here’s the overall pattern:

  1. Initialize the buffer
  2. Add measures (the data series you want to display)
  3. Define the X-axis
  4. Loop through your data, adding columns and setting values
  5. Update the chart control

Building the Chart Step by Step

Initialization and Measures

The first step is to initialize the buffer, then add one or more measures. Each measure represents a data series in your chart. The AddMeasure function takes a caption, a value parameter (more on that in a moment), a data type, and a chart type:

Buffer.Initialize();

// Index 0
Buffer.AddMeasure('Sales', 1, Buffer."Data Type"::Decimal, Buffer."Chart Type"::Column);

// Index 1
Buffer.AddMeasure('Profit', 1, Buffer."Data Type"::Decimal, Buffer."Chart Type"::Pie);

A note on the second parameter to AddMeasure: the documentation calls it something like “value index” or “index value,” and it accepts a variant — you can put anything in there. Erik hasn’t been able to figure out what it actually does; it gets stored in the Business Chart Map table but doesn’t appear to be used for anything meaningful. Putting 1 there seems to be the most common convention.

Defining the X-Axis

Next, define what the X-axis represents:

Buffer.SetXAxis('Customer', Buffer."Data Type"::String);

Since we’re showing customer names, the data type is String.

Populating Data

Now loop through customers, calculate their flow fields, and feed the data into the buffer:

if Customer.FindSet(false, false) then
    repeat
        Customer.CalcFields("Sales (LCY)", "Profit (LCY)");
        if Customer."Sales (LCY)" <> 0 then begin
            Buffer.AddColumn(Customer.Name);
            Buffer.SetValueByIndex(0, i, Customer."Sales (LCY)");
            Buffer.SetValueByIndex(1, i, Customer."Profit (LCY)");
            i += 1;
        end;
    until Customer.Next() = 0;

A few important things to note here:

  • Filtering out zero-sales customers keeps noise out of the chart.
  • AddColumn adds a new entry on the X-axis. The naming is a bit confusing because “column” here refers to a data point in the chart, and we also happen to be using the Column chart type (which renders as bars).
  • SetValueByIndex takes three parameters: the measure index, the X-axis index, and the value.

Zero-Based Indexing — A Quirk

Here’s something that catches people off guard: while almost everything in Business Central is 1-based (including the .NET-adopted collections like Dictionaries and Lists in AL), the SetValueByIndex function uses zero-based indexing for both the measure index and the X-axis index. That’s why the counter variable i starts at 0, and the first measure is referenced as index 0, the second as index 1.

Updating the Chart

Finally, push the buffer data to the chart control:

Buffer.Update(CurrPage.Chart);

The Complete Source Code

Here’s the full page object bringing it all together:

page 50132 "Test Chart"
{
    PageType = Card;
    Caption = 'Test Chart';
    UsageCategory = Administration;
    ApplicationArea = All;
    layout
    {
        area(Content)
        {
            usercontrol(Chart; "Microsoft.Dynamics.Nav.Client.BusinessChart")
            {
                ApplicationArea = All;
                trigger DataPointClicked(point: JsonObject)
                var
                    JsonTxt: Text;
                begin
                    point.WriteTo(JsonTxt);
                    Message('%1', JsonTxt);
                end;

                trigger AddInReady()
                var
                    Buffer: Record "Business Chart Buffer" temporary;
                    Customer: Record Customer;
                    i: Integer;
                begin
                    Buffer.Initialize();

                    // Index 0
                    Buffer.AddMeasure('Sales', 1, Buffer."Data Type"::Decimal, Buffer."Chart Type"::Column);

                    // Index 1
                    Buffer.AddMeasure('Profit', 1, Buffer."Data Type"::Decimal, Buffer."Chart Type"::Pie);

                    Buffer.SetXAxis('Customer', Buffer."Data Type"::String);

                    if Customer.FindSet(false, false) then
                        repeat
                            Customer.CalcFields("Sales (LCY)", "Profit (LCY)");
                            if Customer."Sales (LCY)" <> 0 then begin
                                Buffer.AddColumn(Customer.Name);
                                Buffer.SetValueByIndex(0, i, Customer."Sales (LCY)");
                                Buffer.SetValueByIndex(1, i, Customer."Profit (LCY)");
                                i += 1;
                            end;
                        until Customer.Next() = 0;

                    Buffer.Update(CurrPage.Chart);
                end;
            }
        }
    }
}

Chart Types You Can Use

The "Chart Type" option on the buffer gives you a variety of visualization styles. Each measure can have its own chart type, so you can mix and match within the same chart:

  • Column — standard bar chart (yes, “bar” is called “column” here)
  • Line — line chart, great for trends
  • Pie — classic pie chart
  • Doughnut — like a pie chart but with a hole in the middle
  • Bubble — bubble chart (though this one can break depending on your data)
  • And more…

Erik demonstrates combining Column and Line types for sales and profit respectively — a pattern you’ll see in Microsoft’s own cash flow charts. He also experiments with mixing Column and Doughnut, which overlays the two chart types on top of each other. Not every combination makes visual sense, but the flexibility is there.

Handling Click Events

The chart control exposes three triggers: AddInReady, DataPointClicked, and Refresh. The DataPointClicked trigger fires when a user clicks on a data point and receives a JsonObject containing information about what was clicked:

trigger DataPointClicked(point: JsonObject)
var
    JsonTxt: Text;
begin
    point.WriteTo(JsonTxt);
    Message('%1', JsonTxt);
end;

The JSON payload includes the measure name (e.g., “Sales”), the label of the clicked column (e.g., “Datum Corporation”), and the Y-value (e.g., 335,000). A quick note on JSON direction: when you want to get data out of a JsonObject, you use WriteTo; when you want to put data into one, you use ReadFrom.

In a real-world scenario, Erik recommends building a dictionary behind the scenes that maps column labels to meaningful identifiers like customer numbers, so you can easily react to clicks and navigate to the relevant record.

Summary

Creating charts in Business Central without Power BI is straightforward and requires surprisingly little code. The key components are:

  • The Microsoft.Dynamics.Nav.Client.BusinessChart user control for rendering
  • The Business Chart Buffer temporary record for defining and populating chart data
  • A simple pattern: initialize → add measures → set X-axis → loop and add data → update

Watch out for the zero-based indexing in SetValueByIndex and the slightly confusing naming conventions (columns vs. columns, anyone?), but once you get past those quirks, you have a powerful, high-performance charting capability built right into Business Central. Whether you embed it in a Card page, a CardPart in a FactBox, or anywhere else, it’s a great option for visualizing data without leaving the application.