How to Make a Date Picker (Calendar) for C# in Windows 8

 

OK, so you are making an awesome new Windows 8* app using the C# programming language and XAML designer in Visual Studio* 2012.  You open up the designer for your XAML file and want to paste in a calendar item (DatePicker) into your app.  You peruse through the Toolbox to find that…there's nothing there out-of-the-box to do this! What should we do?  Plan B: build one!  This article shows you how!

I wanted to share this with you because after much searching, I couldn't find a trivial solution for this.  Given that it took some effort, I asked myself: why not share my solution? 

To begin, let's use the following FrameworkElement (viewable) components:

- Scrollviewer.  This will allow us to build a list that we can scroll through by using a finger

- StackPanel.  Within each list, we will add "children" to the panel, each of which representing a year, day, or month.

- TextBlock.  We will display the children via text blocks. 

Robust design is important, and so we will use Windows.Globalization.Calendar to build the years, months, and days.  Let's begin! 

Start with the XAML designer.  I created three stack panels, for months, days, and years, through the XAML designer.  I gave these a border and gradient background.  Here is what it looks like along with a small snippet of the XAML code:

<ScrollViewer x:Name="scrollviewer_month" HorizontalAlignment="Left" Height="161" Margin="730,264,0,0" Grid.Row="1" VerticalAlignment="Top" Width="137" FontFamily="Global User Interface" Tapped="month_changed" BorderBrush="#FF073C87" BorderThickness="10,10,0,10">

            <ScrollViewer.Background>

                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">

                    <GradientStop Color="Black"/>

                    <GradientStop Color="#FF1E5FD1" Offset="1"/>

                </LinearGradientBrush>

            </ScrollViewer.Background>

            <StackPanel x:Name="stackpanel_month" Height="282" Width="203">

                <StackPanel Height="282"/>

            </StackPanel>

        </ScrollViewer> 

 

Figure 1: Preview of Calendar Image and Associated XAML Code**


For the sake of robustness, rather than hard-code in the number of months, years, etc, I decided to retrieve this programmatically; this is why I don't define the text blocks in XAML above.  Notice the use of the Tapped keyword above: we will be handling touch events.  Keep reading on.

On the C# code-behind, I created some configurable parameters at the top of the .cs file:

 

        //font size used for Calendar items

        const int FONT_SIZE = 24;

        //increase size of selected item

        const int DELTA_SIZE = 16;

        //vertical padding between list items;

        const int PADDING = 12;

        //the number of months

        int MONTH_COUNT;

        //maximum number of days in a month

        int MAX_DAYS;

        //bound the number of calendar years

        int MIN_YEAR = 1911;

        int MAX_YEAR = 2012;

        int NUM_YEARS;

 

Figure 2: Some Configurable Parameters***

There is nothing really novel here; I just wanted to organize my settings to make playing around with the view more easily.  I did however want to strictly limit the number of years in my case to the set [1911, 2012]. 

Next, we need to actually retrieve the calendar information.  Microsoft has done a great job providing a Windows.Globalization framework with various calendars to support all the different cultures of the world.  Feel free to read more about them here:

http://msdn.microsoft.com/en-us/library/82aak18x(v=vs.71).aspx

 

There are two ways to handle the names of the months; either you can just create an array, or enumerate through the calendar object months (integers), and for each, retrieve the equivalent String name.  I will do the former here.

 

        using Windows.Globalization;

        Windows.Globalization.Calendar date_picker;

String[] month_list = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };

        TextBlock[] month_text_list;

        TextBlock[] day_text_list;

        TextBlock[] year_text_list;

 

Figure 3: Enumerating the Months**

 

Now, while I did hardcode in the month names, I will now show you how more robustly, you can use enumeration to determine the maximum number of days in any given month (for the Calendar in the US, we know this should be 31). 

//use the default calendar to assign counts

                MONTH_COUNT = date_picker.NumberOfMonthsInThisYear;             

                //enumerate through month list for this calendar to

                //determine max number of days possible for a month

                //also, remember which month we currently are at

                int current_month = date_picker.Month;

                for (int i = date_picker.FirstMonthInThisYear; i <= MONTH_COUNT; i++)

                {

                    date_picker.Month = i; //change the current month

                    if (date_picker.NumberOfDaysInThisMonth > MAX_DAYS)

                        MAX_DAYS = date_picker.NumberOfDaysInThisMonth;

                }

                date_picker.Month = current_month;

 

Figure 4: How Many Days are Possible?**

OK, so we now have a count for years, months, and days.  This is where we will then dynamically add individual text block children to the stack panels, with each child representing a selectable item for the user.  I won't show every case here, but will now cover how to do it for the months:

 

month_text_list = new TextBlock[MONTH_COUNT];

stackpanel_month = new StackPanel();

for (int i = 0; i < MONTH_COUNT; i++)

{

                    month_text_list[i] = new TextBlock();

                    month_text_list[i].FontSize = FONT_SIZE;

                    month_text_list[i].Padding = new Thickness(PADDING);

                    month_text_list[i].Text = month_list[i];

                    stackpanel_month.Children.Add(month_text_list[i]);

}

                scrollviewer_month.Content = stackpanel_month;

 

Figure 5: Adding the Months to the Stack Panel**

So, for each month, I give it a name from our array, give it padding so that there is adequate vertical spacing between all months, override the default font size, and create the child.  Finally, I set the scroll viewer (what we made in XAML) content to be the stack panel which is an aggregate of text block children!

One last step: how do we handle selections?  Recall that in XAML, we used the Tapped keyword.  Let's peek at the event handler here:

 

SolidColorBrush select_brush, deselect_brush;

 

select_brush = new SolidColorBrush();

deselect_brush = new SolidColorBrush();

select_brush.Color = Windows.UI.Colors.Yellow;

deselect_brush.Color = Windows.UI.Colors.White;

 

//called when the list of months is tapped

        private void month_changed(object sender, TappedRoutedEventArgs e)       

        {

            //highlight selection if selected

            for (int i = 0; i < MONTH_COUNT; i++)

            {

                month_text_list[i].Foreground = deselect_brush;

                month_text_list[i].FontSize = FONT_SIZE;

                if (month_text_list[i] == e.OriginalSource)

                {

                    month_text_list[i].FontSize += DELTA_SIZE;

                    month_text_list[i].Foreground = select_brush;

                }

            }

        }

 

Figure 6: Which Month was Selected?**

The event argument identifies which month entry (text block) was tapped.  So, we enumerate through the month list to find the match, change the text color as visual feedback to the user, and enlarge the font size to make it stand out.  After user selection, it's quite easy to write the selected data back to the calendar object's month/day/year members (simple one-liner).

When I finished creating the calendar, it looked like this:

 

There you have it!  Now, go have fun!

 

**This sample source code includes XAML code automatically generated in Visual Studio 2012 IDE and is released under the Intel OBL Sample Source Code License (MS-LPL Compatible)

*** This sample source code is released under the Microsoft Limited Public License

(MS-LPL)

 

 

Nähere Informationen zur Compiler-Optimierung finden Sie in unserem Optimierungshinweis.