07-04-2022 14:51 PM - last edited 07-04-2022 15:25 PM
I was working on an internal project a couple of years ago and one of the desired features was to display the date in a Gantt-type chart. I had already been doing quite a bit with Power Apps at the time so I thought maybe I could do this somehow with a canvas app. After trying several things, including embedding Power BI, it seemed like a futile effort.
Then one day after playing around with making some bar graphs with some dynamically sized label controls, I had an idea. It seemed that it would be possible to use some similar techniques to create a data-driven Gantt-like display. The idea has evolved a bit since the original.
Okay, enough of the boring story and onto the App!
App Features
Expand or Collapse to Parent Level
Click on the chevron to expand or collapse main tasks including the top-level project.
Automatic Grid Scaling
The app will automatically scale the date grid density based on the window size and date range.
Daily View
Weekly View
4-Week View
Navigate and Zoom
Select start and end dates to display tasks in that range on Gannt. Use Navigate buttons to move forward or backward in time. Use Zoom buttons to increase/decrease end date to effectively “Zoom” the time window in or out.
Automatically Adjust Gantt Density Based on Browser Window Size
App will dynamically change timeline grid lines based to display more data on larger/hi-res screens.
Smaller window scales to weekly grid to display data
Maximize window and see that it changes the scale from weekly to daily grid
If a task label is too long to put inside the Gantt bar based on the scaling, it is automatically placed outside the end of the bar.
Project Selection
At the bottom of the screen, there is a project list gallery where you can select the projects to include in the display.
How It All Works
The Data
For this demo app, I created some data that is loosely based on construction and remodeling projects. It is simplified for the purpose of demoing the UI. I imported the data into the app so that it would be easy to distribute the demo. Note that I wanted to make the data flat and simple for the purpose of showing the UI. In a real app, this data would be distilled from multiple tables or views. I load this table into a collection (colTasks) at startup to make things easier.
About the Tasks data:
Column Name | Description |
Id | Unique Id for the row |
ProjectId | Project number |
ProjectName | Project name |
TaskNo | Top-level task number (0=parent project) |
SubTaskNo | Sub task number |
TaskName | Task name |
StartDate | Start date for this task |
EndDate | End date for this task |
TaskType | Task type for this task |
TaskLvl | Task indent level for this task |
Duration | Duration in days for this task (parents contain sum of subtasks) if the duration is 0, then the task is considered a milestone and displayed with a pentagon icon (there is no diamond icon and I was a little too lazy to do something else). |
Show | Show this row in the Gantt |
Expanded | Are the sub tasks showing or not? |
Task Types
The Task Types are in a separate table. Each type has a color specified by a hex RGB color code which is used to color code the Gantt bars.
The Gantt Bars
The core of this app is how the Gantt bars are displayed. It comes down to two basic things: Length and Position.
To find the position, we first need to see how many horizontal pixels there are in a day on the grid.
We can get that by using the following calculation:
pixels per day = (end pixel - start pixel) / (end date - start date)
Then calculate the position X:
X = start pixel + (end date - start date) * pixels per day
For the bar length (width):
width = duration days * pixels per day
I used label controls to track the data to support the above calculations. Label controls are great for this since they automatically calculate the values as the data changes. Although these would normally be hidden, I am displaying them in this app for educational purposes.
The actual formulas in the app are as follows:
Pixels per day (label):
Value(lblPixInRange.Text)/Value(lblDaysInRange.Text)
Gantt bar position X:
Value(lblStartPix.Text) + DateDiff(dteStartDate.SelectedDate,ThisItem.StartDate,Days) * Value(lblPixPerDay.Text)
Gantt bar Width:
ThisItem.Duration * Value(lblPixPerDay.Text)
Date Overlay Grid (Underlay?)
The date grid is just a horizontal gallery control that actually sits behind the Gantt gallery.
The Items/Data source for the gallery is sequence starting at 0. I figured that 150 should be a safe maximum for most applications.
I have a hidden label (lblUGGridSectionStartDate) that I used to simplify calculating the timeline headers.
lblUGGridSectionStartDate.Text will dynamically change its value based on the density of the grid and start date.
Switch(
lblGridDensity.Text,
"4WEEKS",
dteStartDate.SelectedDate + ThisItem.Value * 28,
"WEEKLY",
dteStartDate.SelectedDate + ThisItem.Value * 7,
dteStartDate.SelectedDate + ThisItem.Value
)
Expand/Collapse Subtask Display
The key to determining what data to show in the Gantt view is literally the Boolean ‘Show’ column in the collection (as well as the selected project, of course).
The Items/Data source for the main gallery (galGanttView):
Filter(
colTasks,
ProjectId in Filter(
galProjectList.AllItems,
chkPLIsSelected.Value = true
).Result,
Show
)
When clicking on the chevron (icoGVShowHideSubs), the app will toggle the expanded state of the control and data column: ‘Expanded’ as well as updating the subtasks ‘Show’ column.
Select(Parent);
If(
ThisItem.Expanded,
UpdateIf(
colTasks,
// hide subtasks of current task
And(
ProjectId = ThisItem.ProjectId,
TaskNo = ThisItem.TaskNo,
SubTaskNo > 0
),
{Show: false},
// change expanded field for this task
And(
ProjectId = ThisItem.ProjectId,
TaskNo = ThisItem.TaskNo,
SubTaskNo = 0
),
{Expanded: false},
// hide all tasks for this project task (0)
If(
ThisItem.TaskNo = 0,
And(
ProjectId = ThisItem.ProjectId,
TaskNo > 0,
Show
)
),
{
Show: false,
Expanded: false
}
),
UpdateIf(
colTasks,
// show subtasks of current task
And(
ProjectId = ThisItem.ProjectId,
TaskNo = ThisItem.TaskNo,
SubTaskNo > 0
),
{Show: true},
// change expanded field for this task
And(
ProjectId = ThisItem.ProjectId,
TaskNo = ThisItem.TaskNo,
SubTaskNo = 0
),
{Expanded: true},
// show all tasks for this project task (0)
If(
ThisItem.TaskNo = 0,
And(
ProjectId = ThisItem.ProjectId,
TaskNo > 0
)
),
{
Show: true,
Expanded: true
}
)
);
The appearance of the chevron icon itself is controlled by the ‘Expanded’ column.
If(
ThisItem.Expanded,
Icon.ChevronDown,
Icon.ChevronRight
)
Summary
You will find that there are other features there that I have not called out. But the general idea was to show what could be done with standard controls. I didn’t want to over complicate things by adding too much for this demo.
This is my first post to the Community Samples. Hopefully others will find this useful for their projects.
Cheers!
-Ron Larsen
watch?v=l60u2IpveTA
Hi @RonLar you did a great job with this job, but i have a issue with importing the app i have this error:
I've checked there is no xml files which it demands, what can i do to get your app and run it on my enviroment?
This is really amazing. I have 'borrowed' your idea and made a version for my own uses but I'm having trouble with the dates as I'm in the UK.
I've changed all the formats to display as I want however if my data has a start date of 01/04/2022 - in the UK this is 1st of April, however my btnGVGanttBar is interpreting it USA style starting at 4th of January. Tearing my hair out trying to work out how to amend, any ideas?
edit I think I managed to solve following this guide but I'm not sure which one fixed it Power Apps Guide - Dates - 4 tips to make sure that dates display correctly in UK "dd mm yyyy" forma...
I think the real-time SharePoint response can be a little slow at times. I would suggest loading the SharePoint list into a Collection initially and then do Patch to Collection and SharePoint at the same time. That way that application can respond the collection quickly and SharePoint can be updated in background.
@Mrkelley You should be able to add additional rows for the same person. As long as the data was sorted correctly, you could have it grouped by person for the display. (similar to how the demo is grouped by project)
I had the same issue - UK dates with Powerapps constantly drive me mad! I’ll have to take a look but the way I sorted it was to change every reference from “US” to “GB”. Off the top of my head, the code was within the ‘width’ or it might have been ‘position’ in the GanttBar object. Hope this helps.
Many thanks. I’ve since sorted it by opting for a second gallery containing the different task types and then filtering and sorting the Gantt display using these items selected as well as the main projectId checkboxes.
Hi Ron
im getting there - for some reason I had to pop in a REFRESH each time the show / hide is pressed. This seems to be now working. Only issue I’m now having is that whenever I close a subtask line it seems to close all lines and reset back to only showing the main task 0 line? Any thoughts please? It’s nearly there but as I say if I want to close a subtask line but still show it, I can’t and it closes all lines? I think it might be something to do with my refresh issue?
If I understand you correctly, I'm not sure about icons, but you could just change the Text for the task button (btnGVGanttBar) to:
With(ThisItem,
If(
Len(
If(TaskNo=0,
ProjectName & " | Due: " & DateValue(ThisItem.EndDate,"en-US"),
TaskName & " | Due: " & DateValue(ThisItem.EndDate,"en-US")
)
)
<Self.Width/7,
If(TaskNo=0, ProjectName & " | Due: " & DateValue(ThisItem.EndDate,"en-US"),
TaskName & " | Due: " & DateValue(ThisItem.EndDate,"en-US")
)
)
)
You will be given the following results, which will display the Due Date along with the task:
Hope this helps, but the only drawback to this is when you zoom too far in on the timeline, the text will not display correctly. However, Ron has the Tooltip to show the Start and End Date when hovering over the task... so you could always do that.
First of all, in my opinion, this is a masterpiece. I commend you for being willing to dive into this challenge, and share it with the rest of the world... so, thank you!!!
I'm trying to figure out how to only display weekdays in the timeline (M-F) as that's the world some of us live in, and the weekends clutter up the view. I know how to display this info on the label level. However, I'm struggling because I know there are variables set up for the gallery dates, and formulas for the timeline formatting when changing dates as well as zooming. Can you (or someone else) offer advice on attaining this goal of showing just Weekdays on the timeline?