Gantt Best Practice: How To Use The Timescale As Scrollbar

Posted by Frank Köhnen on Feb 18, 2015 2:00:00 PM

If you have a timescale in a Gantt chart spanning several years, but still want to be able to see every single day or even the hours in a day, you will soon find that the horizontal scrollbar becomes very sensitive. The smallest movement leads the timescale to shift several days into one or the other direction, even if smaller intervals are required as you look for a specific date and time. This behaviour can result in some unwanted user experiences for the planner working with your scheduling application.

This blog post belongs to a series of Gantt best practices that we regularly publish relating to our Gantt control VARCHART XGantt. This trick describes how to extend the mouse functionality to also use the timescale as scrollbar.

Usually you expand or collapse the timescale by mouse movement. How about if  you use the fluid movement of the mouse interaction to drag the timescale in addition? You can utilize the scrollbar, of course, but when working with a long planning horizon the movement becomes jumpy and you can have problems to place the Gantt chart at a sprecific date and time. 

Below are some event implementations and functions for a mode that is activated if you push a shift-key and move the mouse in the timescale. Now you can drag and drop the timescale to the date you want to see.

Watch a timescale used as scrollbar

 

Events that are used in this code 

            vcGantt1.MouseMove += vcGantt1_MouseMove;

            vcGantt1.MouseDown += vcGantt1_MouseDown;

            vcGantt1.MouseUp += vcGantt1_MouseUp;

            vcGantt1.KeyUp += vcGantt1_KeyUp;

            vcGantt1.KeyDown += vcGantt1_KeyDown;

 

Code Sample

private bool timeScaleDraggingMode;

       private DateTime dateUnderCursor;

        private void vcGantt1_MouseDown(object sender, MouseEventArgs e)

       {            

           object hitObject = null;

           VcObjectType hitObjectType = VcObjectType.vcObjTypeNone;

           vcGantt1.IdentifyObjectAt(e.X, e.Y, ref hitObject, ref hitObjectType);

 

           //The timeScaleDraggingMode will only be set to true if the timescale rescaling is disabled and you drag and drop with your left mousebutton on the timescale

           if (hitObjectType == VcObjectType.vcObjTypeTimeScale && e.Button == MouseButtons.Left && vcGantt1.TimeScaleRescalingAllowed == false)

           {

               timeScaleDraggingMode = true;

 

               //This date will be below the mousecursor for every movement while dragging

               dateUnderCursor = vcGantt1.GetDate(e.X);              

           }

       }

 

       private void vcGantt1_MouseMove(object sender, MouseEventArgs e)

       {

           if (timeScaleDraggingMode)

           {

               ScrollToMousePosition(vcGantt1, e, dateUnderCursor);

           }

       }

 

       private void vcGantt1_MouseUp(object sender, MouseEventArgs e)

       {

           if (timeScaleDraggingMode)

           {

               timeScaleDraggingMode = false;              

           }

       }

 

       private void ScrollToMousePosition(VcGantt gantt, MouseEventArgs e, DateTime dateUnderCursor)

       {

           // Save the original timeunit set in the XGantt control

           VcTimeUnit timeUnit = gantt.TimeUnit;

           gantt.TimeUnit = VcTimeUnit.vcSecond; //Use seconds as timeunit for highest precision during mouse movement

 

           //Get the x Location of the XGantt Timescale

           int xTimeScalePosition = 0;

           int dummy = 0;

 

           gantt.GetViewComponentSize(VcComponentType.vcTimeScaleComponent, ref xTimeScalePosition, ref dummy, ref dummy, ref dummy);

 

           const int SplitterWidth = 4;

           int mousePositionInTimeScale = e.X - xTimeScalePosition - SplitterWidth; //Calculate the mouseposition relative to the x location of the timescale

                       

           VcSection section = gantt.TimeScaleCollection.Active.get_Section(0);

 

           //This calculates the number of seconds between the most left date and time showing on the timescale and the date and time that are supposed to be under the mouse cursor

           double gapAsNoOfTimeUnits = (gantt.ConvertDistance(VcDistanceConversionType.vcXPixelsToCentiMillimeters, mousePositionInTimeScale)

               / (section.UnitWidthEx)) * GetTimeUnitFactor(gantt.TimeUnit) / GetTimeUnitFactor(section.TimeUnit);

 

           //If the non work intervals were collapsed we need to use the active calendar

           if (section.NonWorkIntervalsCollapsed)

           {

               gantt.ScrollToDate(gantt.CalendarCollection.Active.AddDuration(dateUnderCursor,

                   (int)-gapAsNoOfTimeUnits), VcHorizontalAlignment.vcLeftAligned, 0);

           }

           else

           {

 

               gantt.ScrollToDate(dateUnderCursor.AddSeconds(-gapAsNoOfTimeUnits), VcHorizontalAlignment.vcLeftAligned, 0);

           }

 

           gantt.TimeUnit = timeUnit;

       }

 

       //Returns the number of given units (day, hour, minute, second) in one day

       private double GetTimeUnitFactor(VcTimeUnit timeUnit)

       {

           switch (timeUnit)

           {

              case VcTimeUnit.vcDay:

                   return 1;

 

               case VcTimeUnit.vcHour:

                   return 24;

 

               case VcTimeUnit.vcMinute:

                   return 24 * 60;

 

               case VcTimeUnit.vcSecond:

                   return 24 * 60 * 60;

 

               default:

                   return 0;

           }

       }

 

       //As soon as you press the shift keys you can move the time scale via drag and drop.

       private void vcGantt1_KeyDown(object sender, KeyEventArgs e)

       {

           if (e.KeyCode == Keys.ShiftKey)

           {

               //If movement should be standard for drag and drop on the timescale change this to true

               //This will enable you to rescale the timescale while pressing shift

               vcGantt1.TimeScaleRescalingAllowed = false;

           }

       }

 

       private void vcGantt1_KeyUp(object sender, KeyEventArgs e)

       {

           if (e.KeyCode == Keys.ShiftKey)

           {

               //If movement should be standard for drag and drop on the timescale change this to false

               vcGantt1.TimeScaleRescalingAllowed = true;

           }

       }

 

Want to try by your own? Download the complementary trial version of VARCHART XGantt and start building a powerful scheduling application today.

 

VARCHART XGantt free trial - Gantt chart control .NET

Topics: Windows Forms Gantt Control, XGantt development tricks, Gantt Best Practice, Gantt Chart Controls