Today, I want to share with you my last developed control : a WinRT Calendar Control
This control is very simple. It’s based on 3 ListBox. There is no infinite scroll because a simple ListBox doesn’t support such Behavior.
You can grab the code on codeplex : https://xamlwinrtcalendar.codeplex.com/
This is a short video on how this calendar works in a simple Windows 8 application :
Some particular points of interests :
By design the Visual States and Transition from the Listbox control didn’t work correctly with the control, I had to make my own transitions and disable standards transitions.
There is three States : Selected, UnSelected and Transparent:
[TemplateVisualState(Name = "PickerItemSelected", GroupName = "Picker")][TemplateVisualState(Name = "PickerItemUnSelected", GroupName = "Picker")][TemplateVisualState(Name = "PickerItemTransparent", GroupName = "Picker")]public sealed class PickerSelectorItem : ListBoxItem{..
And the Template of course :
<VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="Picker"> <VisualState x:Name="PickerItemTransparent"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="InnerGrid"> <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource TransparentColor}"/> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentPresenter"> <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource TransparentColor}"/> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> <VisualState x:Name="PickerItemSelected"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="InnerGrid"> <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PurpleColor}"/> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentPresenter"> <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource WhiteColor}"/> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> <VisualState x:Name="PickerItemUnSelected"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="InnerGrid"> <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource DarkGrayColor}"/> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentPresenter"> <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource WhiteColor}"/> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> </VisualStateGroup> <VisualStateGroup x:Name="CommonStates"> <VisualState x:Name="Normal"> </VisualState> <VisualState x:Name="PointerOver" > </VisualState> <VisualState x:Name="Disabled" > </VisualState> <VisualState x:Name="Pressed" > </VisualState> </VisualStateGroup> <VisualStateGroup x:Name="SelectionStates"> <VisualState x:Name="Unselected"> </VisualState> <VisualState x:Name="Selected"> </VisualState> <VisualState x:Name="SelectedUnfocused" > </VisualState> <VisualState x:Name="SelectedDisabled" > </VisualState> <VisualState x:Name="SelectedPointerOver"> </VisualState> <VisualState x:Name="SelectedPressed"> </VisualState> </VisualStateGroup> <VisualStateGroup x:Name="FocusStates"> <VisualState x:Name="Focused" > </VisualState> <VisualState x:Name="Unfocused"> </VisualState> <VisualState x:Name="PointerFocused"> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups>
When you select an item in one of the three ListBox, you can’t center the ListBoxItem (especialy when it’s the first one) To achieve this goal, there is a simple method : apply some margin on the ItemsPresenter like this:
/// <summary>/// Applies the margin on items presenter to allow first and last item to be centered/// </summary>public void ApplyMarginOnItemsPresenter(){ var itemsPresenter = this.ScrollViewer.GetVisualDescendent<ItemsPresenter>(); Thickness t = new Thickness(0, this.ScrollViewer.ViewportHeight / 2, 0, this.ScrollViewer.ViewportHeight / 2); itemsPresenter.Margin = t;}
We can’t override default animation of the ScrollViewer. Like WPF or Silverlight this action is not accessible.
I need to animate the ScrollViewer to correctly place the selected item in the center of the control. To achieve this goal, I used an internal Scrollbar which will be animated.
On each value change, we will make a scroll to vertical offset on the ScrollViewer.
The slider used :
sliderVertical = new Slider{ SmallChange = 0.0000000001, Minimum = double.MinValue, Maximum = double.MaxValue, StepFrequency = 0.0000000001};sliderVertical.ValueChanged += OnVerticalOffsetChanged;
the Vertical offset changed Handler:
/// <summary>/// Hook to animate the scrollviewer/// </summary>private void OnVerticalOffsetChanged(object sender, RangeBaseValueChangedEventArgs e){ if (this.ScrollViewer == null) return; this.viewer.ViewChanged -= ViewerViewChanged; this.ScrollViewer.ScrollToVerticalOffset(e.NewValue);}
And the animation used to center the item :
/// <summary> /// Select the current item and snap it on the good placement (middle) with animation /// </summary> internal void SelectAndSnapTo(DateTimeWrapper wrapper) { double viewerVerticalOffestTarget = GetViewerVerticalOffestTarget(wrapper); if (viewerVerticalOffestTarget <= 0d) return; storyBoardSnap = new Storyboard(); var animationSnap = new DoubleAnimation { EnableDependentAnimation = true, From = viewer.VerticalOffset, To = viewerVerticalOffestTarget, Duration = animationDuration, EasingFunction = new CubicEase { EasingMode = EasingMode.EaseOut } }; storyBoardSnap.Children.Add(animationSnap); Storyboard.SetTarget(storyBoardSnap, sliderVertical); Storyboard.SetTargetProperty(animationSnap, "Value"); storyBoardSnap.Completed += (sender, o) => { // Reactivate Scrollviewer viewer.VerticalScrollMode = ScrollMode.Enabled; viewer.ViewChanged -= ViewerViewChanged; viewer.ViewChanged += ViewerViewChanged; this.GoToUnFocused(); }; this.GoToFocusState(); // Prevent viewer to scroll beacause the animation will force scrollviewer to scroll viewer.VerticalScrollMode = ScrollMode.Disabled; storyBoardSnap.Begin();
When we select an item, we need to know:
To get the correct item, we can use the ItemContainerGenerator. To get its bound, we can use a GeneralTransform :
rowElement = this.ItemContainerGenerator.ContainerFromItem(dtw) as PickerSelectorItem; if (rowElement == null) return 0d; Point origin, bottom; GeneralTransform transform = rowElement.TransformToVisual(this.ScrollViewer); if (transform == null || !transform.TryTransform(new Point(), out origin) || !transform.TryTransform(new Point(rowElement.ActualWidth, rowElement.ActualHeight), out bottom)) return 0d;
For each item, selected or not, tapped or manipulated, I always need to know what is the center item or what item I tapped and the Delta of vertical offset to animate
Thanks to VerticalOffset and ViewportHeight, we have all what we need to calculate this delta:
// Get delta ScrollViewerdouble viewerVerticalOffset = this.ScrollViewer.VerticalOffset;// Vertical size of the Viewerdouble viewerPortHeight = this.ScrollViewer.ViewportHeight;// get the middlevar middle = viewerPortHeight / 2;// Get top elementvar topElementTarget = (middle - (rowElement.ActualHeight / 2));// Set Deltavar delta = rectItem.Top - topElementTarget;// Déduction du vertical offset cible :return viewerVerticalOffset + delta;
This control supports globalization. You have to specify the correct language “before” the first invoke of the control
In the sample, you can see this code line in the OnLaunched Event :
// Change current application language Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = "fr-FR";
This control is provider “as is”. I know there is some limitations and ( maybe ? ) some bugs. Feel free to post comments here or on Codeplex !
Happy Calendar time
But how can I change the date? The day, month, year are not changing. Is it perhaps Touch-only? I'm using my mouse.
Yes, it's touch only for the moment.
However, you can scroll with the mouse Wheel but the user experience is not very conclusive.
I will add the Keyboard support later.
I'm very much happy to see this datepicker for win 8..but i don know how to use this in my app..can u pls help me..
Nachi, what is your problem exactly ?
I'm developing an windows store app..I need to have a datepicker control in my app.Hw can i use this control in my application..thanks in advance
Nachi,
Just go to the Codeplex website : xamlwinrtcalendar.codeplex.com
Go to the SOURCE CODE tab and clic DOWNLOAD. It will download the Visual Studio Solution. Open it with Visual Studio. You just have to reproduce the sample in your project. That's all.
i am unable to change the date using mouse too.what is the solution for that.