Last active
December 28, 2020 16:52
Star
You must be signed in to star a gist
Xamarin.Forms DatePicker with hours, minutes and seconds: TimeCountdownPicker
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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); | |
} | |
} | |
} | |
} |
Hello @marcusts, unfortunately not.
I don't want to promise anything, but I will think of the Android implementation in the near future.
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.
@cullenjohnson I didn't get to test on smaller screens. I guess you could tweak it in CreatePickerLabels
method.
Thanks for letting me now.
Thanks so much @yuv4ik!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi, do you have an Android renderer for this? Thanks!