MindFusion WinForms Programmer's Guide
Tutorial 4: Creating Custom Appointment Classes

This tutorial shows how to create a custom schedule item class. For simplicity, the sample demonstrates that by inheriting a class from Appointment.

1. Create and initialize a new Windows Forms application project

Follow steps 1 through 3 from Tutorial 1: Getting Started.

2. Create new class for the custom item

Select Project -> Add Class... from the menu, type the name of the custom item class in the dialog that appears (for example MyApp) and click 'OK'. Include the MindFusion.Scheduling namespace at the beginning of the file.

C#  Copy Code

using MindFusion.Scheduling;

Visual Basic  Copy Code

Imports MindFusion.Scheduling

The custom class will support a new property of type Boolean (called Kept) that will indicate whether the appointment has been kept or cancelled. The implementation of the property is trivial. Simply add a private field of type Boolean and make the property getter setter to return the field and to set the field respectively. The following sample code illustrates this:

C#  Copy Code

public class MyApp : Appointment
{
    public MyApp()
    {
        _kept = true;
    }

    public bool Kept
    {
        get { return _kept; }
        set { _kept = value; }
    }

    private bool _kept;
}

Visual Basic  Copy Code

Class MyApp
    Inherits Appointment

    Public Sub New()

        _kept = True

    End Sub


    Public Property Kept() As Boolean

        Get
            Return _kept
        End Get

        Set(ByVal Value As Boolean)
            _kept = Value
        End Set

    End Property


    Private _kept As Boolean

End Class

 Note

You must provide constructor that takes no argument with your custom item class. MindFusion.Scheduling creates item class instances using reflection and an exception will be thrown if there isn't such constructor available.

3. Set the custom item as default

By default the Calendar control instantiates items of type Appointment when the user creates items interactively (by typing). To make the calendar create items of the new class, we need to add the following line of code in the form's Load event handler.

C#  Copy Code

calendar.InteractiveItemType = typeof(MyApp);

Visual Basic  Copy Code

calendar.InteractiveItemType = GetType(MyApp)

Now all items created by the user at runtime will be instances of MyApp.

4. Test

To test whether the calendar actually creates MyApp instances, add an event handler to the control's ItemClick event. Add the following as a body of the event handler:

C#  Copy Code

if (e.Item is MyApp)
{
    calendar.ResetDrag();
    MessageBox.Show("This is our item.");
}

Visual Basic  Copy Code

If TypeOf e.Item Is MyApp Then

    calendar.ResetDrag()
    MessageBox.Show("This is our item.")

End If

Clicking on an item now should display the message box.

5. Serialization

Serialization of custom items requires additional efforts. Attempts to save a schedule containing items of type MyApp will result in an InvalidOperationException exception. In order to support serialization, we have to register MyApp with the schedule. For this purpose, add the following line of code somewhere in the form's Load event handler:

C#  Copy Code

Schedule.RegisterItemClass(typeof(MyApp), "myapp", 1);

Visual Basic  Copy Code

Schedule.RegisterItemClass(GetType(MyApp), "myapp", 1)

Now the exception won't be thrown, but we still need to override the SaveTo and LoadFrom methods of the Appointment class in order to serialize the custom property Kept. The code below illustrates how this is done for the SaveTo and LoadFrom overloads that serialize item's contents in binary streams.

C#  Copy Code

public override void SaveTo(System.IO.BinaryWriter writer, BinarySerializationContext context)
{
    base.SaveTo(writer, context);
    context.WriteBool(writer, _kept);
}

public override void LoadFrom(System.IO.BinaryReader reader, BinarySerializationContext context)
{
    base.LoadFrom(reader, context);
    _kept = context.ReadBool(reader);
}

Visual Basic  Copy Code

Public Overloads Overrides Sub SaveTo(ByVal writer As System.IO.BinaryWriter, ByVal context As BinarySerializationContext)

    MyBase.SaveTo(writer, context)
    context.WriteBool(writer, context)

End Sub

Public Overloads Overrides Sub LoadFrom(ByVal reader As System.IO.BinaryReader, ByVal context As SerializationContext)

    MyBase.LoadFrom(reader, context)
    _kept = context.ReadBool(reader)

End Sub

A code that implements serialization of items in XML format would look similar to the above, but it will override the other SaveTo and LoadFrom method overloads.

6. Recurrence

Recurrence of items from a custom class is not handled wholly automatically. Let's take a closer look at how recurrence works in MindFusion.Scheduling. When a recurrence pattern is associated with an existing item, the item is said to become the master of the recurrence. Every time the occurrences of a master item must be displayed in a given time range, they are generated by instantiating new objects of the same type as that of the master. This is necessary in order to support infinite recurrences, where the generation of all recurrence instances is not possible. When the occurrences are generated, their properties are copied from the master item. This is done automatically for the built-in item properties, but not for those added by a derived item class. In order to copy the custom properties from the master item to a particular occurrence, you need to override the CopyOccurrence method. The following code sample shows how to do this.

C#  Copy Code

protected override void CopyOccurrence(Item master)
{
    base.CopyOccurrence(master);
    _kept = (master as MyApp).Kept;
}

Visual Basic  Copy Code

Protected Overloads Overrides Sub CopyOccurrence(ByVal master As Item)

    MyBase.CopyOccurrence(master)
    _kept = CType(master, MyApp).Kept

End Sub

In addition, it is recommended to mark an item from a recurrent series as an exception when the value of a custom property changes. Otherwise, the change will be lost the next time the instance is generated.

C#  Copy Code

//...
public bool Kept
{
    get { return _kept; }
    set
    {
        _kept = value;

        if (Recurrence != null)
            Recurrence.MarkException(this, false);
    }
}
//...

Visual Basic  Copy Code

' ...
Public Property Kept() As Boolean

    Get
        Return _kept
    End Get

    Set(ByVal Value As Boolean)

        _kept = Value

        If Not Recurrence Is Nothing Then
            Recurrence.MarkException(Me, False)
        End If

    End Set

End Property
' ...

7. Interactive cloning

Interactive item cloning has been introduced in version 4.0. The users can clone items by holding down a particular key (the SHIFT key by default). To enable your custom items for interactive cloning, you have to override the Clone method, create a new object from your custom type and duplicate the fields of the prototype item. If you do not override the Clone method, MindFusion.Scheduling will still create copies of the item, but they will be of type Appointment instead of the custom item type. Here is a sample implementation of the Clone method.

C#  Copy Code

//...
public override object Clone()
{
    MyApp clone = new MyApp();

    // The following code replicates the code used in
    // the Appointment's Clone method
    clone.AllDayEvent = this.AllDayEvent;
    clone.DescriptionText = this.DescriptionText;
    clone.EndTime = this.EndTime;
    clone.HeaderText = this.HeaderText;
    clone.Location = this.Location;
    clone.Locked = this.Locked;
    clone.Priority = this.Priority;
    clone.Reminder = this.Reminder;
    clone.SelectedStyle = this.SelectedStyle.Clone() as Style;
    clone.StartTime = this.StartTime;
    clone.Style = this.Style.Clone() as Style;
    clone.Tag = this.Tag;
    clone.Task = this.Task;
    clone.Visible = this.Visible;

    foreach (Resource resource in this.Resources)
        clone.Resources.Add(resource);

    foreach (Contact contact in this.Contacts)
        clone.Contacts.Add(contact);

    // Now copy the custom fields
    clone.Kept = this.Kept;

    return clone;
}
//...

Visual Basic  Copy Code

'...
Public Overloads Overrides Function Clone() As Object

    Dim aClone As New MyApp

    ' The following code replicates the code used in
    ' the Appointment's Clone method
    aClone.AllDayEvent = Me.AllDayEvent
    aClone.DescriptionText = Me.DescriptionText
    aClone.EndTime = Me.EndTime
    aClone.HeaderText = Me.HeaderText
    aClone.Location = Me.Location
    aClone.Locked = Me.Locked
    aClone.Priority = Me.Priority
    aClone.Reminder = Me.Reminder
    aClone.SelectedStyle = CType(Me.SelectedStyle.Clone(), Style)
    aClone.StartTime = Me.StartTime
    aClone.Style = CType(Me.Style.Clone(), Style)
    aClone.Tag = Me.Tag
    aClone.Task = Me.Task
    aClone.Visible = Me.Visible

    Dim resource As Resource
    For Each resource In Me.Resources
        aClone.Resources.Add(resource)
    Next resource

    Dim contact As Contact
    For Each contact In Me.Contacts
        aClone.Contacts.Add(contact)
    Next contact

    ' Now copy the custom fields
    aClone.Kept = Me.Kept

    Return aClone

End Function
'...

8. Undo and Redo Support

In order to enable undo/redo of changes done on MyApp, create an ItemState-derived class that contains member corresponding to the additional properties defined by MyApp:

C#  Copy Code

public class MyAppState : ItemState
{
    public override bool Equals(ItemState other)
    {
        MyAppState state = other as MyAppState;
        if (state == null)
            return false;

        if (Kept != state.Kept)
            return false;

        return base.Equals(other);
    }


    public bool Kept
    {
        get { return kept; }
        set { kept = value; }
    }


    private bool kept;
}

Visual Basic  Copy Code

Public Class MyAppState
    Inherits ItemState

    Public Overloads Function Equals(ByVal other As ItemState) As Boolean

        Dim state As MyAppState = CType(other, MyAppState)
        If (state Is Nothing) Then
            Return False
        End If

        If (Kept <> state.Kept) Then
            Return False
        End If

        Return MyBase.Equals(other)

    End Function


    Public Property Kept() As Boolean

        Get
            Return _kept
        End Get
        Set(ByVal value As Boolean)
            _kept = value
        End Set

    End Property


    Private _kept As Boolean

End Class

Now override the CreateState, SaveState and RestoreState methods in the custom appointment class:

C#  Copy Code

protected override ItemState CreateState()
{
    return new MyAppState();
}

protected override ItemState SaveState()
{
    MyAppState state = base.SaveState() as MyAppState;
    state.Kept = this.Kept;
    return state;
}

protected override void RestoreState(ItemState state)
{
    MyAppState myState = state as MyAppState;
    this.Kept = myState.Kept;
    base.RestoreState(state);
}

Visual Basic  Copy Code

Protected Overloads Function CreateState() As ItemState

    Return New MyAppState()

End Function

Protected Overloads Function SaveState() As ItemState

    Dim state As MyAppState = CType(MyBase.SaveState(), MyAppState)
    state.Kept = Me.Kept
    Return state

End Function

Protected Overloads Sub RestoreState(ByVal state As ItemState)

    Dim myState As MyAppState = CType(state, MyAppState)
    Me.Kept = myState.Kept
    MyBase.RestoreState(state)

End Sub