Skip to main content

SchedulerControl.CustomDrawTimeCell Event

Allows you to manually paint time cells.

Namespace: DevExpress.XtraScheduler

Assembly: DevExpress.XtraScheduler.v24.2.dll

Declaration

public event CustomDrawObjectEventHandler CustomDrawTimeCell

Event Data

The CustomDrawTimeCell event's data class is CustomDrawObjectEventArgs. The following properties provide information specific to this event:

Property Description
Bounds Returns the bounding rectangle of the drawing area.
Cache Gets an object which specifies the storage for the pens, fonts and brushes. Use it for custom painting in Scheduler Reports.
Graphics Gets an object used for painting.
Handled Gets or sets whether an event was handled. If it was handled, the default actions are not required.
ObjectInfo Gets information on the painted element.

The event data class exposes the following methods:

Method Description
DrawDefault() Renders the element using the default drawing mechanism.
DrawHtml(HtmlTemplate, DxHtmlPainterContext, Action<DxHtmlPainterArgs>) Paints the required HTML template inside an element that raised this event. The context parameter allows you to assign an object that transfers mouse events to template elements.
DrawHtml(HtmlTemplate, Action<DxHtmlPainterArgs>) Paints the required HTML template inside an element that raised this event.
GetDisplayValue(String)
GetValue(String)

Remarks

Handle the CustomDrawTimeCell event to paint time cells manually. Use the e.Bounds event parameter to get the cell’s bounding rectangle.

Set the e.Handled parameter to true to handle the event and ignore default painting. You can also call the e.DrawDefault() method to force default cell painting.

Note

It is recommended that you first draw the background of the cell, and then draw its contents.

Use the e.ObjectInfo parameter to get information on the cell currently being painted. To get the cell-specific characteristics, such as the time interval or whether the cell is selected, typecast the ObjectInfo object to the DevExpress.XtraScheduler.Drawing.SelectableIntervalViewInfo class, which is the base class for time cells of all scheduler views.

To access more specific cell information, you may need to cast ObjectInfo to a corresponding SelectableIntervalViewInfo descendant (TimeCell, SchedulerViewCellBase, SelectionBarCell, etc.). Choose the target type depending on which Scheduler View is currently applied and what information you need to retrieve. To determine that, use Visual Studio debugging methods to check the type of an object returned by the ObjectInfo field.

Example 1

The code below paints Scheduler cells that belong to the “8 a.m. to 6 p.m.” interval with a custom hatch-styled brush. A regular solid brush applies the MediumVioletRed background color to selected cells that belong to the same interval.

Scheduler - Custom Paint Cell Time Range

private void schedulerControl2_CustomDrawTimeCell(object sender, CustomDrawObjectEventArgs e) {
    SelectableIntervalViewInfo cell = e.ObjectInfo as SelectableIntervalViewInfo;
    SchedulerControl scheduler = sender as SchedulerControl;
    TimeSpan startWorkHours = new TimeSpan(8, 0, 0);
    TimeSpan endWorkHours = new TimeSpan(18, 0, 0);

    if(cell.Interval.Start.TimeOfDay >= startWorkHours && cell.Interval.End.TimeOfDay <= endWorkHours && cell.Interval.Start.Hour != 23) {
        Rectangle rec = new Rectangle(e.Bounds.X, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height);
        using(var hatchBrush = new HatchBrush(HatchStyle.LightDownwardDiagonal, Color.LightYellow, Color.DodgerBlue))  
            e.Cache.FillRectangle(hatchBrush, rec);
        if(cell.Selected) {
            Rectangle recSelected = new Rectangle(e.Bounds.X, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height - 1);
            e.Cache.FillRectangle(Brushes.MediumVioletRed, rec);
            e.Handled = true;
        }
        e.Handled = true;
    }
}

Example 2

The following sample code handles the SchedulerControl.CustomDrawAppointment event to manually paint appointments. The image below shows the result.

CustomDrawAppointment

View Example

using DevExpress.XtraScheduler;
using DevExpress.XtraScheduler.Drawing;
using System.Drawing.Drawing2D;
private void schedulerControl1_CustomDrawAppointment(object sender, CustomDrawObjectEventArgs e) {
    TimeLineAppointmentViewInfo tlvi = e.ObjectInfo as TimeLineAppointmentViewInfo;
    // This code works only for the Timeline View.
    if(tlvi != null) {
        Rectangle r = e.Bounds;
        r.Offset(3, 3);
        string[] s = tlvi.Appointment.Subject.Split(' ');

        for(int i = 0; i < s.Length; i++) {
            using(var foreBrush = new SolidBrush(colorArray[i]))
                e.Cache.DrawString(s[i], tlvi.Appearance.Font, foreBrush,
r, StringFormat.GenericDefault);
            SizeF shift = e.Cache.CalcTextSize(s[i] + " ", tlvi.Appearance.Font);
            r.X += (int)shift.Width;
        }
        e.Handled = true;
    }
}

Example 3

The following example demonstrates how to hide grid lines in the Scheduler view. It handles the SchedulerControl.CustomDrawTimeCell event. Cell rectangles are painted with the specified brushes, the borders are not painted.

CustomDrawTimeCell

public static void scheduler_CustomDrawTimeCell(object sender, DevExpress.XtraScheduler.CustomDrawObjectEventArgs e)
{
    // Get the cell to draw.
    SelectableIntervalViewInfo cell =
        e.ObjectInfo as SelectableIntervalViewInfo;
    if (cell != null) {
        // Draw the cell.
        Brush myBrush = (cell.Selected) ? SystemBrushes.Highlight : SystemBrushes.Window;
        e.Cache.FillRectangle(myBrush, cell.Bounds);
    }
    e.Handled = true;
}

Example 4

In the sample below, three employees have different shift patterns:

  • Employee 1: 4 on 4 off
  • Employee 2: 5 on 2 off, Mondays and Tuesdays are days off
  • Employee 3: 5 on 2 off, Fridays and Sundays are days off

Custom “ProcessResourceX” methods utilize the RecurrenceInfo objects to define shift patterns and determine whether the current cell belongs to a work day or a weekend.

The SchedulerControl.CustomDrawTimeCell event is handled to apply a custom drawing to days off. Additionally, the SchedulerControl.AllowAppointmentCreate event prevents users from adding new appointments that belong to these crossed-off days.

Scheduler - Custom Work Shifts

using System;
using System.Drawing;
using System.Windows.Forms;
using DevExpress.XtraScheduler;
using DevExpress.XtraScheduler.Drawing;
using System.Drawing.Drawing2D;

namespace XtraSchedulerListDataBinding
{
    public partial class Form1 : Form
    {
        private SchedulerDataStorage storage = new SchedulerDataStorage();
        public Form1()
        {
            InitializeComponent();
            schedulerControl1.Start = DateTime.Today;
            schedulerControl1.TimelineView.GroupType = SchedulerGroupType.Resource;
            schedulerControl1.ActiveViewType = SchedulerViewType.Timeline;
            schedulerControl1.OptionsBehavior.SelectOnRightClick = true;

            PopulateStorage();

            schedulerControl1.CustomDrawTimeCell += SchedulerControl1_CustomDrawTimeCell;
            schedulerControl1.OptionsCustomization.AllowAppointmentCreate = UsedAppointmentType.Custom;
            schedulerControl1.AllowAppointmentCreate += SchedulerControl1_AllowAppointmentCreate;
            schedulerControl1.DataStorage.AppointmentInserting += Storage_AppointmentInserting;
            schedulerControl1.DataStorage.AppointmentChanging += Storage_AppointmentChanging;
        }

        private void Storage_AppointmentChanging(object sender, PersistentObjectCancelEventArgs e)
        {
            Appointment apt = (Appointment)e.Object;
            e.Cancel = !IsAllowed((int)apt.ResourceId, new TimeInterval(apt.Start, apt.End));
        }

        private void Storage_AppointmentInserting(object sender, PersistentObjectCancelEventArgs e)
        {
            Appointment apt = (Appointment)e.Object;
            e.Cancel = !IsAllowed((int)apt.ResourceId, new TimeInterval(apt.Start, apt.End));
        }

        private void SchedulerControl1_AllowAppointmentCreate(object sender, AppointmentOperationEventArgs e)
        {
            SchedulerControl scheduler = (SchedulerControl)sender;
            e.Allow = IsAllowed((int)scheduler.TimelineView.SelectedResource.Id, scheduler.TimelineView.SelectedInterval);
        }

        private void SchedulerControl1_CustomDrawTimeCell(object sender, CustomDrawObjectEventArgs e)
        {
            SchedulerControl scheduler = (SchedulerControl)sender;
            if(scheduler.ActiveViewType == SchedulerViewType.Timeline) {
                SchedulerViewCellBase cell = (SchedulerViewCellBase)e.ObjectInfo;
                if(cell.Resource.Id != EmptyResourceId.Id) {
                    e.Handled = true;
                    if(!IsAllowed((int)cell.Resource.Id, cell.Interval)) {
                        Rectangle rec = new Rectangle(cell.Bounds.X, cell.Bounds.Y, cell.Bounds.Width, cell.Bounds.Height);
                        using(var hatchBrush = new HatchBrush(HatchStyle.DiagonalCross, Color.DimGray, Color.Azure))
                            e.Cache.FillRectangle(hatchBrush, rec);
                    }
                    else e.DrawDefault();
                }
            }
        }

        private bool IsAllowed(int resourceId, TimeInterval interval)
        {
            switch (resourceId)
            {
                case 0:
                    return ProcessResource0(interval);
                case 1:
                    return ProcessResource1(interval);
                case 2:
                    return ProcessResource2(interval);
                default:
                    return true;
            }
        }

        private bool ProcessResource0(TimeInterval interval)
        {
            var recInfo = new RecurrenceInfo()
            {
                AllDay = true,
                Start = DateTime.Today,
                Duration = TimeSpan.FromDays(4),
                Type = RecurrenceType.Daily,
                Periodicity = TimeSpan.FromDays(4).Days * 2
            };

            return IsContained(interval, recInfo);
        }

        private bool ProcessResource1(TimeInterval interval)
        {
            var recInfo = new RecurrenceInfo()
            {
                AllDay = true,
                Start = DateTime.Today,
                Duration = TimeSpan.FromDays(1),
                Type = RecurrenceType.Weekly,
                WeekDays = WeekDays.Monday | WeekDays.Tuesday
            };

            return IsContained(interval, recInfo);
        }

        private bool ProcessResource2(TimeInterval interval)
        {
            var recInfo = new RecurrenceInfo()
            {
                AllDay = true,
                Start = DateTime.Today,
                Duration = TimeSpan.FromDays(1),
                Type = RecurrenceType.Weekly,
                WeekDays = WeekDays.Friday | WeekDays.Sunday
            };

            return IsContained(interval, recInfo);
        }

        private bool IsContained(TimeInterval interval, RecurrenceInfo recInfo)
        {
            AppointmentBaseCollection occs = CalcOccs(interval, recInfo);
            foreach (var occ in occs)
            {
                if (!(interval.End <= occ.Start || occ.End <= interval.Start)) {
                    return false;
                }
            }

            return true;
        }

        private AppointmentBaseCollection CalcOccs(TimeInterval interval, RecurrenceInfo recInfo)
        {
            var patt = storage.CreateAppointment(AppointmentType.Pattern);
            patt.Start = recInfo.Start;
            patt.Duration = recInfo.Duration;
            patt.RecurrenceInfo.Assign(recInfo);
            var calc = OccurrenceCalculator.CreateInstance(recInfo);
            var occs = calc.CalcOccurrences(interval, patt);
            return occs;
        }

        private void PopulateStorage()
        {
            for (int i = 0; i < 3; i++)
            {
                var res = schedulerStorage1.CreateResource(i);
                res.Caption = String.Format("Employee {0}", i+1);
                schedulerStorage1.Resources.Add(res);
            }
        }
    }
}
See Also