Working with Dates on Windows Phone 7

This article presents various data editors-all built with the help of MobileForms Toolkit from Resco.

Introduction

Windows Phone 7 (WP7) programming combines several technologies:

  • .NET (WP7 uses a subset of the full .NET class library.)
  • C#
  • Silverlight for WP7. (A simplification of the desktop SVL.)

Mastering these technologies for a programmer coming from (say) Windows Mobile programming is a real problem.

This article shows how to work with dates using MobileForms Toolkit from Resco.

Prerequisites

The target audience is a C# .NET programmer with a reading knowledge of Silverlight. At the very minimum the reader should understand basic Silverlight controls and layout.

You need to have installed Windows Phone Developer Tools.

Further you should install Resco MobileForms Toolkit, Windows Phone 7 Edition from http://www.resco.net/developer/mobileformstoolkit/. The Toolkit contains a set of useful controls that simplify Windows Phone 7 programming.

Simple use cases

WinPhone7_1.png

The screenshots above are notoriously known to all Windows Phone 7 users. Yes, the famous date/time pickers.

The MobileForms Toolkit does not (yet) provide direct access to these pickers, instead it packages them into a few higher level controls. Following screenshot shows three of these controls in action:

WinPhone7_2.png

Here is the corresponding Xaml code:

xmlns:r="clr-namespace:Resco.Controls;assembly=Resco.Controls"
...
<r:DateEditor Text="{Binding Path=ModifiedOn}" TextDecoration="Underline" FormatString="{}{0:D}" />1
<r:TimeEditor Text="{Binding Path=ModifiedOn}" FormatString="{}Modified on {0:t}" />
<r:DetailItemDateTime Label="Last Transaction" DataMember="ModifiedOn" />
<r:DetailItemDateTime Label="Last Transaction" DataMember="ModifiedOn" TimeVisibility="Collapsed" />

1) Leading {} is not part of the actual format string. It is a Xaml escape allowing subsequent use of curly brackets.

The DataContext is set to an object with the property

public DateTime ModifiedOn { get; set; }

All controls act as editors of this property. (The effect is achieved through binding.)

DateEditor/TimeEditor are text controls that open corresponding picker upon a click. The above code demonstrates custom formatting; normally you would not use it.

DetailItemDateTime uses appearance typical for all DetailItem-derived classes 2, i.e. (optional) label and specific look&feel. You can set it up as date or time editor (using DateVisibility/TimeVisibility properties) or as a full DateTime editor (default). Instead of binding, you can use simpler DataMember syntax 3. You can customize date and time formats and a number of other properties.

2) These controls are primarily intended for use in DetailView form where they provide common look&feel. As the sample code demonstrates, they can be used independently as well.

3) DataMember refers to the property name to be used for binding that is created in the class code. This terminology is commonly used in the .NET world for data-related controls. In fact we could replace

DataMember="ModifiedOn"
by explicit binding
Value={Binding Path=ModifiedOn, Mode=TwoWay}

So much for the basic date controls. In the future more similar controls will be added depending on the user requests. The rest of this article will demonstrate some interesting uses of another control - MonthCalendar.

Selecting dates using MonthCalendar

MonthCalendar is a rather advanced control. Its task is to schematically present a series of appointments. However, all we need right now is the ability to select a single date. No appointments 4, just the reaction to selection change.

4) In other words, we shall use null DataSource.

The UI looks as follows:

WinPhone7_3.png

A MonthCalendar control is in the bottom part. You can list individual months and select suitable day.

A TextBlock in the upper part will reflect current selection. This is just to prove the concept. Your arrangement will probably differ - for example a popup dialog with ok/cancel buttons.

The important code is almost trivial:

// Excerpt from MonthCalendarPage.xaml
<StackPanel Orientation="Horizontal">
<r:PhoneTextControl Text="Selected date:  />
<r:PhoneTextControl x:Name="m_selected" />
</StackPanel>
<r:MonthCalendar x:Name="m_calendar" Height="480" SelectionChanged="DayChanged"/>
 

// MonthCalendarPage.xaml.cs
// Calendar has null DataSource; we only react to the item selection.
public partial class MonthCalendarPage : PhoneApplicationPage
{
    public MonthCalendarPage()
    {
        InitializeComponent();
    }
    private void DayChanged(object sender, EventArgs e)
    {
        DateTime? dt = m_calendar.SelectedDate;
        if (dt != null)
        m_selected.Text = m_calendar.SelectedDate.Value.ToString("d");
        // NavigationService.GoBack();         // Also possible: Return to previous page
    }
}

Xaml code defines MonthCalendar instance and a text field providing feedback for date selection. As we mentioned above - this is just a proof of the concept. For the real use you would need to package MonthCalendarPage into a new DateEditor class somehow:

  • It could be a PhoneTextControl-derived class (i.e. a TextBlock with formatting capabilities), that could be bound to a DateTime-based property. The control would open MonthCalendarPage inside a Popup as a reaction to the MouseLeftButtonDown event.
  • Or it could be a HyperlinkButton-derived control able to bind to a DateTime-based property. This solution would replace the use of a Popup by the HyperlinkNavigation.

Both solutions require additional non-trivial programming. A bit easier is the direct usage of MonthCalendarPage. (NavigationService.GoBack()) However, you still need to handle the transfer of the edited date.

Date multi-selector using MonthCalendar

Here is another interesting example - MonthCalendar control used to select multiple dates. The control allows you to page month by month and select/unselect individual days. A brief summary of the current selection is displayed at the top of the page.

WinPhone7_4.png

Calendar is implemented as a collection of 1-day events; the event is represented by DateTime object 5. Except the ICalendarDataSource implementation we shall provide two methods:

  • AddOrRemove() is used as a toggle - A new date is added to the list, an old one is removed.
  • ToString() provides brief summary of the current selection

5) Calendar is a class implementing ICalendarDataSource - a minimalistic interface designed for appointment modeling.
The appointment (event) is a time interval plus some GUI representation. MonthCalendar itself provides default visualization of the appointments (blue rectangles). That's good enough. Concerning the time, we need just one characteristic - the day. Hence we don't need any special class, but can use standard DateTime class right away

public class MyCalendar : ICalendarDataSource
{
    List<DateTime> m_list = new List<DateTime>();          // An event is represented by DateTime object
    // Return list of events between start-end dates
    IEnumerable ICalendarDataSource.GetRange(DateTime start, DateTime end)
    {
        var list = new List<Object>();
        foreach( DateTime d in m_list)   {
            if( start <= d  &&  d <= end )
            list.Add(d);
        }
        return list;
    }
    // Returns time interval describing given event
    void ICalendarDataSource.GetElementRange(object elem, out DateTime start, out DateTime end)      {
        start = end = (DateTime)elem ;           // In our case the event itself describes the time interval
    }
    // Reaction to the click
    public void AddOrRemove(DateTime dt)
    {
        foreach (DateTime d in m_list)  {
            if (d == dt)  {
                m_list.Remove(d);                // Old event is removed
                return;
            }
        }
        m_list.Add(dt);                     // No old event found => add a new event
    }
    // Used for presentation purposes
    public override string ToString()
    {
        DateTime min = DateTime.MaxValue;
        DateTime max = DateTime.MinValue;
        foreach (DateTime d in m_list)  {
            if (min > d)  min = d;
            if (max < d  max = d;
        }
        if (m_list.Count == 0)
            return "Nothing; click some day(s)";
        if (m_list.Count == 1)
            return min.ToString("d");
        else
            return String.Format("{0} days; {1:d} - {2:d}", m_list.Count, min, max);
    }
}

We can finally implement MonthCalendarPage.

// C# code
public partial class MonthCalendarPage : PhoneApplicationPage
{
    // We use static DataSource to provide continuity between multiple uses of MonthCalendarPage.
    //Replace by the implementation that best suits to your needs.
    static MyCalendar DataSource = new MyCalendar();
    public ICalendarDataSource MyCalendar  {
        get { return DataSource; }
}
    public MonthCalendarPage()  {
        DataContext = this;
        InitializeComponent();
        m_selected.Text = DataSource.ToString();
    }
    // SelectionChanged event handler
    private void DayChanged(object sender, EventArgs e)  {
        DateTime? val = m_calendar.SelectedDate;
        if (val != null)  {
            DateTime dt = val.Value;
            DataSource.AddOrRemove(dt);
            m_selected.Text = DataSource.ToString();
            // A trick to force calendar refresh because the ResetList method is private.
            //m_calendar.ResetList();                    // (Will be available in next release.)
            DateTime x = m_calendar.Date;
            m_calendar.Date = m_calendar.Date.AddDays(1);        // Just force Date change
            m_calendar.Date = x;                          // Revert the original ate value
        }
    }
} 

// Excerpt from MonthCalendarPage.xaml
<StackPanel Grid.Row="1">
<StackPanel Orientation="Horizontal">
<r:PhoneTextControl Text="Selected date:" Margin="10" />
<r:PhoneTextControl x:Name="m_selected" />
</StackPanel>
<r:MonthCalendar x:Name="m_calendar" Height="480" SelectionChanged="DayChanged"/>
</StackPanel>

StringToDayTimeConverter

Ever wondered how the dates can be entered into Xaml? For example

<WeekCalendar   ...  Minimum="11/1/2010" Maximum="11/30/2011" Date="today" />

Well, Silverlight is simpler than WPF, but this is one of many examples where one really misses WPF power. We cannot add the ability to read dates to a (for example) system class, but if the code is under our control, then all we need to do is to define suitable TypeConverter attribute for the property we want to use in Xaml.

Here is an example how the WeekCalendar control defines the property used in the Xaml code above:

[TypeConverter(typeof(StringToDateTimeConverter))]               // The only added line
public DateTime Minimum  { get; set; }

StringToDateTimeConverter class is part of the namespace Resco.Controls.Tools. The nice thing about this converter is that it not only handles a lot of date formats (it is based on DateTime.TryParse()), but also special strings "today" and "Today".

Note you don't need to add any converter to numeric types as the Silverlight handles the conversion automatically.

What's Next

Resco is a company with a long tradition of mobile programming covering many platforms and both end-user applications and developer tools. The Toolkit - as it is published now - matured a bit since the first version. However, this is still an early development stage. In the near future existing controls will be enhanced and new ones added.

Any feedback is welcome - either here or directly at Resco forums.

About the Author

Jan Slodicka. Programming for over 30 years. Covered several desktop platforms and programming languages. Since 2003 working for Resco on mobile technologies - Palm OS, Windows Mobile, now Windows Phone 7.

You can contact me at: jano (at) resco (dot) net.


Similar Articles