FAQ (exschedule)
Exontrol.COM Software - Frequently Asked Questions - ExSchedule Component
1:
The control's release notes can be found on our web site, looking for the Release Notes column in the control's main page. Click here for direct link.
2:
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
        
3:
By default, the calendar panel can be:
  • 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.
4:
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.
5:
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.
6:
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:

<EBN> ::= <elements> | <root> "(" [<elements>] ")"
<elements> ::= <element> [ "," <elements> ]
<root> ::= "root" [ <attributes> ] | [ <attributes> ]
<element> ::= <anchor> [ <attributes> ] [ "(" [<elements>] ")" ]
<anchor> ::= "none" | "left" | "right" | "client" | "top" | "bottom"
<attributes> ::= "[" [<client> ","] <attribute> [ "," <attributes> ] "]"
<client> ::= <expression> | <expression> "," <expression> "," <expression> "," <expression>
<expression> ::= <number> | <number> "%"
<attribute> ::= <backcolor> | <text> | <wordwrap> | <align> | <pattern> | <patterncolor> | <frame> | <framethick> | <data> | <others>
<equal> ::= "="
<digit> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
<decimal> ::= <digit><decimal>
<hexadigit> ::= <digit> | "A" | "B"  "C" | "D" | "E"  "F"
<hexa> ::= <hexadigit><hexa>
<number> ::= <decimal> | "0x" <hexa>
<color> ::= <rgbcolor> | number
<rgbcolor> ::= "RGB" "(" <number> "," <number> "," <number> ")"
<string> ::= "`" <characters> "`" | "'" <characters> "'" | " <characters> "
<characters> ::= <char>|<characters>
<char> ::= <any_character_excepts_null>
<backcolor> ::= "back" <equal> <color>
<text> ::= "text" <equal> <string>
<align> ::= "align" <equal> <number>
<pattern> ::= "pattern" <equal> <number>
<patterncolor> ::= "patterncolor" <equal> <color>
<frame> ::= "frame" <equal> <color>
<data> ::= "data" <equal> <number> | <string>
<framethick> ::= "framethick"
<wordwrap> ::= "wordwrap"
Others like: pic, stretch, hstretch, vstretch, transparent, from, to are reserved for future use only.
7:
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.

8:
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
9:
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.
10:
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".
11:

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
12:

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.

How-To Questions
General Questions