Recently I have been researching and playing around with the Windows Store App development for the Windows 8 and RT. I am used to develop enterprise solution or business application and I thought of may be creating something useful that can be used with a tablet PC. When I was playing with the Windows Store App UI and the available controls, I realize that the most important or commonly used controls such as Calendar, DateTime Picker, GridView, etc are not available in Windows Store App. :(
Therefore, I have no choice but to create my own Calendar control. And, yeah, I know there are 3rd party Calendar controls available such as Telerik but I just want to try to create one by my own. I made it and today I want to share about the making of custom Calendar control in XAML for Windows Store App. This is how my Calendar control look like:
Concept
The logic of the calendar construction is to construct the previous month date boxes first. If the last day of the previous month is on Saturday (last day of the week), then skip making date boxes, otherwise create number of boxes until before the first day of current month.
Then, start appending the current month date boxes and if the row contain 7 boxes, then create a new row and then append more boxes until the last day of the month.
Finally, append the remaining boxes start with the first day of next month until the last day of the week.
Implementation
So, how to implement the above concept?
First, create a UserControl for the Calendar control and design the grid in such way:
And then, create another UserControl for the individual box of the day.
This box user control contain very simple logic which is to display the box background color and text after the user control is loaded.
Now back to the Calendar user control, create a method to display the day of the week (the blue color header boxes as you see from above screenshot). I will just skip the detail, and below is the code snippet of my logic to form the calendar with the boxes (user controls). If you want to see the full detail, scroll down to the end of this post and download my source code.
The calendar formation is divided into 3 parts: Previous Month + Current Month + Next Month.
Before that, I need to create a new event handler to handle the value change after the user click at the the box. Also, I need to create the properties to store the current calendar viewing month value and the selected date value.
Next, create the methods to form the Calendar.
This method is to construct the previous month calendar.
This method is to construct next month calendar.
In the end, you need to execute these above 3 methods to form a complete calendar of the month. Finally, use the above custom control in the page like this with correct namespace:
Then, how to get the selected date value? Just use the following code.
If you are interested with my source code, feel free to download from HERE.
Customize the Calendar for your own purpose and please let me know or drop me a comment if you detect any bug with the Calendar control.
My next post is going to be DateTime Picker for Windows Store App (XAML). Stay tuned.
Therefore, I have no choice but to create my own Calendar control. And, yeah, I know there are 3rd party Calendar controls available such as Telerik but I just want to try to create one by my own. I made it and today I want to share about the making of custom Calendar control in XAML for Windows Store App. This is how my Calendar control look like:
Concept
The logic of the calendar construction is to construct the previous month date boxes first. If the last day of the previous month is on Saturday (last day of the week), then skip making date boxes, otherwise create number of boxes until before the first day of current month.
Then, start appending the current month date boxes and if the row contain 7 boxes, then create a new row and then append more boxes until the last day of the month.
Finally, append the remaining boxes start with the first day of next month until the last day of the week.
Implementation
So, how to implement the above concept?
First, create a UserControl for the Calendar control and design the grid in such way:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!--
Header -->
<Rectangle Grid.Row="0"
Grid.ColumnSpan="3"
Style="{StaticResource
CalendarHeaderBox}" />
<TextBlock Grid.Column="1" Name="CurrentDateText"
Style="{StaticResource
CalendarHeader}" />
<!--
Navigation Button -->
<Button Name="PreviousButton"
Grid.Column="0" Content="<"
HorizontalAlignment="Left" Margin="20"
Tapped="PreviousButton_Tapped"
/>
<Button Name="NextButton"
Grid.Column="2" Content=">"
HorizontalAlignment="Right" Margin="20"
Tapped="NextButton_Tapped"
/>
<!--
Calendar Grid -->
<Grid Grid.Row="1"
Grid.ColumnSpan="3" Name="CalendarGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
</Grid>
</Grid>
And then, create another UserControl for the individual box of the day.
<Grid Name="ItemBox" Style="{StaticResource CalendarItemBox}">
<Rectangle Stroke="Gainsboro"
StrokeThickness="1" ></Rectangle>
<TextBlock Name="ItemValue" Text="1" Style="{StaticResource CalendarItem}"
/>
</Grid>
This box user control contain very simple logic which is to display the box background color and text after the user control is loaded.
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
ItemValue.Text = this.Text;
ItemBox.Style = this.GridStyle;
if (this.Value.CompareTo(DateTime.Today) == 0)
ItemBox.Background = new SolidColorBrush(Colors.Orange);
}
Now back to the Calendar user control, create a method to display the day of the week (the blue color header boxes as you see from above screenshot). I will just skip the detail, and below is the code snippet of my logic to form the calendar with the boxes (user controls). If you want to see the full detail, scroll down to the end of this post and download my source code.
The calendar formation is divided into 3 parts: Previous Month + Current Month + Next Month.
Before that, I need to create a new event handler to handle the value change after the user click at the the box. Also, I need to create the properties to store the current calendar viewing month value and the selected date value.
public DateTime CurrentDate { get; set; }
public DateTime SelectedDate { get; set; }
private event EventHandler<TappedRoutedEventArgs> _selectionChange;
public event EventHandler<TappedRoutedEventArgs> SelectionChange
{
add
{
_selectionChange += value;
}
remove
{
_selectionChange -= value;
}
}
public void OnSelectionChange(object sender, TappedRoutedEventArgs e)
{
if (_selectionChange != null)
_selectionChange(sender, e);
}
Next, create the methods to form the Calendar.
This method is to construct the previous month calendar.
private void InitializePreviousMonthBoxes()
{
DateTime previousMonthDate = this.CurrentDate.AddMonths(-1);
DateTime previousMonthDateIteration = new DateTime(previousMonthDate.Year,
previousMonthDate.Month, DateTime.DaysInMonth(previousMonthDate.Year,
previousMonthDate.Month));
CalendarGrid.RowDefinitions.Add(new RowDefinition() {
Height = new GridLength(1, GridUnitType.Star)
});
for (int dayOfWeek = (int)previousMonthDateIteration.DayOfWeek; dayOfWeek >= 0;
dayOfWeek--)
{
CalendarItem item = new CalendarItem(previousMonthDateIteration,
previousMonthDateIteration.Day.ToString(), Application.Current.Resources["CalendarOtherMonthItemBox"] as Style);
item.PointerEntered += (sender, args)
=>
{
((CalendarItem)sender).GridStyle = Application.Current.Resources["CalendarMouseOverItemBox"] as Style;
};
item.PointerExited += (sender, args)
=>
{
if (((CalendarItem)sender).Value == this.SelectedDate)
((CalendarItem)sender).GridStyle
= Application.Current.Resources["CalendarSelectedItemBox"] as Style;
else
((CalendarItem)sender).GridStyle = Application.Current.Resources["CalendarOtherMonthItemBox"] as Style;
};
//delegate
the tapped event to selection change event
item.Tapped += (sender, args) =>
{
//update
the selected date value
this.SelectedDate = ((CalendarItem)sender).Value;
this.CurrentDate = this.CurrentDate.AddMonths(-1);
InitializeCalendar();
OnSelectionChange(sender, args);
};
item.SetValue(Grid.RowProperty, 1);
item.SetValue(Grid.ColumnProperty, dayOfWeek);
CalendarGrid.Children.Add(item);
previousMonthDateIteration =
previousMonthDateIteration.AddDays(-1);
}
}
This method is to construct current month calendar.
private void InitializeCurrentMonthBoxes()
{
int row = 1;
int maxDay = DateTime.DaysInMonth(this.CurrentDate.Year, this.CurrentDate.Month);
for (int day = 1; day <= maxDay; day++)
{
DateTime dateIteration = new DateTime(this.CurrentDate.Year, this.CurrentDate.Month, day);
int dayOfWeek = (int)dateIteration.DayOfWeek;
CalendarItem item = new CalendarItem(dateIteration, day.ToString(), Application.Current.Resources["CalendarItemBox"] as Style);
item.PointerEntered += (sender, args)
=>
{
((CalendarItem)sender).GridStyle = Application.Current.Resources["CalendarMouseOverItemBox"] as Style;
};
item.PointerExited += (sender, args)
=>
{
if (((CalendarItem)sender).Value == this.SelectedDate)
((CalendarItem)sender).GridStyle
= Application.Current.Resources["CalendarSelectedItemBox"] as Style;
else
((CalendarItem)sender).GridStyle
= Application.Current.Resources["CalendarItemBox"] as Style;
};
//delegate
the tapped event to selection change event
item.Tapped += (sender, args) =>
{
//get the box day value
((CalendarItem)sender).GridStyle = Application.Current.Resources["CalendarSelectedItemBox"] as Style;
//get
the box before selected value and reset the color
var selectedItem = (CalendarItem)CalendarGrid.Children.Single(x =>
x.GetType()
== typeof(CalendarItem)
&&
((CalendarItem)x).Value
== this.SelectedDate);
selectedItem.GridStyle = Application.Current.Resources["CalendarItemBox"] as Style;
//update
the selected date value
this.SelectedDate = ((CalendarItem)sender).Value;
OnSelectionChange(sender, args);
};
//highlight
selected date
if (this.SelectedDate.CompareTo(dateIteration) == 0)
item.GridStyle = Application.Current.Resources["CalendarSelectedItemBox"] as Style;
item.SetValue(Grid.RowProperty, row);
item.SetValue(Grid.ColumnProperty, dayOfWeek);
CalendarGrid.Children.Add(item);
if (dayOfWeek == 6 && day != maxDay)
{
row++;
CalendarGrid.RowDefinitions.Add(new RowDefinition() {
Height = new GridLength(1, GridUnitType.Star)
});
}
}
}
This method is to construct next month calendar.
private void InitializeNextMonthBoxes()
{
DateTime nextMonthDate = this.CurrentDate.AddMonths(1);
DateTime nextMonthDateIteration = new DateTime(nextMonthDate.Year, nextMonthDate.Month, 1);
int lastRow = CalendarGrid.RowDefinitions.Count - 1;
if (nextMonthDateIteration.DayOfWeek != DayOfWeek.Sunday)
for (int dayOfWeek = (int)nextMonthDateIteration.DayOfWeek; dayOfWeek < 7;
dayOfWeek++)
{
CalendarItem item = new CalendarItem(nextMonthDateIteration,
nextMonthDateIteration.Day.ToString(), Application.Current.Resources["CalendarOtherMonthItemBox"] as Style);
item.PointerEntered += (sender,
args) =>
{
((CalendarItem)sender).GridStyle
= Application.Current.Resources["CalendarMouseOverItemBox"] as Style;
};
item.PointerExited += (sender,
args) =>
{
if (((CalendarItem)sender).Value == this.SelectedDate)
((CalendarItem)sender).GridStyle
= Application.Current.Resources["CalendarSelectedItemBox"] as Style;
else
((CalendarItem)sender).GridStyle
= Application.Current.Resources["CalendarOtherMonthItemBox"] as Style;
};
//delegate
the tapped event to selection change event
item.Tapped += (sender, args) =>
{
//update
the selected date value
this.SelectedDate = ((CalendarItem)sender).Value;
this.CurrentDate = this.CurrentDate.AddMonths(1);
InitializeCalendar();
OnSelectionChange(sender,
args);
};
item.SetValue(Grid.RowProperty, lastRow);
item.SetValue(Grid.ColumnProperty, dayOfWeek);
CalendarGrid.Children.Add(item);
nextMonthDateIteration =
nextMonthDateIteration.AddDays(1);
}
}
In the end, you need to execute these above 3 methods to form a complete calendar of the month. Finally, use the above custom control in the page like this with correct namespace:
<Page
x:Class="CalendarControl.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:CalendarControl"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:custom="using:CalendarControl.CustomControl"
mc:Ignorable="d">
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Name="SelectedDateText"
Width="1024" Margin="10" />
<custom:Calendar Grid.Row="1" x:Name="MyCalendar"
Width="1024"
Height="768"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="10"
SelectionChange="Calendar_SelectionChange"
/>
</Grid>
</Page>
private void Calendar_SelectionChange(object sender, TappedRoutedEventArgs e)
{
//if you
get the out of context error, make sure to use the namespace x:
//in
your XAML - x:Name
SelectedDateText.Text =
MyCalendar.SelectedDate.ToString("yyyy-MM-dd");
}
If you are interested with my source code, feel free to download from HERE.
Customize the Calendar for your own purpose and please let me know or drop me a comment if you detect any bug with the Calendar control.
My next post is going to be DateTime Picker for Windows Store App (XAML). Stay tuned.