If you encounter performance issues with the control, please consider the followings:
(valid for all versions)
Use the BeginUpdate/EndUpdate
when multiple changes are performed, like loading a set of events.
The BeginUpdate methods prevents the control from painting until the EndUpdate method is called.
In other words, when a change occurs, the control 's is updated
once, when the last EndUpdate method is encountered.
(valid for all versions)
If you have a large database, that includes events from different
years, loads events from the browsing year only. You can use the
Year( Calendar.Date
) to get the browsing year, or the year displayed in the calendar
panel of the control. The LayoutEndChanging(exCalendarDateChange)
event is fired once a new date/year is browsed. Handle the LayoutEndChanging
event when exCalendarDateChange operation occurs, get the year using
the Calendar.Date property and loads if necessary the events of the
new year. You can use a hash table to save the years being loaded,
to prevent loading the same year twice. Also, you should use the
best code to load or retrieve the events from your database, so it
won't be a time consuming. In the same way, you can
improve the speed if necessary to load only the required events, for
instance for a month, and so on, not really need to load the events
for the entire year.
(valid for versions prior to 7.1.1.0)
Set the properties: DefaultEventLongLabel,
DefaultEventShortLabel,
DefaultEventTooltip
to "", empty string. These properties support expressions and built-in HTML tags, and as soon as a new event is added, the LongLabel, ShortLabel and ToolTip properties of the event are initialized
and parsed with the associated value, which could be a time
consuming. Newer versions, does not require setting the DefaultEventLongLabel, DefaultEventShortLabel, DefaultEventTooltip
properties
For instance, the following VB6 sample loads events from a
database, using ADO:
Dim d As Long
d = GetTickCount()
With Schedule1
.BeginUpdate
.DefaultEventLongLabel = "<%=%5%><br><%=%256%>"
.DefaultEventShortLabel = .DefaultEventLongLabel
.DefaultEventTooltip = .DefaultEventLongLabel
.Events.Clear
Dim rs As Object
Set rs = CreateObject("ADOR.Recordset")
Dim iCount As Long
iCount = 0
With rs
.Open "Event", "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & App.Path & "\xScheduler.mdb", 3, 3
Schedule1.Calendar.Selection = Fix(rs("StartDateTime").Value)
While Not .EOF() And iCount < 2000
With Schedule1.Events.Add(rs("StartDateTime").Value, rs("EndDateTime").Value)
Dim v As Variant
v = rs("Subject").Value
.Caption = IIf(IsNull(v), "", v)
.Editable = exEditCaption
iCount = iCount + 1
End With
rs.MoveNext
Wend
End With
.EndUpdate
End With
d = GetTickCount() - d
MsgBox (Schedule1.Events.Count & " events loaded in " & d & " msec")
The time to load 2000 events, using ADO, in VB6 is 63 milliseconds.
For instance, the following MS Access sample loads events from a
database, using DAO:
Dim d As Long
d = GetTickCount()
With Schedule1
.BeginUpdate
.DefaultEventLongLabel = "<%=%5%><br><%=%256%>"
.DefaultEventShortLabel = .DefaultEventLongLabel
.DefaultEventTooltip = .DefaultEventLongLabel
.Events.Clear
Dim rs As Object
Set rs = CurrentDb.OpenRecordset("Event", dbOpenTable)
Dim iCount As Long
iCount = 0
With rs
Schedule1.Calendar.Selection = Fix(rs("StartDateTime").Value)
While Not .EOF() And iCount < 2000
With Schedule1.Events.Add(rs("StartDateTime").Value, rs("EndDateTime").Value)
Dim v As Variant
v = rs("Subject").Value
.Caption = IIf(IsNull(v), "", v)
.Editable = exEditCaption
iCount = iCount + 1
End With
rs.MoveNext
Wend
End With
.EndUpdate
End With
d = GetTickCount() - d
MsgBox (Schedule1.Events.Count & " events loaded in " & d & " msec")
The time to load 2000 events, using DAO in MS Access, is 265 milliseconds.
In
conclusion, we have did a simple sample that counts the number of
records from a table using ADO ( in VB6 ) vs DAO ( MS Access ).
The
following VB6 sample HAS nothing to do with our component, JUST
counts the number of records in a large table:
Dim d As Long
d = GetTickCount()
Dim rs As Object
Set rs = CreateObject("ADOR.Recordset")
Dim iCount As Long
With rs
.Open "Event", "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & App.Path & "\xScheduler.mdb", 3, 3
While Not .EOF()
iCount = iCount + 1
rs.MoveNext
Wend
End With
d = GetTickCount() - d
MsgBox ("Enumerate " & iCount & " records in " & d & " msec")
The time to count 88132 records, using ADO, in VB6 is is 421 msec.
The
following MS Access sample HAS nothing to do with our component, JUST
counts the number of records in a large table:
Dim d As Long
d = GetTickCount()
Dim rs As Object
Set rs = CreateObject("ADOR.Recordset")
Dim iCount As Long
With rs
.Open "Event", "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & App.Path & "\xScheduler.mdb", 3, 3
While Not .EOF()
iCount = iCount + 1
rs.MoveNext
Wend
End With
d = GetTickCount() - d
MsgBox ("Enumerate " & iCount & " records in " & d & " msec")
The time to count 88132 records, using ADO, in VB6 is is 702
msec.
Here's the definition for the GetTickCount API function
Private Declare Function GetTickCount Lib "kernel32" () As Long
auto hide the calendar panel. Ability to hide the calendar section while the cursor is not in it (OnResizeControlEnum.exResizePanelRight Or OnResizeControlEnum.exCalendarFit Or
OnResizeControlEnum.exCalendarAutoHide)
hide completely the calendar section (exHideSplitter)
specify the alignment of the calendar, as on the left or right side of the schedule view
(OnResizeControlEnum.exChangePanels Or
OnResizeControlEnum.exCalendarFit)
full or partially view of the calendar panel (exResizePanelRight)
disabling the control's vertical split bar ( so user can not resize the fixed panel )
(OnResizeControlEnum.exResizePanelRight Or OnResizeControlEnum.exDisableSplitter Or
OnResizeControlEnum.exCalendarFit)
All these, can be changed using the OnResizeControl
property.
In case, you need to place the calendar panel to some other place,
you need to use the Calendar.Parent
property as shown bellow. The Calendar.Panel allows to specify the
handle of the window that hosts the calendar panel. In this case, the
OnResizeControl property has no effect. Using the Calendar.Parent
property you can move the calendar panel from the scheduler to any other
window/place.
Here's how you can place the scheduler and the calendar to different places:
Insert two eXSchedule components to the same form, with names: Schedule1 and Schedule2
Handle the Load event of the form/dialog and call the following
code
Private Sub Form_Load()
Schedule1.Calendar.Parent = Schedule2.hWnd
End Sub
This way the second scheduler component acts as a host for the calendar panel of the first schedule component. Any action on the schedule or calendar will be reflected on both. When a window hosts the calendar panel, it fits the entire client area. In case you re-size the window that hosts the calendar panel, you can re-assign the Calendar.Parent property, so the calendar panel updates its size so it fits the new client area of the host.
The control provides the DateEvents property that gives a
collection/safe array of Event objects in the specified date. Using the
DateEvents property you can count the number of events / appointments
within a date.
The following VB6 sample enumerates the Event objects found in the
selected date ( calendar panel selection )
With Schedule1
Dim c As Variant
For Each c In .DateEvents(.Calendar.SelDate(0))
Debug.Print c.Start
Next
End With
The following VB6 sample counts the number of Event objects within
each group found within the giving date:
With Schedule1
Dim c As Variant
Dim e As New Dictionary, d As Variant
For Each c In .DateEvents(.Calendar.SelDate(0))
e(c.GroupID) = CLng(e(c.GroupID)) + 1
Next
Dim i As Variant
For Each i In e
Debug.Print "GroupID: " & i & " contains " & e(i)
Next
End With
The sample uses the Dictionary object of Microsoft Scripting Runtime.
There are several ways of setting the caption within the event. The
ShortLabel, LongLabel, ExtraLabel and BodyBackgroundExt properties of
the Event object may specify a caption to be displayed in the event. The
AddEvent event notifies your application once a new event is added to
the scheduler. The AddEvent event is fired also when the user adds the
events by code. The LayoutStartChanging(exScheduleCreateEvent) / LayoutEndChanging(exScheduleCreateEvent)
events notify your application once the user starts creating an event by
dragging the mouse over the control.
The following VB6 sample changes the event's caption when a new event
is added to the scheduler:
' AddEvent event - Notifies your application once the a new event is added.
Private Sub Schedule1_AddEvent(ByVal Ev As EXSCHEDULELibCtl.IEvent)
With Schedule1
With Ev
.ShortLabel = "short"
.LongLabel = "long"
.ExtraLabel = "extra"
.BodyBackgroundExt = "root[text=`back`,align=0x11]"
End With
End With
End Sub
The following VB6 changes the caption just for the events added by
the user:
Dim iCreateEvent As Long
Private Sub Schedule1_LayoutStartChanging(ByVal Operation As EXSCHEDULELibCtl.LayoutChangingEnum)
If (Operation = exScheduleCreateEvent) Then
iCreateEvent = iCreateEvent + 1
End If
End Sub
Private Sub Schedule1_LayoutEndChanging(ByVal Operation As EXSCHEDULELibCtl.LayoutChangingEnum)
If (Operation = exScheduleCreateEvent) Then
iCreateEvent = iCreateEvent - 1
End If
End Sub
Private Sub Schedule1_AddEvent(ByVal Ev As EXSCHEDULELibCtl.IEvent)
If Not (iCreateEvent = 0) Then
With Schedule1
With Ev
.ShortLabel = "short"
.LongLabel = "long"
.ExtraLabel = "extra"
.BodyBackgroundExt = "root[text=`back`,align=0x11]"
End With
End With
End If
End Sub
The sample changes the iCreateEvent member when the
exScheduleCreateEvent event occurs, so we know that the user creates a
new event at runtime.
The BodyBackgroundExt property of
the Event object can be used to specify multiple captions, icons,
pictures, frames, patterns on the same event.
Easy samples:
"[pattern=6]",
shows the BDiagonal pattern on the object's background.
"[frame=RGB(255,0,0),framethick]",
draws a red thick-border around the object.
"[frame=RGB(255,0,0),framethick,pattern=6,patterncolor=RGB(255,0,0)]",
draws a red thick-border around the object, with a patter inside.
"[[patterncolor=RGB(255,0,0)](none[(4,4,100%-8,100%-8),pattern=0x006,patterncolor=RGB(255,0,0),frame=RGB(255,0,0),framethick])]",
draws a red thick-border around the object, with a patter inside, with a
4-pixels wide padding:
"top[4,back=RGB(0,0,255)]",
draws a blue line on the top side of the object's background, of 4-pixels
wide.
"[text=`caption`,align=0x22]",
shows the caption string aligned to the bottom-right side of the object's background.
"[text=`<img>flag</img>`,align=0x11]"
shows the flag picture and the sweden string aligned to the bottom side of
the object.
"left[10,back=RGB(255,0,0)]",
draws a red line on the left side of the object's background, of 10-pixels
wide.
"bottom[50%,pattern=6,frame]",
shows the BDiagonal pattern
with a border arround on the
lower-half part of the object's background.
"root[text=`caption <b>2`,align=0x22](client[text=`caption <b>1`,align=0x20])",
shows the caption 1 aligned to the bottom-left side, and the
caption 2 to the bottom-right side
Complex samples:
Now, lets say we have the following request to layout the
colors on the objects:
We define the BodyBackgroundExt property such as "top[30%,back=RGB(253,218,101)],client[back=RGB(91,157,210)],none[(0%,0%,10%,100%)](top[90%,back=RGB(0,0,0)])",
and it looks as:
so, if we apply to our object we got:
Now, lets say we have the following request to layout the
colors on the objects:
We define BodyBackgroundExt property such as "left[10%](top[90%,back=RGB(0,0,0)]),top[30%,back=RGB(254,217,102)],client[back=RGB(91,156,212)]",
and it looks as:
so, if we apply to our object we got:
The Exontrol's eXButton
WYSWYG Builder helps you to generate or view the EBN String Format, in the To
String field as shown in the following screen shot:
The
To String field of the EBN Builder defines the EBN String Format that can be
used on BodyBackgroundExt property.
The
EBN String Format syntax in BNF notation is defined like follows:
This is usually happens when the Selection property uses expressions and
the browsing year is different than what you have selected. For
instance, Calendar.Selection = "value in (#5/4/2002#,#5/5/2002#)",
indicates that you want to select the #5/4/2002# and #5/5/2002# dates,
but still the scheduler displays the current year ( another year than
2002 )
Instead using a code like:
Calendar.Selection = "value in (#5/4/2002#,#5/5/2002#)"
you shall use a code like:
.Calendar.Selection = #5/4/2002#
.Calendar.Selection = "value in (#5/4/2002#,#5/5/2002#)"
The difference, is that the second code, calls additionally the Calendar.Selection = #5/4/2002#
( selects a date within the year being used in the expression ), which
makes the controls to display/browse for a new year, the year being used
in the Selection's expression.
By default, the PageUp/PageDown keys advances the selected date to the
prev/next month. You can change this behavior by handling the KeyDown
event by doing the following:
Set the KeyCode parameter to 0, so no
further/default action for specified key is performed. In case you
are using a programming language that does not support changing the
value of the parameter passed by reference ( like uniPaas 1.5 (formerly known as eDeveloper), DBase, and so on
), you can use the EventParam(0) = 0 or Template =
"EventParam(0) = 0", ...
Update the DayViewOffsetY property with the height of a day
in the schedule view.
The following sample implements PageUp/Page Down keys to vertical
scrolling the view by page:
Private Sub Schedule1_KeyDown(KeyCode As Integer, Shift As Integer)
If KeyCode = 33 Then ' PageUp
KeyCode = 0 ' Schedule1.EventParam(0) = 0
With Schedule1
.DayViewOffsetY = .DayViewOffsetY - .DayViewHeight
End With
Else
If KeyCode = 34 Then ' PageDown
KeyCode = 0 ' Schedule1.EventParam(0) = 0
With Schedule1
.DayViewOffsetY = .DayViewOffsetY + .DayViewHeight
End With
End If
End If
End Sub
The exViewSingleRowLockHeader(3) of ShowViewCompact property specifies
that the control locks the date header, so it is visible while user
scrolls the view. If ShowViewCompact property is
exViewSingleRowLockHeader, you should specify the height of a day within
the schedule view by using the DayViewHeight property, so the vertical
scroll bar will be shown. The following sample handles the
LayoutEndChanging event, so the DayViewHeight property should be updated
once the user selects a new date in the calendar panel.
The following VB sample shows how you can lock the control's header:
' LayoutStartChanging event - Notifies your application once the control's layout is about to be changed.
Private Sub Schedule1_LayoutStartChanging(ByVal Operation As EXSCHEDULELibCtl.LayoutChangingEnum)
Schedule1_LayoutEndChanging (Operation)
End Sub
' LayoutEndChanging event - Notifies your application once the control's layout has been changed.
Private Sub Schedule1_LayoutEndChanging(ByVal Operation As EXSCHEDULELibCtl.LayoutChangingEnum)
If (Operation = exCalendarSelectionChange Or Operation = exLayoutCalendarAutoHide) Then
With Schedule1
.DayViewHeight = 2016
End With
End If
End Sub
With Schedule1
.BeginUpdate
With .Calendar
.SelectDate(#5/20/2012#) = True
.Select exSelectWeek
End With
.ScrollBars = exVertical
.ShowViewCompact = exViewSingleRowLockHeader
.DayViewHeight = 2016
.AllowMoveSchedule = exDisallow
.AllowResizeSchedule = exDisallow
.AllowToggleSchedule = exDisallow
.AllowExchangePanels = exDisallow
.AllowMoveTimeScale = exDisallow
.AllowResizeTimeScale = exDisallow
.AllowMultiDaysEvent = False
.TimeScales.Item(0).MinorTimeRuler = "00:10"
.DayStartTime = "00:00"
.DayEndTime = "24:00"
.OnResizeControl = OnResizeControlEnum.exCalendarAutoHide Or OnResizeControlEnum.exCalendarFit Or OnResizeControlEnum.exResizePanelRight
.EndUpdate
End With
The LayoutStartChanging/LayoutEndChanging events keeps the height of
the day to be larger than control's client area, so we have enough space
for each timeslot. The LayoutStartChanging/LayoutEndChanging events
change the DayViewHeight property when the user changes the selection in
the control's calendar panel.
The control provides events like Click or DblClick that notifies once
the user clicks / double clicks the control. By default, the control's
edits the current event once the user clicks it. You can change the AllowEditEvent
property to exDisallow to prevent editing the current event once the
user clicks it:
The following VB sample displays the event from the cursor, if any:
Private Sub Schedule1_Click()
Dim e As EXSCHEDULELibCtl.Event
With Schedule1
Set e = .EventFromPoint(-1, -1)
If Not e Is Nothing Then
MsgBox e.Start & " " & e.End
End If
End With
End Sub
In VBA/MSAccess, you need to replace the EXSCHEDULELibCtl with
EXSCHEDULELib, else you will be prompted for a compiler error: "Can't find project or library".
The DataSource property binds the control to an ADO.Recordset, ADODB.Recordset objects, DAO recordsets.
The DataField property automatically updates / synchronizes the known property of the event with the associated data field and reverse.
Here's the list
of properties of the Event object you can associated with a field within the
table.
The following screen shot shows the grouped-events
In order to serialize the event's GroupID property to a table you must:
associates the GroupID property with a field within the table, using the
DataField property such as DataField(exEventGroupID) = "GroupID"
ensures that the table includes the field you associated with ("GroupID"
field)
The following sample associates the event's GroupID property with "GroupID" field:
With Schedule1
.BeginUpdate
.Calendar.Selection = #11/11/2013#
.DataField(exEventStartDateTime) = "Start"
.DataField(exEventEndDateTime) = "End"
.DataField(exEventExtraLabel) = "Extra"
.DataField(exEventGroupID) = "GroupID"
.DataSource = CurrentDb.OpenRecordset("Events")
.EndUpdate
End With
The following sample adds and displays the groups:
With Schedule1
.BeginUpdate
.ShowGroupingEvents = True
With .Groups
With .Add(1, "Group 1")
.Visible = True
.EventBackColor = RGB(128, 128, 128)
.Title = "1'st Group"
End With
With .Add(2, "Group 2")
.Visible = True
.EventBackColor = RGB(255, 0, 0)
.Title = "2'nd Group"
End With
End With
.EndUpdate
End With
Yes, it is possible. The Event.Client property gets the event's client rectangle relative to control's top-left corner, as a safe array of [x, y, width, height] type (4-numeric values of VT_I4 type).
The following VB sample shows, hides, moves and resizes the ExComboBox control based on the coordinates of the focused event:
Private Sub Schedule1_LayoutStartChanging(ByVal Operation As EXSCHEDULELibCtl.LayoutChangingEnum)
ComboBox1.Visible = False
End Sub
Private Sub Schedule1_LayoutEndChanging(ByVal Operation As EXSCHEDULELibCtl.LayoutChangingEnum)
With ComboBox1
.Visible = False
If (Operation = exScheduleSelectionChange) Then
Dim e As EXSCHEDULELibCtl.Event
Set e = Schedule1.EventFromPoint(-1, -1)
If Not (e Is Nothing) Then
.Left = e.Client(0) * Screen.TwipsPerPixelX + Schedule1.Left
.Width = e.Client(2) * Screen.TwipsPerPixelX
.Top = (e.Client(1) + e.Client(3)) * Screen.TwipsPerPixelY + Schedule1.Top - .Height
End If
.Visible = True
End If
End With
End Sub
The form contains two controls: ComboBox1 (of Exontrol.ComboBox, exontrol.EXCOMBOBOXLib.excombobox, wpf.exontrol.EXCOMBOBOXLib.excombobox type) and Schedule1 (of Exontrol.Schedule, exontrol.EXSCHEDULELib.exschedule, wpf.exontrol.EXSCHEDULELib.exschedule type). This sample is just a basic idea on how you can accomplish the task, and can be easily improved. For instance, Screen.TwipsPerPixelX or Screen.TwipsPerPixelY are not required on /NET or /WPF version. On Access you should use 15 instead or check the link.