Random Procrastination

Darren Gosbell [MVP]

Power BI: How to make the Gantt chart show events in progress

I had a colleague approach me at work with an interesting problem. He had a Power BI report using the Gantt chart custom visual however when he used a date slicer to select a date range it was only showing events which started on that date range and he wanted to see any events that were in progress for that date range.

I figured out a way to get this working and I thought it might be helpful to not just show the solution, but also to walk through the process I used to develop it. Let’s start by looking at an example of the issue he was dealing with.

Given the following data, what he wanted was to filter the data for dates between Mar-20 to Apr-10 so that the Gantt chart would show the section in Yellow below:

But he was getting output like the following, where it was only showing the bottom 3 tasks from the image above with a start date between Mar-20 and Apr-10. It was not showing tasks which were already in progress like the first two (Division… and Functional…).

To figure out what options we had to change this default behaviour I turned on the Performance Profiler in Power BI Desktop (I could also have used the All Queries trace in DAX Studio). This captured the following query for the Gantt visual:

// DAX Query
DEFINE VAR __DS0FilterTable = 
  FILTER(
    KEEPFILTERS(VALUES('Table1'[Start Date])),
    AND('Table1'[Star Date] >= DATE(2016, 3, 20), 'Table1'[Start Date] < DATE(2016, 4, 11))
  )

EVALUATE
  TOPN(
    1002,
    SUMMARIZECOLUMNS(
      'Table1'[Task Name],
      'Table1'[Task ID],
      'Table1'[Start Date],
      'Table1'[Resource],
      __DS0FilterTable,
      "MinEnd_Date", CALCULATE(MIN('Table1'[End Date]))
    ),
    'Table1'[Task ID],
    1,
    'Table1'[Task Name],
    1,
    'Table1'[Start],
    1,
    'Table1'[Resource],
    1
  )

ORDER BY
  'Table1'[Task ID], 'Table1'[Task Name], 'Table1'[Start], 'Table1'[Resource]

There are two important parts to notice from the above query.

First is that the filter is currently explicitly filtering for start dates based on the values selected in the slicer. Obviously this is going to cause an issue as events that are already in-progress will have a start date before the earliest date in the slicer.

To fix this we need to create a separate date table that does not have a relationship to our main fact table. In the demo file I simply created a calculated table using Date Slicer = CALENDARAUTO() but you can use whatever method you like to create this table.

If we replace the 'Table1'[Start Date] field used in the slicer that “fixes” our issue of start dates earlier than those in the slicer being filtered out, but now our slicer is not filtering the data at all, but all is not lost, we will fix that next.

The second interesting thing that I noticed from the captured query is that [Start Date] is being used as a grouping column in the SUMMARIZECOLUMNS() function, but [End Date] is getting the earliest end using CALCULATE(MIN('Table1'[End Date])). What is happening is that the Gantt chart is creating an implied measure when we pass in the [End Date] column. So instead of letting the Gantt chart create an implied measure we can create our own measure and use that instead.

Below is the measure I developed

Gantt End Date = 
VAR _maxDate =
    MAX ( 'Date Slicer'[Date] )
VAR _minDate =
    MIN ( 'Date Slicer'[Date] )
VAR _tasks = VALUES(Table1[Task ID])
RETURN
    MAXX (
        CALCULATETABLE (
            SUMMARIZE( Table1, Table1[Start Date], Table1[End Date] ),
            Table1[Start Date] <= _maxDate,
            Table1[End Date] >= _minDate,
            _tasks
        ),
        Table1[End Date]
    )

This gives us the following

If you look at the output of this measure in a table all it does is the following:

Note that I’ve force the display of all rows by including a simple row count measure. This lets us see that the [Gantt End Date] only returns values where the End date is after the start of the selected date range and the start is before the end of the selected date range, otherwise it returns a blank and SUMMARIZECOLUMNS does not return rows where all the measures return blank.

If you want to look at the Power BI file I used in the screenshots for this post you can download it from here

7 Comments

  1. Genius! Thanks for sharing.

  2. Hi!
    Nice trick.

    There is also another way that solves the problem. In End Date well of Gantt Chart place Table1[End Date] instead of you measure.

    Then add the following measure as a filter for this visual. (I slightly modified your measure)
    _Include =
    VAR _maxDate =
    MAX ( ‘Date Slicer'[Date] )
    VAR _minDate =
    MIN ( ‘Date Slicer'[Date] )
    RETURN
    If(
    ISEMPTY(
    CALCULATETABLE (
    SUMMARIZE( Table1; Table1[Start Date]; Table1[End Date] );
    Table1[Start Date] = _minDate
    )
    );
    BLANK();
    1
    )

    Apply “1” as a filter. The result will be the same, at least as it seems at a first glance 🙂

    • Darren Gosbell

      March 3, 2020 at 10:01 am

      Hi Sergy, I think there might be a bug in your CALCULATETABLE() filter where you have [Start Date] = _minDate. But I can see how this approach would work – It’s logically a similar approach to my one. However I would be wary of the SUMMARIZETABLE call, it’s difficult to measure in a small “toy” model like the one in this article, but that could introduce a materialization that might cause performance issues on a larger model.

      • I see that the code is incorrect. Something went wrong during pasting and I didn’t spot that. I’ll try one more time 🙂
        CALCULATETABLE (
        SUMMARIZE( Table1; Table1[Start Date]; Table1[End Date] );
        Table1[Start Date] = _minDate
        )

        • Don’t know what happens. My comment was distorted again. Sorry.

          • Darren Gosbell

            March 3, 2020 at 10:56 am

            Very strange, I’m assuming that you want similar filter to the one in my measure:

            Table1[Start Date] <= _maxDate, Table1[End Date] >= _minDate,

            Actually I think the following code is probably better than the version in my article above as it gets rid of the SUMMARIZE:

            Gantt End Date =
            VAR _maxDate =
            MAX ( 'Date Slicer'[Date] )
            VAR _minDate =
            MIN ( 'Date Slicer'[Date] )
            RETURN
            CALCULATE(
            MAX (Table1[End Date]),
            Table1[Start Date] <= _maxDate, Table1[End Date] >= _minDate
            )

  3. Thanks Darren – really useful.

    I didn’t realise there was such a useful Gantt chart visual available either . Will definitely be using that!

Leave a Reply to Brian Mather Cancel reply

Your email address will not be published. Required fields are marked *