windowsforms-devexpress-dot-xtrascheduler-dot-schedulercontrol-057f5a2e.md
Allows you to manually paint time cells.
Namespace : DevExpress.XtraScheduler
Assembly : DevExpress.XtraScheduler.v25.2.dll
NuGet Package : DevExpress.Win.Scheduler
public event CustomDrawObjectEventHandler CustomDrawTimeCell
Public Event CustomDrawTimeCell As CustomDrawObjectEventHandler
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) |
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.
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.
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;
}
}
Private Sub schedulerControl2_CustomDrawTimeCell(ByVal sender As Object, ByVal e As CustomDrawObjectEventArgs)
Dim cell As SelectableIntervalViewInfo = TryCast(e.ObjectInfo, SelectableIntervalViewInfo)
Dim scheduler As SchedulerControl = TryCast(sender, SchedulerControl)
Dim startWorkHours As New TimeSpan(8, 0, 0)
Dim endWorkHours As New TimeSpan(18, 0, 0)
If cell.Interval.Start.TimeOfDay >= startWorkHours AndAlso cell.Interval.End.TimeOfDay <= endWorkHours AndAlso cell.Interval.Start.Hour <> 23 Then
Dim rec As New Rectangle(e.Bounds.X, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height)
Using hatchBrush = New HatchBrush(HatchStyle.LightDownwardDiagonal, Color.LightYellow, Color.DodgerBlue)
e.Cache.FillRectangle(hatchBrush, rec)
End Using
If cell.Selected Then
Dim recSelected As New Rectangle(e.Bounds.X, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height - 1)
e.Cache.FillRectangle(Brushes.MediumVioletRed, rec)
e.Handled = True
End If
e.Handled = True
End If
End Sub
The following sample code handles the SchedulerControl.CustomDrawAppointment event to manually paint appointments. The image below shows the result.
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;
}
}
Imports DevExpress.XtraScheduler
Imports DevExpress.XtraScheduler.Drawing
Imports System.Drawing.Drawing2D
Private Sub schedulerControl1_CustomDrawAppointment(ByVal sender As Object, ByVal e As CustomDrawObjectEventArgs) Handles schedulerControl1.CustomDrawAppointment
Dim tlvi As TimeLineAppointmentViewInfo = TryCast(e.ObjectInfo, TimeLineAppointmentViewInfo)
' This code works only for the Timeline View.
If tlvi IsNot Nothing Then
Dim r As Rectangle = e.Bounds
r.Offset(3, 3)
Dim s() As String = tlvi.Appointment.Subject.Split(" "c)
For i As Integer = 0 To s.Length - 1
Using foreBrush = New SolidBrush(colorArray(i))
e.Cache.DrawString(s(i), tlvi.Appearance.Font, foreBrush, r, StringFormat.GenericDefault)
End Using
Dim shift As SizeF = e.Cache.CalcTextSize(s(i) & " ", tlvi.Appearance.Font)
r.X += CInt(shift.Width)
Next i
e.Handled = True
End If
End Sub
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.
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;
}
Public Shared Sub scheduler_CustomDrawTimeCell(ByVal sender As Object, ByVal e As DevExpress.XtraScheduler.CustomDrawObjectEventArgs)
' Get the cell to draw.
Dim cell As SelectableIntervalViewInfo = TryCast(e.ObjectInfo, SelectableIntervalViewInfo)
If cell IsNot Nothing Then
' Draw the cell.
Dim myBrush As Brush = If(cell.Selected, SystemBrushes.Highlight, SystemBrushes.Window)
e.Cache.FillRectangle(myBrush, cell.Bounds)
End If
e.Handled = True
End Sub
In the sample below, three employees have different shift patterns:
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.
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);
}
}
}
}
Imports System
Imports System.Drawing
Imports System.Windows.Forms
Imports DevExpress.XtraScheduler
Imports DevExpress.XtraScheduler.Drawing
Imports System.Drawing.Drawing2D
Namespace XtraSchedulerListDataBinding
Partial Public Class Form1
Inherits Form
Private storage As New SchedulerDataStorage()
Public Sub New()
InitializeComponent()
schedulerControl1.Start = Date.Today
schedulerControl1.TimelineView.GroupType = SchedulerGroupType.Resource
schedulerControl1.ActiveViewType = SchedulerViewType.Timeline
schedulerControl1.OptionsBehavior.SelectOnRightClick = True
PopulateStorage()
AddHandler schedulerControl1.CustomDrawTimeCell, AddressOf SchedulerControl1_CustomDrawTimeCell
schedulerControl1.OptionsCustomization.AllowAppointmentCreate = UsedAppointmentType.Custom
AddHandler schedulerControl1.AllowAppointmentCreate, AddressOf SchedulerControl1_AllowAppointmentCreate
AddHandler schedulerControl1.DataStorage.AppointmentInserting, AddressOf Storage_AppointmentInserting
AddHandler schedulerControl1.DataStorage.AppointmentChanging, AddressOf Storage_AppointmentChanging
End Sub
Private Sub Storage_AppointmentChanging(ByVal sender As Object, ByVal e As PersistentObjectCancelEventArgs)
Dim apt As Appointment = CType(e.Object, Appointment)
e.Cancel = Not IsAllowed(CInt(Math.Truncate(apt.ResourceId)), New TimeInterval(apt.Start, apt.End))
End Sub
Private Sub Storage_AppointmentInserting(ByVal sender As Object, ByVal e As PersistentObjectCancelEventArgs)
Dim apt As Appointment = CType(e.Object, Appointment)
e.Cancel = Not IsAllowed(CInt(Math.Truncate(apt.ResourceId)), New TimeInterval(apt.Start, apt.End))
End Sub
Private Sub SchedulerControl1_AllowAppointmentCreate(ByVal sender As Object, ByVal e As AppointmentOperationEventArgs)
Dim scheduler As SchedulerControl = DirectCast(sender, SchedulerControl)
e.Allow = IsAllowed(CInt(Math.Truncate(scheduler.TimelineView.SelectedResource.Id)), scheduler.TimelineView.SelectedInterval)
End Sub
Private Sub SchedulerControl1_CustomDrawTimeCell(ByVal sender As Object, ByVal e As CustomDrawObjectEventArgs)
Dim scheduler As SchedulerControl = CType(sender, SchedulerControl)
If scheduler.ActiveViewType = SchedulerViewType.Timeline Then
Dim cell As SchedulerViewCellBase = CType(e.ObjectInfo, SchedulerViewCellBase)
If cell.Resource.Id <> EmptyResourceId.Id Then
e.Handled = True
If Not IsAllowed(CInt(Math.Truncate(cell.Resource.Id)), cell.Interval) Then
Dim rec As New Rectangle(cell.Bounds.X, cell.Bounds.Y, cell.Bounds.Width, cell.Bounds.Height)
Using hatchBrush = New HatchBrush(HatchStyle.DiagonalCross, Color.DimGray, Color.Azure)
e.Cache.FillRectangle(hatchBrush, rec)
End Using
Else
e.DrawDefault()
End If
End If
End If
End Sub
Private Function IsAllowed(ByVal resourceId As Integer, ByVal interval As TimeInterval) As Boolean
Select Case resourceId
Case 0
Return ProcessResource0(interval)
Case 1
Return ProcessResource1(interval)
Case 2
Return ProcessResource2(interval)
Case Else
Return True
End Select
End Function
Private Function ProcessResource0(ByVal interval As TimeInterval) As Boolean
Dim recInfo = New RecurrenceInfo() With {.AllDay = True, .Start = Date.Today, .Duration = TimeSpan.FromDays(4), .Type = RecurrenceType.Daily, .Periodicity = TimeSpan.FromDays(4).Days * 2}
Return IsContained(interval, recInfo)
End Function
Private Function ProcessResource1(ByVal interval As TimeInterval) As Boolean
Dim recInfo = New RecurrenceInfo() With {.AllDay = True, .Start = Date.Today, .Duration = TimeSpan.FromDays(1), .Type = RecurrenceType.Weekly, .WeekDays = WeekDays.Monday Or WeekDays.Tuesday}
Return IsContained(interval, recInfo)
End Function
Private Function ProcessResource2(ByVal interval As TimeInterval) As Boolean
Dim recInfo = New RecurrenceInfo() With {.AllDay = True, .Start = Date.Today, .Duration = TimeSpan.FromDays(1), .Type = RecurrenceType.Weekly, .WeekDays = WeekDays.Friday Or WeekDays.Sunday}
Return IsContained(interval, recInfo)
End Function
Private Function IsContained(ByVal interval As TimeInterval, ByVal recInfo As RecurrenceInfo) As Boolean
Dim occs As AppointmentBaseCollection = CalcOccs(interval, recInfo)
For Each occ In occs
If Not(interval.End <= occ.Start OrElse occ.End <= interval.Start) Then
Return False
End If
Next occ
Return True
End Function
Private Function CalcOccs(ByVal interval As TimeInterval, ByVal recInfo As RecurrenceInfo) As AppointmentBaseCollection
Dim patt = storage.CreateAppointment(AppointmentType.Pattern)
patt.Start = recInfo.Start
patt.Duration = recInfo.Duration
patt.RecurrenceInfo.Assign(recInfo)
Dim calc = OccurrenceCalculator.CreateInstance(recInfo)
Dim occs = calc.CalcOccurrences(interval, patt)
Return occs
End Function
Private Sub PopulateStorage()
For i As Integer = 0 To 2
Dim res = schedulerStorage1.CreateResource(i)
res.Caption = String.Format("Employee {0}", i+1)
schedulerStorage1.Resources.Add(res)
Next i
End Sub
End Class
End Namespace
See Also