Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Xamarin.Forms DatePicker with hours, minutes and seconds: TimeCountdownPicker
/*
Xaml example.
*/
<?xml version="1.0" encoding="utf-8"?>
<ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:YourNameSpace"
x:Class="YourNameSpace.ContentPage">
<local:TimeCountdownPicker SelectedTime="{Binding Countdown}" WidthRequest="85" />
</ContentPage>
/*
Special thanks:
@Mathieu who shared his solution on stackoVerflow.
http://stackoverflow.com/questions/35931470/timepicker-with-seconds/35954206#35954206
http://stackoverflow.com/users/659546/mathieu
Original solution was based on @XLabs, in this example the dependency is removed.
https://github.com/XLabs/Xamarin-Forms-Labs
*/
using System;
using Xamarin.Forms;
namespace YourNameSpace
{
public class TimeCountdownPicker : Picker
{
public static readonly BindableProperty SelectedTimeProperty =
BindableProperty.Create(nameof(SelectedTime), typeof(TimeSpan), typeof(TimeCountdownPicker), defaultValue: TimeSpan.Zero, defaultBindingMode: BindingMode.TwoWay, propertyChanged: OnSelectedTimePropertyPropertyChanged);
public TimeCountdownPicker()
{
// Add only one item, later will manipulate only it's value for performance
Items.Add("00:00:00");
SelectedIndex = 0;
SelectedTime = TimeSpan.Zero;
}
public TimeSpan SelectedTime
{
get { return (TimeSpan)GetValue(SelectedTimeProperty); }
set { SetValue(SelectedTimeProperty, value); }
}
private static void OnSelectedTimePropertyPropertyChanged(BindableObject bindable, object value, object newValue)
{
var picker = (TimeCountdownPicker)bindable;
// Update value
picker.Items[0] = newValue.ToString();
picker.SelectedIndex = 0;
}
}
}
/*
Special thanks:
@Mathieu who shared his solution on stackoVerflow.
http://stackoverflow.com/questions/35931470/timepicker-with-seconds/35954206#35954206
http://stackoverflow.com/users/659546/mathieu
Original solution was based on @XLabs, in this example the dependency is removed.
https://github.com/XLabs/Xamarin-Forms-Labs
*/
using System;
using CoreGraphics;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using YourNameSpace;
using YourNameSpace.iOS;
[assembly: ExportRendererAttribute(typeof(TimeCountdownPicker), typeof(TimeCountdownPickerRenderer))]
namespace YourNameSpace.iOS
{
public class TimeCountdownPickerRenderer : PickerRenderer
{
public static double DisplayScale = UIScreen.MainScreen.Scale;
public static int DispalyHeight = (int)UIScreen.MainScreen.NativeBounds.Height;
public static int DisplayWidth = (int)UIScreen.MainScreen.NativeBounds.Width;
internal const int ComponentCount = 6;
private const int _labelSize = 30;
private TimeCountdownPicker timeCountdownPicker;
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Control.BorderStyle = UITextBorderStyle.None;
timeCountdownPicker = e.NewElement as TimeCountdownPicker;
var customModelPickerView = new UIPickerView
{
Model = new TimeCountdownPickerView(timeCountdownPicker)
};
SelectPickerValue(customModelPickerView, timeCountdownPicker);
CreatePickerLabels(customModelPickerView);
Control.InputView = customModelPickerView;
}
}
private void SelectPickerValue(UIPickerView customModelPickerView, TimeCountdownPicker myTimePicker)
{
if (myTimePicker == null)
return;
customModelPickerView.Select(new nint(myTimePicker.SelectedTime.Hours), 0, false);
customModelPickerView.Select(new nint(myTimePicker.SelectedTime.Minutes), 2, false);
customModelPickerView.Select(new nint(myTimePicker.SelectedTime.Seconds), 4, false);
}
private void CreatePickerLabels(UIPickerView customModelPickerView)
{
nfloat verticalPosition = (customModelPickerView.Frame.Size.Height / 2) - (_labelSize / 2);
nfloat componentWidth = new nfloat(DisplayWidth / ComponentCount /DisplayScale);
var hoursLabel = new UILabel(new CGRect(componentWidth, verticalPosition, _labelSize, _labelSize));
hoursLabel.Text = "h";
var minutesLabel = new UILabel(new CGRect((componentWidth * 3) + (componentWidth / 2), verticalPosition, _labelSize, _labelSize));
minutesLabel.Text = "m";
var secondsLabel = new UILabel(new CGRect((componentWidth * 5) + (componentWidth / 2), verticalPosition, _labelSize, _labelSize));
secondsLabel.Text = "s";
customModelPickerView.AddSubview(hoursLabel);
customModelPickerView.AddSubview(minutesLabel);
customModelPickerView.AddSubview(secondsLabel);
}
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (Control == null)
return;
if (e.PropertyName == Picker.SelectedIndexProperty.PropertyName)
{
var customModelPickerView = (UIPickerView)Control.InputView;
SelectPickerValue(customModelPickerView, timeCountdownPicker);
}
}
public class TimeCountdownPickerView : UIPickerViewModel
{
private TimeCountdownPicker timeCountdownPicker { get; }
public TimeCountdownPickerView(TimeCountdownPicker picker)
{
timeCountdownPicker = picker;
}
public override nint GetComponentCount(UIPickerView pickerView)
{
return new nint(ComponentCount);
}
public override nint GetRowsInComponent(UIPickerView pickerView, nint component)
{
if (component == 0)
{
// Hours
return new nint(24);
}
if (component % 2 != 0)
{
// Odd components are labels for hrs, mins and secs
return new nint(1);
}
// Minutes & seconds
return new nint(60);
}
public override string GetTitle(UIPickerView pickerView, nint row, nint component)
{
if (component == 0)
{
return row.ToString();
}
else if (component == 1)
{
return null;
}
else if (component == 3)
{
return null;
}
else if (component == 5)
{
return null;
}
return row.ToString("#0");
}
public override void Selected(UIPickerView pickerView, nint row, nint component)
{
var selectedHours = pickerView.SelectedRowInComponent(0);
var selectedMinutes = pickerView.SelectedRowInComponent(2);
var selectedSeconds = pickerView.SelectedRowInComponent(4);
var time = new TimeSpan((int)selectedHours, (int)selectedMinutes, (int)selectedSeconds);
timeCountdownPicker.SelectedTime = time;
}
public override nfloat GetComponentWidth(UIPickerView pickerView, nint component)
{
var screenWidth = DisplayWidth;
var componentWidth = screenWidth /
ComponentCount /
DisplayScale;
return new nfloat(componentWidth);
}
}
}
}
@marcusts

This comment has been minimized.

Copy link

commented Feb 27, 2018

Hi, do you have an Android renderer for this? Thanks!

@yuv4ik

This comment has been minimized.

Copy link
Owner Author

commented Apr 2, 2018

Hello @marcusts, unfortunately not.
I don't want to promise anything, but I will think of the Android implementation in the near future.

@cullenjohnson

This comment has been minimized.

Copy link

commented Apr 3, 2018

I had to remove the "h", "m", and "s" labels, as they caused the time values to be displayed as "..." on tiny iPod touch screens. They weren't positioning properly for me anyway.

@yuv4ik

This comment has been minimized.

Copy link
Owner Author

commented Apr 5, 2018

@cullenjohnson I didn't get to test on smaller screens. I guess you could tweak it in CreatePickerLabels method.
Thanks for letting me now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.