Does this sound familiar? You have to visualize plenty of operations with short durations and have to cover both a short-term and a long-term planning horizon with one view in your Gantt scheduling appplication? There are two options of how to display the time scale. You could choose between
- A very detailed time scale, offering the advantage of making the single operations clearly visible but, on the other hand only showing a very short period, thus losing the overview.
- A rough time scale offering a good overview of a longer period, while however affecting the accuracy of details.
In our series of Gantt chart tricks we describe how to create advanced planning and scheduling charts with our .NET Gantt control VARCHART XGantt. Read in this blog post how to enhance the time scale function to realize a magnifiying glass letting you keep track of the longer period while at the same time zooming into a selected time window to show every detail of the operations.
Time Scale with Magnifying Glass Function
Define a rough time scale, select a group of operations and switch to a detailed time scale only for the period containing the selected operations. The following steps are needed:
Step 1) Initial situation: The time scale lies within a planning horizon of several days.
The operations circled in red could lead the planner to the assumption that they consist only of three operations.
Step 2) To check this, the planner selects these operations with the so called rectangle rubber band.
In the background, VARCHART XGantt switches to a detailed time resolution for the selected period and it becomes immediately clear that there are lots of operations located within this time window:
Step 3) Clicking in the white area of the diagram switches off the time scale magnifying glass and the planner sees the former overview again.
Hands-on step-by-step-guide how to achieve this with VARCHART XGantt
Create a time scale with three sections:
The labeling of the time scale section DetailedArea has to be more detailed than those of the other two, e.g., markings every ten minutes in contrast to hourly markings for the other time scale sections.
Layers with two filters are needed for displaying operations in the detailed time scale section and in the other two sections:
Add the data field Tasks:NodeZoomed to your node data table. It will be set at runtime.
Create two date lines to use as optical divider of the three time scale sections:
The following code is needed:
//Global variables
List<string> zoomedNodesIDs = new List<string>();
DateTime earliestStart;
DateTime latestEnd;
double oldUnitWidthEx0;
double oldUnitWidthEx1;
double oldUnitWidthEx2;
bool diagramLeftClicked = false;
VcDateLine dateLine1;
VcDateLine dateLine2;
int sectionIndex;
private void Form1_Load(object sender, EventArgs e)
{
//Add the following code after you have loaded your data into XGantt:
ts.get_Section(1).StartDate = vcGantt1.TimeScaleStart;
ts.get_Section(2).StartDate = vcGantt1.TimeScaleStart;
dateLine1 = vcGantt1.DateLineCollection.DateLineByName("DateLine1");
dateLine2 = vcGantt1.DateLineCollection.DateLineByName("DateLine2");
oldUnitWidthEx0 = ts.get_Section(0).UnitWidthEx;
oldUnitWidthEx1 = ts.get_Section(1).UnitWidthEx;
oldUnitWidthEx2 = ts.get_Section(2).UnitWidthEx;
}
private void vcGantt1_VcNodesMarking(object sender, VcNodesMarkingEventArgs e)
{
if (e.NodeCollection.Count > 0)
{
DateTime startDate;
DateTime endDate;
VcCalendar cal = vcGantt1.CalendarCollection.Active;
VcTimeScale ts = vcGantt1.TimeScaleCollection.Active;
earliestStart = DateTime.MaxValue;
latestEnd = DateTime.MinValue;
//Reset all
ts.get_Section(1).StartDate = vcGantt1.TimeScaleStart;
ts.get_Section(2).StartDate = vcGantt1.TimeScaleStart;
ts.get_Section(0).UnitWidthEx = oldUnitWidthEx0;
ts.get_Section(1).UnitWidthEx = oldUnitWidthEx1;
ts.get_Section(2).UnitWidthEx = oldUnitWidthEx2;
//Compute start and end date for zoomed timescale section
foreach (VcNode node in e.NodeCollection)
{
startDate = (DateTime)node.get_DataField(eTasks.Start);
endDate = (DateTime)node.get_DataField(eTasks.End);
if (startDate < earliestStart)
earliestStart = startDate;
if (endDate > latestEnd)
latestEnd = endDate;
}
//Round to hours
earliestStart = earliestStart.AddMinutes(-1 * earliestStart.Minute);
latestEnd = latestEnd.AddMinutes(-1 * latestEnd.Minute).AddHours(1);
//Set new section start dates
ts.get_Section(2).StartDate = latestEnd;
ts.get_Section(1).StartDate = earliestStart;
vcGantt1.FitRangeIntoView(earliestStart, latestEnd, 120);
ts.get_Section(0).UnitWidthEx = oldUnitWidthEx0;
ts.get_Section(2).UnitWidthEx = oldUnitWidthEx2;
vcGantt1.ScrollToDate(earliestStart, VcHorizontalAlignment.vcLeftAligned, 120);
dateLine1.Date = earliestStart;
dateLine1.Visible = true;
dateLine2.Date = latestEnd;
dateLine2.Visible = true;
}
}
private void vcGantt1_VcNodesMarked(object sender, VcNodesMarkedEventArgs e)
{
UnmarkAllNodes();
MarkNodesInTimeRange();
}
private void UnmarkAllNodes()
{
vcGantt1.SuspendUpdate(true);
foreach (string nodeID in zoomedNodesIDs)
{
VcNode node = vcGantt1.GetNodeByID(nodeID);
node.set_DataField(eTasks.NodeZoomed, 0);
node.Update();
}
vcGantt1.SuspendUpdate(false);
zoomedNodesIDs.Clear();
}
private void MarkNodesInTimeRange()
{
//Set "NodeZoomed" datafield for all nodes in zoomed timescale section
DateTime startDate;
DateTime endDate;
if (!diagramLeftClicked)
{
vcGantt1.SuspendUpdate(true);
foreach (VcNode node in vcGantt1.NodeCollection)
{
startDate = (DateTime)node.get_DataField(eTasks.Start);
endDate = (DateTime)node.get_DataField(eTasks.End);
if (startDate >= earliestStart && startDate < latestEnd || endDate > earliestStart && endDate <= latestEnd)
{
node.set_DataField(eTasks.NodeZoomed, 1);
node.Update();
zoomedNodesIDs.Add(node.get_DataField(eTasks.ID).ToString());
}
}
vcGantt1.SuspendUpdate(false);
}
diagramLeftClicked = false;
}
private void vcGantt1_VcDiagramLeftClicking(object sender, VcDiagramClickingEventArgs e)
{
//Reset the timescale so that only section 2 is visible
VcTimeScale ts = vcGantt1.TimeScaleCollection.Active;
ts.get_Section(1).StartDate = vcGantt1.TimeScaleStart;
ts.get_Section(2).StartDate = vcGantt1.TimeScaleStart;
ts.get_Section(0).UnitWidthEx = oldUnitWidthEx0;
ts.get_Section(1).UnitWidthEx = oldUnitWidthEx1;
ts.get_Section(2).UnitWidthEx = oldUnitWidthEx2;
UnmarkAllNodes();
dateLine1.Visible = false;
dateLine2.Visible = false;
diagramLeftClicked = true;
}
Do you want to learn how to integrate a shift calendar into timescale? See this blog post.