Click here to Skip to main content
Click here to Skip to main content

Creating the Microsoft Outlook Appointment View in WPF - Part 1

, 22 Apr 2009
Rate this:
Please Sign up or sign in to vote.
Part 1 of a series describing creation of a WPF based appointment control with the Outlook look and feel

Article Series

This article is part one of a series of articles on developing an advanced WPF control designed to function like the Calendar view in Microsoft Outlook, shown below:

OutlookWpfCalendarPart1_1_small_.PNG

Introduction

About six months ago, I began on a initiative to learn Windows Presentation Foundation (WPF). As part of that initiative, I decided I needed a project to apply my skills against, one with sufficient variation of issues to solve, but one which would also minimize the amount of non-WPF code I would have to write. I decided to write a WPF based on Microsoft Outlook, utilizing the Outlook COM API as the underlying domain layer. At some point, I decided to build the necessary controls and components to display the Calendar view. In this series of articles, I will provide the design of the control both for purposes of publishing the code and to help others developing custom controls by explaining some of the design decisions made during the process.

Requirements

In developing the control, I decided upon a number of requirements:

  • Support multiple views of the appointment data (one day, one week, etc.)
  • Support binding the underlying data to an arbitrary collection of objects rather than require utilization of a pre-defined Appointment class
  • Support "look-less" control design
  • Support custom styling of all visual aspects of the control
  • Support overlapping appointments (for example, an appointment from 1 PM - 3 PM and an appointment from 2 PM - 4 PM)
  • Support displaying more than one day within a single view (similar to the "Week View" in Microsoft Outlook)
  • Support reuse of the underlying classes in other applicable scenarios (to avoid making all the code specific to the Appointment View)
  • Support arbitrary beginning and end of the calendar period(s) shown
    For example, rather than support showing one day as midnight to midnight, allow 9 AM - 6 PM or even a 48 hour period across 2 days.

Background

This article series depends on the reader having advanced knowledge of Windows Presentation Foundation as it delves into topics such as item container generators, custom layout controls, etc.

Step 1 - Basic Control Class Design

The first step was to figure out the basic class design for the control itself and what properties would be needed. The two main options I had were to use the ListView/ViewBase combination or to inherit from Selector (the parent class of controls such as ListBox and ComboBox). To support multiple views, I decided to make use of the ListView component, which supports multiple views of the underlying data view the ListView.View property (which is of type ViewBase). The most common view used with a ListView is the GridView. There is a sample in the MSDN Library about creating a custom ViewBase class here. Because of the usage of ListView/ViewBase, which is different in some ways from inheriting from a class such as Selector, not all the design points are directly applicable to more standard custom control creation.

The next step is to define the properties the class will need. To fulfill the requirement to display multiple days within a single view, I followed a design similar to GridView, which has a Columns collection containing individual GridViewColumn instances. Similarly, the CalendarView class will need a CalendarViewPeriod data class and CalendarViewPeriodCollection collection class. Again, I used the GridView helper classes to determine the appropriate base classes to use. Inheriting CalendarViewPeriod from DependencyObject is necessary to permit data binding to the various properties of the class. Inheriting CalendarViewPeriodCollection from ObservableCollection automatically provides capability to listen for modifications to the list (add/remove/insert/replace/etc.), which will be needed later.

using System;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace OutlookWpfCalendar.Windows.Controls
{
    public class CalendarView: ViewBase
    {
        private CalendarViewPeriodCollection periods =
				new CalendarViewPeriodCollection();

        public CalendarViewPeriodCollection Periods
        {
            get { return periods; }
        }
    }

    public class CalendarViewPeriod: DependencyObject
    {
    }

    public class CalendarViewPeriodCollection: ObservableCollection<CalendarViewPeriod>
    {
    }
}

Each period will need properties to define the beginning and end of itself, as well as the header (which won't be used at this time). Each of these will be defined as dependency properties to support data binding.

public class CalendarViewPeriod: DependencyObject
{
    public static readonly DependencyProperty BeginProperty =
		DependencyProperty.Register("Begin", typeof(DateTime),
		typeof(CalendarViewPeriod));
    public static readonly DependencyProperty EndProperty =
		DependencyProperty.Register("End", typeof(DateTime),
		typeof(CalendarViewPeriod));
    public static readonly DependencyProperty HeaderProperty =
		DependencyProperty.Register("Header", typeof(object),
		typeof(CalendarViewPeriod));

    public DateTime Begin
    {
        get { return (DateTime)this.GetValue(BeginProperty); }
        set { this.SetValue(BeginProperty, value); }
    }

    public DateTime End
    {
        get { return (DateTime)this.GetValue(EndProperty); }
        set { this.SetValue(EndProperty, value); }
    }

    public object Header
    {
        get { return (object)this.GetValue(HeaderProperty); }
        set { this.SetValue(HeaderProperty, value); }
    }
}

To support binding to an arbitrary collection instead of a collection of fixed appointment classes, it is necessary to provide a method to ask each object within the ItemsSource what the begin and end time for that appointment is. This will be done by asking for a binding, much like how the GridViewColumn class requires a binding (DisplayMemberBinding) to extract the value from a particular item to be displayed for that column within the GridView.

public class CalendarView: ViewBase
{
    // Existing code excluded to emphasize changes

    public BindingBase ItemBeginBinding { get; set; }

    public BindingBase ItemEndBinding { get; set; }
}

Step 2 - Basic Visual Tree

The next step is to define the styles to be used when rendering the ViewBase's underlying visual tree as well as the style to be used when rendering each ListViewItem (each ListViewItem is a control meant to render a single data object contained with the ItemsSource of the ListView). This is done via overriding of two ViewBase properties within the CalendarView class and adding two styles to the Themes/Generic.xaml file. Note the use of the ComponentResourceKey class. While it would be legitimate to just return a raw string, there could be the potential for a name conflict down the road. A ComponentResourceKey essentially adds a qualifier of a Type instance, permitting multiple styles with the same "name" (or in this case, Resource ID). The first style targets the ListView as a whole and for now renders just a StackPanel, with the individual ListViewItem instances contained inside it (via the IsItemsHost being set to true). The second style targets each ListViewItem and renders a TextBlock bound to the underlying data object itself (in this case, the appointment), which will render the result of invoking the ToString() method on the data object.

public class CalendarView: ViewBase
{
    // Existing code excluded to emphasize changes

    protected override object DefaultStyleKey
    {
        get { return new ComponentResourceKey(this.GetType(), "DefaultStyleKey"); }
    }

    protected override object ItemContainerDefaultStyleKey
    {
        get { return new ComponentResourceKey(this.GetType(),
				"ItemContainerDefaultStyleKey"); }
    }
}
<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:controls="clr-namespace:OutlookWpfCalendar.Windows.Controls">

    <Style x:Key="{ComponentResourceKey TypeInTargetAssembly=
		{x:Type controls:CalendarView}, ResourceId=DefaultStyleKey}"
		TargetType="{x:Type ListView}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ListView}">

                    <StackPanel IsItemsHost="True" />
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <Style x:Key="{ComponentResourceKey TypeInTargetAssembly=
		{x:Type controls:CalendarView},
		ResourceId=ItemContainerDefaultStyleKey}"
		TargetType="{x:Type ListViewItem}">

        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ListViewItem}">
                    <TextBlock Text="{Binding}" />
                </ControlTemplate>
            </Setter.Value>

        </Setter>
    </Style>
</ResourceDictionary>   

Step 3 - Display Initial View with Customized Item Template

Included with the download is a sample application that includes a simple Appointment class and the following instance of the CalendarView class (and the underlying result):

<Window x:Class="OutlookWpfCalendar.UI.CalendarViewWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:OutlookWpfCalendar.UI"
    xmlns:controls="clr-namespace:OutlookWpfCalendar.Windows.Controls;
			assembly=OutlookWpfCalendar"
    Title="Calendar View Window" Height="500" Width="400">
    <Grid>
        <ListView>
            <ListView.View>
                <controls:CalendarView ItemBeginBinding="{Binding Path=Start}"
			ItemEndBinding="{Binding Path=Finish}">
                    <controls:CalendarView.Periods>

                        <controls:CalendarViewPeriod Header="Monday"
			Begin="03/02/2009 12:00 AM" End="03/03/2009 12:00 AM" />
                        <controls:CalendarViewPeriod Header="Tuesday"
			Begin="03/03/2009 12:00 AM" End="03/04/2009 12:00 AM" />
                        <controls:CalendarViewPeriod Header="Wednesday"
			Begin="03/04/2009 12:00 AM" End="03/05/2009 12:00 AM" />
                        <controls:CalendarViewPeriod Header="Thursday"
			Begin="03/05/2009 12:00 AM" End="03/06/2009 12:00 AM" />
                        <controls:CalendarViewPeriod Header="Friday"
			Begin="03/06/2009 12:00 AM" End="03/07/2009 12:00 AM" />
                    </controls:CalendarView.Periods>

                </controls:CalendarView>
            </ListView.View>
            <ListView.Items>
                <local:Appointment Start="03/02/2009 2:00 AM"
			Finish="03/02/2009 3:00 AM" Subject="Meet with John"
			Location="Southwest Meeting Room" Organizer="Jim Smith" />
                <local:Appointment Start="03/03/2009 4:00 AM"
			Finish="03/03/2009 5:00 AM" Subject="Meet with Rick"
			Location="Southwest Meeting Room" Organizer="Jim Smith" />
                <local:Appointment Start="03/04/2009 6:00 AM"
			Finish="03/04/2009 6:30 AM" Subject="Meet with Dave"
			Location="Southwest Meeting Room" Organizer="Jim Smith" />

                <local:Appointment Start="03/02/2009 1:30 AM"
			Finish="03/02/2009 5:00 AM" Subject="Meet with Larry"
			Location="Southwest Meeting Room" Organizer="Jim Smith" />
                <local:Appointment Start="03/03/2009 4:30 AM"
			Finish="03/03/2009 7:30 AM" Subject="Meet with Jim"
			Location="Southwest Meeting Room" Organizer="Jim Smith" />
            </ListView.Items>
        </ListView>
    </Grid>
</Window>

OutlookWpfCalendarPart1_2.png

Obviously, the appointments could use a little better styling, which requires a style to be defined for the individual ListViewItem controls that encompass each Appointment. This could be a global style or defined, as shown below, within the context of the ListView itself via the ItemContainerStyle property:

<ListView.ItemContainerStyle>
    <Style TargetType="{x:Type ListViewItem}">
        <Style.Setters>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ListViewItem}">

                        <Border BorderBrush="#5076A7" BorderThickness="1,1,1,1"
				CornerRadius="4,4,4,4">
                            <Border.Background>
                                <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                                    <GradientStop Color="#FFFFFF" Offset="0.0" />
                                    <GradientStop Color="#C0D3EA" Offset="1.0" />
                                </LinearGradientBrush>

                            </Border.Background>
                            <StackPanel TextElement.FontFamily="Segoe UI"
						TextElement.FontSize="12">
                                <TextBlock FontWeight="Bold" Padding="3,0,0,0"
						Text="{Binding Path=Subject}" />
                                <TextBlock Padding="3,0,0,0"
						Text="{Binding Path=Location}" />
                                <TextBlock Padding="3,0,0,0"
						Text="{Binding Path=Organizer}" />
                            </StackPanel>

                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style.Setters>
    </Style>

</ListView.ItemContainerStyle>

OutlookWpfCalendarPart1_3.png

Next Steps

In this article, the initial class structure has been designed and implemented. The focus has been more so on the class design over the visual layout. Other than the look of the individual appointment items, which is simply a style that could be applied in a normal ListBox control, there really is nothing to suggest the Microsoft Outlook Calendar UI. In the following articles, build out of the actual controls necessary to create the true look and feel will occur. This matches the way in which I developed the control, first focusing on the XAML that would provide the necessary flexibility (which drove the class and property design), followed by determining the individual pieces of the visual layout that make up the whole.

History

  • 04/15/2009: Initial version
  • 04/21/2009: Updated article

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Richard Gavel
Architect Catalyst Software Solutions
United States United States
Richard Gavel is a Solutions Architect with Catalyst Software Solutions in Chicago, IL with a focus in Microsoft .NET technologies and SQL databases. He has been a developer in the industry for 12 years and has worked on both small scale and enterprise projects with thick and thin client front ends.

Comments and Discussions

 
GeneralCool article but PinmvpSacha Barber21-Apr-09 9:24 
GeneralRe: Cool article but PinmemberRugbyLeague21-Apr-09 9:30 
GeneralRe: Cool article but PinmemberRichard Gavel21-Apr-09 9:49 
GeneralRe: Cool article but PinmvpSacha Barber21-Apr-09 21:31 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140721.1 | Last Updated 22 Apr 2009
Article Copyright 2009 by Richard Gavel
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid