MindFusion.Wpf Pack Programmer's Guide
Tutorial 5: Custom Item Templates

This tutorial demonstrates how to customize the items presentation through custom item templates.

1. Create and initialize a new WPF application project

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

Set CurrentView to Timetable, Theme to Vista and Date to Jan 1, 2010.

2. Creating the item template

In order to customize the item appearance, you need to define a new control template for the item presentation just what you would do when you are customizing the template of a regular WPF control. Since the Item itself is not a WPF Control-derived class, there is a special class, which is used to represent items in the Calendar control - ItemPresenter. For more information on the ItemPresenter and its role in the item presentation, check Item Presenters.

The easiest way to customize the ItemPresenter's template is to create a copy of the default template and modify it. The full code of the default item template can be found here. The default template contains several triggers, which determine the item's appearance in different views and in different orientations. For simplicity this tutorial will only customize the appearance of the items in vertical Timetable view.

Copy the template listed below to the App.xaml (Application.xaml in Visual Basic) file in your project.

XAML  Copy Code

<Style TargetType="{x:Type planner:ItemPresenter}">
  <Style.Triggers>

    <DataTrigger Binding="{Binding StyleKey, RelativeSource={RelativeSource Self}}" Value="Complex">
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="{x:Type planner:ItemPresenter}">
            <Border DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}"
              Background="{Binding ItemStyle.Background}"
              BorderBrush="{Binding ItemStyle.BorderBrush}"
              BorderThickness="{Binding ItemStyle.BorderThickness}"
              CornerRadius="8"
              Margin="0,5,0,5">

              <Grid>
                <Grid.RowDefinitions>
                  <RowDefinition Height="Auto" />
                  <RowDefinition Height="Auto" />
                  <RowDefinition />
                </Grid.RowDefinitions>

                <Border HorizontalAlignment="Stretch" BorderThickness="0,0,0,1"
                  BorderBrush="{Binding ItemStyle.LineBrush}"
                  Height="{Binding ItemSettings.HeaderSize}"
                  Margin="5,1,5,1">

                  <Grid>
                    <Grid.ColumnDefinitions>
                      <ColumnDefinition />
                      <ColumnDefinition Width="Auto" />
                    </Grid.ColumnDefinitions>

                    <TextBlock x:Name="HeaderBlock"
                      TextWrapping="Wrap"
                      Text="{Binding Item.HeaderText, RelativeSource={RelativeSource TemplatedParent}}"
                      DataContext="{TemplateBinding ItemStyle}"
                      Foreground="{Binding Foreground}"
                      TextAlignment="Left"
                      HorizontalAlignment="Stretch"
                      VerticalAlignment="{Binding VerticalAlignment}"
                      FontFamily="{Binding FontFamily}"
                      FontSize="{Binding FontSize}"
                      FontStretch="{Binding FontStretch}"
                      FontStyle="{Binding FontStyle}"
                      FontWeight="{Binding FontWeight}" />
                    <StackPanel Orientation="Horizontal" Grid.Column="1">
                      <Image Source="pack://application:,,,/MindFusion.Scheduling.Wpf;component/Resources/Recurrence.png"
                        Visibility="{Binding RecurrenceIconVisibility}" Margin="1" />
                      <Image Source="pack://application:,,,/MindFusion.Scheduling.Wpf;component/Resources/RecurrenceEx.png"
                        Visibility="{Binding RecurrenceExceptionIconVisibility}" Margin="1" />
                      <Image Source="pack://application:,,,/MindFusion.Scheduling.Wpf;component/Resources/Reminder.png"
                        Visibility="{Binding ReminderIconVisibility}" Margin="1" />
                    </StackPanel>
                  </Grid>
                </Border>
                <Border Grid.Row="1" Margin="5,1,5,1" DataContext="{TemplateBinding Item}">
                  <StackPanel Orientation="Vertical">
                    <TextBlock Text="{Binding StartTime}" />
                    <TextBlock Text="{Binding EndTime}" />
                  </StackPanel>
                </Border>
                <Border HorizontalAlignment="Stretch" BorderThickness="0,1,0,0"
                  Grid.Row="2"
                  BorderBrush="{Binding ItemStyle.LineBrush}"
                  Margin="5,1,5,1">
                  <TextBlock x:Name="DescriptionBlock"
                    DataContext="{TemplateBinding Item}"
                    TextWrapping="Wrap" Text="{Binding DescriptionText}" />
                </Border>
              </Grid>
            </Border>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </DataTrigger>

    <MultiDataTrigger>
      <MultiDataTrigger.Conditions>
        <Condition Binding="{Binding StyleKey, RelativeSource={RelativeSource Self}}" Value="Simple" />
        <Condition Binding="{Binding Orientation, RelativeSource={RelativeSource Self}}" Value="Horizontal" />
      </MultiDataTrigger.Conditions>

      <!-- Simple horizontal template omitted for clarity. -->

    </MultiDataTrigger>

    <MultiDataTrigger>
      <MultiDataTrigger.Conditions>
        <Condition Binding="{Binding StyleKey, RelativeSource={RelativeSource Self}}" Value="Simple" />
        <Condition Binding="{Binding Orientation, RelativeSource={RelativeSource Self}}" Value="Vertical" />
      </MultiDataTrigger.Conditions>

      <!-- Simple vertical template omitted for clarity. -->

    </MultiDataTrigger>

  </Style.Triggers>
</Style>

The planner namespace is defined in the <Application> tag as follows:

XAML  Copy Code

xmlns:planner="clr-namespace:MindFusion.Scheduling.Wpf;assembly=MindFusion.Scheduling.Wpf"

The local namespace is set to the namespace of your project (Tutorial5 by default).

The first line of the above XAML code automatically associates the defined style with all ItemPresenter instances.

XAML  Copy Code

<Style TargetType="{x:Type planner:ItemPresenter}">

The first trigger defined in the style is matched for items displayed in Timetable and Resource views. Those items are identified when the value of the StyleKey property is set to "Complex".

XAML  Copy Code

<DataTrigger Binding="{Binding StyleKey, RelativeSource={RelativeSource Self}}" Value="Complex">

The other two triggers are matched for horizontal and vertical items displayed in SingleMonth, MonthRange, List and WeekRange views. The value of the StyleKey property for those items is set to "Simple". This tutorial uses the default templates for these two types of presentation and therefore they are omitted above for clarity.

The new template is similar to the default one with the following differences:

Without making any further modifications, run the sample, create a new item and see the newly defined template applied to it. The following image illustrates how an item will look like in the Vista theme.

3. Formatting the start and end time

Looking at the new item presentation you can see that the start and end dates displayed below the item's header are formatted using the default DateTime formatting and are not very readable. Let's define our custom IValueConverter class, which will enable us to display the dates in a custom format during databinding. For this purpose, create a new class - FormattingConverter and implement it as illustrated below.

C#  Copy Code

class FormattingConverter : System.Windows.Data.IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        string format = parameter as string;
        if (format == null)
            return value.ToString();

        return string.Format(culture, format, value);
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Visual Basic  Copy Code

Class FormattingConverter
    Implements System.Windows.Data.IValueConverter

    Public Function Convert(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.Convert

        Dim format As String = CStr(parameter)
        If format Is Nothing Then
            Return value.ToString()
        End If

        Return String.Format(culture, format, value)

    End Function

    Public Function ConvertBack(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.ConvertBack

        Throw New NotImplementedException()

    End Function

End Class

The above class takes a string argument and uses this string in order to format the input value. The next step is to create a static resource object of this class. Insert the following line before the <Style> definition in App.xaml (Application.xaml in Visual Basic):

XAML  Copy Code

<local:FormattingConverter x:Key="formattingConverter" />

Now locate the following two lines in the template:

XAML  Copy Code

<TextBlock Text="{Binding StartTime}" />
<TextBlock Text="{Binding EndTime}" />

And modify them as following:

XAML  Copy Code

<TextBlock Text="{Binding StartTime, Converter={StaticResource formattingConverter}, ConverterParameter='Start time: \{0:H:mm\}', Mode=Default}" />
<TextBlock Text="{Binding EndTime, Converter={StaticResource formattingConverter}, ConverterParameter='End time: \{0:H:mm\}', Mode=Default}" />

The above code instructs the Text bindings to format their source values using the "Start time: {0:H:mm}" and "{End time: {0:H:mm}" formatting strings respectively.

4. Build and run

Compile and run the application. Creating a new item with a Reminder and sample description will yield a result similar to the following: