1
votes

I have found a custom renderer online but there is an error. Does anyone know how can I make a date time control? Currently I am using separate date picker and time picker but I want it to be combined.

I will post the code below that I have found from another post. This is the link to the post Xamarin Forms date and time picker

using System;

using Foundation;
using Test;
using Test.Droid;
using UIKit;
using ObjCRuntime;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

[assembly:ExportRenderer(typeof(MyPicker), typeof(MyPickerRenderer))]
namespace Test.Droid
{
public class MyPickerRenderer : PickerRenderer
{

    string SelectedValue;

    [Obsolete]
    public MyPickerRenderer()
    {

    }

    protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
    {
        base.OnElementChanged(e);

        if (Control != null)
        {
            SetTimePicker();
        }

    }


    void SetTimePicker()
    {
        UIDatePicker picker = new UIDatePicker
        {
            Mode = UIDatePickerMode.DateAndTime
        };

        picker.SetDate(NSDate.Now, true);

        picker.AddTarget(this, new Selector("DateChange:"), UIControlEvent.ValueChanged);

        Control.InputView = picker;


        UIToolbar toolbar = (UIToolbar)Control.InputAccessoryView;

        UIBarButtonItem done = new UIBarButtonItem("Done", UIBarButtonItemStyle.Done, (object sender, EventArgs click) =>
        {
            Control.Text = SelectedValue;
            toolbar.RemoveFromSuperview();
            picker.RemoveFromSuperview();
            Control.ResignFirstResponder();
            MessagingCenter.Send<Object, string>(this, "pickerSelected", SelectedValue);



        });

        UIBarButtonItem empty = new UIBarButtonItem(UIBarButtonSystemItem.FlexibleSpace, null);

        toolbar.Items = new UIBarButtonItem[] { empty, done };

    }

    [Export("DateChange:")]
    void DateChange(UIDatePicker picker)
    {
        NSDateFormatter formatter = new NSDateFormatter();

        formatter.DateFormat = "MM-dd HH:mm aa"; //you can set the format as you want

        Control.Text = formatter.ToString(picker.Date);

        SelectedValue = formatter.ToString(picker.Date);

        MessagingCenter.Send<Object, string>(this, "pickerSelected", SelectedValue);
      }
   }
}
4
"there is an error" - it would be incredibly helpful if you told us what the specific error is.Jason
I am using on android instead of IOS so I am not sure if this is causing the error. The error I got is "Severity Code Description Project File Line Suppression State Error CS1061 'EditText' does not contain a definition for 'ResignFirstResponder' and no accessible extension method 'ResignFirstResponder' accepting a first argument of type 'EditText' could be found (are you missing a using directive or an assembly reference?)"Charis

4 Answers

7
votes

Make the DateTimePicker inherit a ContentView instead of just an Entry, and then creates the Stacklayout which add the Entry and the date and time pickers to content.

See the DateTimePicker2.cs:

  public class DateTimePicker2 : ContentView, INotifyPropertyChanged
{
    public Entry _entry { get; private set; } = new Entry();
    public DatePicker _datePicker { get; private set; } = new DatePicker() { MinimumDate = DateTime.Today, IsVisible = false };
    public TimePicker _timePicker { get; private set; } = new TimePicker() { IsVisible = false };
    string _stringFormat { get; set; }
    public string StringFormat { get { return _stringFormat ?? "dd/MM/yyyy HH:mm"; } set { _stringFormat = value; } }
    public DateTime DateTime
    {
        get { return (DateTime)GetValue(DateTimeProperty); }
        set { SetValue(DateTimeProperty, value); OnPropertyChanged("DateTime"); }
    }

    private TimeSpan _time
    {
        get
        {
            return TimeSpan.FromTicks(DateTime.Ticks);
        }
        set
        {
            DateTime = new DateTime(DateTime.Date.Ticks).AddTicks(value.Ticks);
        }
    }

    private DateTime _date
    {
        get
        {
            return DateTime.Date;
        }
        set
        {
            DateTime = new DateTime(DateTime.TimeOfDay.Ticks).AddTicks(value.Ticks);
        }
    }

    BindableProperty DateTimeProperty = BindableProperty.Create("DateTime", typeof(DateTime), typeof(DateTimePicker2), DateTime.Now, BindingMode.TwoWay, propertyChanged: DTPropertyChanged);

    public DateTimePicker2()
    {
        BindingContext = this;

        Content = new StackLayout()
        {
            Children =
            {
                _datePicker,
                _timePicker,
                _entry
            }
        };

        _datePicker.SetBinding<DateTimePicker2>(DatePicker.DateProperty, p => p._date);
        _timePicker.SetBinding<DateTimePicker2>(TimePicker.TimeProperty, p => p._time);
        _timePicker.Unfocused += (sender, args) => _time = _timePicker.Time;
        _datePicker.Focused += (s, a) => UpdateEntryText();

        GestureRecognizers.Add(new TapGestureRecognizer()
        {
            Command = new Command(() => _datePicker.Focus())
        });
        _entry.Focused += (sender, args) =>
        {
            Device.BeginInvokeOnMainThread(() => _datePicker.Focus());
        };
        _datePicker.Unfocused += (sender, args) =>
        {
            Device.BeginInvokeOnMainThread(() =>
            {
                _timePicker.Focus();
                _date = _datePicker.Date;
                UpdateEntryText();
            });
        };
    }

    private void UpdateEntryText()
    {
        _entry.Text = DateTime.ToString(StringFormat);
    }

    static void DTPropertyChanged(BindableObject bindable, object oldValue, object newValue)
    {
        var timePicker = (bindable as DateTimePicker2);
        timePicker.UpdateEntryText();
    }
}

Usage in Xaml:

<StackLayout>
        <local:DateTimePicker2></local:DateTimePicker2>
    </StackLayout>

enter image description here

0
votes

On Xamarin.forms Android, you could try the code below.

Create a DateTimePicker.cs class.

 public class DateTimePicker : Entry, INotifyPropertyChanged
{
    public DatePicker _datePicker { get; private set; } = new DatePicker() { MinimumDate = DateTime.Today, IsVisible = false };
    public TimePicker _timePicker { get; private set; } = new TimePicker() { IsVisible = false };
    string _stringFormat { get; set; }
    public string StringFormat { get { return _stringFormat ?? "dd/MM/yyyy HH:mm"; } set { _stringFormat = value; } }
    public DateTime DateTime
    {
        get { return (DateTime)GetValue(DateTimeProperty); }
        set { SetValue(DateTimeProperty, value); OnPropertyChanged("DateTime"); }
    }

    private TimeSpan _time
    {
        get
        {
            return TimeSpan.FromTicks(DateTime.Ticks);
        }
        set
        {
            DateTime = new DateTime(DateTime.Date.Ticks).AddTicks(value.Ticks);
        }
    }

    private DateTime _date
    {
        get
        {
            return DateTime.Date;
        }
        set
        {
            DateTime = new DateTime(DateTime.TimeOfDay.Ticks).AddTicks(value.Ticks);
        }
    }

    BindableProperty DateTimeProperty = BindableProperty.Create("DateTime", typeof(DateTime), typeof(DateTimePicker), DateTime.Now, BindingMode.TwoWay, propertyChanged: DTPropertyChanged);

    public DateTimePicker()
    {
        BindingContext = this;
        _datePicker.SetBinding<DateTimePicker>(DatePicker.DateProperty, p => p._date);
        _timePicker.SetBinding<DateTimePicker>(TimePicker.TimeProperty, p => p._time);
        _timePicker.Unfocused += (sender, args) => _time = _timePicker.Time;
        _datePicker.Focused += (s, a) => UpdateEntryText();

        GestureRecognizers.Add(new TapGestureRecognizer()
        {
            Command = new Command(() => _datePicker.Focus())
        });
        Focused += (sender, args) =>
        {
            Device.BeginInvokeOnMainThread(() => _datePicker.Focus());
        };
        _datePicker.Unfocused += (sender, args) =>
        {
            Device.BeginInvokeOnMainThread(() =>
            {
                _timePicker.Focus();
                _date = _datePicker.Date;
                UpdateEntryText();
            });
        };
    }

    private void UpdateEntryText()
    {
        Text = DateTime.ToString(StringFormat);
    }

    static void DTPropertyChanged(BindableObject bindable, object oldValue, object newValue)
    {
        var timePicker = (bindable as DateTimePicker);
        timePicker.UpdateEntryText();
    }
}

Uasge in App.xaml.cs

   var dtPicker = new DateTimePicker()
        {
            VerticalOptions = LayoutOptions.FillAndExpand,
            HorizontalOptions = LayoutOptions.FillAndExpand,
            StringFormat = "HH:mm dd/MM/yyyy"
        };

        MainPage = new ContentPage
        {
            Content = new StackLayout
            {
                VerticalOptions = LayoutOptions.Center,
                Children = {
                    dtPicker._datePicker,
                    dtPicker._timePicker,
                    dtPicker
                },
                BackgroundColor = Color.Aqua
            }
        };

enter image description here

0
votes

This is my Code Behind

public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
    }

    void OnDateSelected(object sender, DateChangedEventArgs args)
    {
        Recalculate();
    }

    void OnSwitchToggled(object sender, ToggledEventArgs args)
    {
        Recalculate();
    }

    void Recalculate()
    {
        TimeSpan timeSpan = endDatePicker.Date - startDatePicker.Date +
        (includeSwitch.IsToggled ? TimeSpan.FromDays(1) : TimeSpan.Zero);

        resultLabel.Text = String.Format("{0} day{1} between dates",
                                        timeSpan.Days, timeSpan.Days == 1 ? "" : "s");
    }
}
-1
votes

I have attached code for date time picker

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         xmlns:local="clr-namespace:DaysBetweenDates"
         x:Class="DaysBetweenDates.MainPage">
<ContentPage.Padding>

    <OnPlatform x:TypeArguments="Thickness">
        <On Platform="iOS" Value="0, 20, 0, 0" />
    </OnPlatform>
</ContentPage.Padding>

<StackLayout Margin="10">
    <Label Text="Days Between Dates"
           Style="{DynamicResource TitleStyle}"
           Margin="0, 20"
           HorizontalTextAlignment="Center" />

    <Label Text="Start Date:" />

    <DatePicker x:Name="startDatePicker"
                Format="D"
                Margin="30, 0, 0, 30"
                DateSelected="OnDateSelected" />

    <Label Text="End Date:" />

    <DatePicker x:Name="endDatePicker"
                MinimumDate="{Binding Source={x:Reference startDatePicker},
                                      Path=Date}"
                Format="D"
                Margin="30, 0, 0, 30"
                DateSelected="OnDateSelected" />

    <StackLayout Orientation="Horizontal"
                 Margin="0, 0, 0, 30">
        <Label Text="Include both days in total: "
               VerticalOptions="Center" />
        <Switch x:Name="includeSwitch"
                Toggled="OnSwitchToggled" />
    </StackLayout>

    <Label x:Name="resultLabel"
           FontAttributes="Bold"
           HorizontalTextAlignment="Center" />
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         xmlns:local="clr-namespace:DaysBetweenDates"
         x:Class="DaysBetweenDates.MainPage">
<ContentPage.Padding>
    <OnPlatform x:TypeArguments="Thickness">
        <On Platform="iOS" Value="0, 20, 0, 0" />
    </OnPlatform>
</ContentPage.Padding>


<StackLayout Margin="10">
    <Label Text="Days Between Dates"
           Style="{DynamicResource TitleStyle}"
           Margin="0, 20"
           HorizontalTextAlignment="Center" />

    <Label Text="Start Date:" />

    <DatePicker x:Name="startDatePicker"
                Format="D"
                Margin="30, 0, 0, 30"
                DateSelected="OnDateSelected" />

    <Label Text="End Date:" />

    <DatePicker x:Name="endDatePicker"
                MinimumDate="{Binding Source={x:Reference startDatePicker},
                                      Path=Date}"
                Format="D"
                Margin="30, 0, 0, 30"
                DateSelected="OnDateSelected" />

    <StackLayout Orientation="Horizontal"
                 Margin="0, 0, 0, 30">
        <Label Text="Include both days in total: "
               VerticalOptions="Center" />
        <Switch x:Name="includeSwitch"
                Toggled="OnSwitchToggled" />
    </StackLayout>

    <Label x:Name="resultLabel"
           FontAttributes="Bold"
           HorizontalTextAlignment="Center" />

</StackLayout>