Articles in this section
Category / Section

How to build a DateTimePicker using the .NET MAUI Picker (SfPicker)

How to Build a DateTimePicker Using the .NET MAUI SfPicker

.NET MAUI SfPicker control supports multi-column pickers, making it ideal for building a custom DateTimePicker with separate columns for month, day, year, hour, and minute.

Step 1: Create a Custom Control

Create a new control (e.g., CustomDateTimePicker) that encapsulates all logic and collections for date and time selection. Inherit from ContentView and add the SfPicker as a Children.

C#

public partial class CustomDateTimePicker : ContentView
{
    private DateTimePickerViewModel viewModel = null!;

    /// <summary>
    /// Bindable property for the selected DateTime
    /// </summary>
    public static readonly BindableProperty SelectedDateTimeProperty =
        BindableProperty.Create(
            nameof(SelectedDateTime),
            typeof(DateTime),
            typeof(CustomDateTimePicker),
            DateTime.Now,
            BindingMode.TwoWay,
            propertyChanged: OnSelectedDateTimePropertyChanged);

    /// <summary>
    /// Event raised when the selected DateTime changes
    /// </summary>
    public event EventHandler<DateTimeChangedEventArgs>? DateTimeChanged;

    public DateTime SelectedDateTime
    {
        get => (DateTime)GetValue(SelectedDateTimeProperty);
        set => SetValue(SelectedDateTimeProperty, value);
    }

    public CustomDateTimePicker()
    {
        InitializeComponent();

        // Initialize the ViewModel
        viewModel = new DateTimePickerViewModel();
        this.BindingContext = viewModel;

        // Set initial selected values in UI
        UpdateDisplayLabel();
    }

    /// <summary>
    /// Opens the DateTime picker dialog
    /// </summary>
    public void OpenPicker()
    {
        if (DateTimePickerControl != null)
        {
            DateTimePickerControl.IsOpen = true;
        }
    }

    /// <summary>
    /// Handle button click to open picker
    /// </summary>
    private void OnOpenPickerClicked(object sender, EventArgs e)
    {
        OpenPicker();
    }

    /// <summary>
    /// Handle selection changed event from SfPicker
    /// </summary>
    private void OnDateTimePickerSelectionChanged(object sender, PickerSelectionChangedEventArgs e)
    {
        if (DateTimePickerControl?.Columns != null)
        {
            try
            {
                // Get selected values from each column
                int selectedMonth = GetSelectedValue(0, viewModel.Months, viewModel.SelectedMonth);
                int selectedDay = GetSelectedValue(1, viewModel.Days, viewModel.SelectedDay);
                int selectedYear = GetSelectedValue(2, viewModel.Years, viewModel.SelectedYear);
                int selectedHour = GetSelectedValue(3, viewModel.Hours, viewModel.SelectedHour);
                int selectedMinute = GetSelectedValue(4, viewModel.Minutes, viewModel.SelectedMinute);

                // Update ViewModel
                viewModel.SelectedMonth = selectedMonth;
                viewModel.SelectedDay = selectedDay;
                viewModel.SelectedYear = selectedYear;
                viewModel.SelectedHour = selectedHour;
                viewModel.SelectedMinute = selectedMinute;

                // Create DateTime from selected values
                var selectedDateTime = new DateTime(selectedYear, selectedMonth, selectedDay, selectedHour, selectedMinute, 0);

                // Update the BindableProperty
                SelectedDateTime = selectedDateTime;

                // Update display label
                UpdateDisplayLabel();

                // Raise the DateTimeChanged event
                DateTimeChanged?.Invoke(this, new DateTimeChangedEventArgs(selectedDateTime));
            }
            catch (Exception ex)
            {
                SelectedDateTimeLabel.Text = $"Error: {ex.Message}";
            }
        }
    }

    /// <summary>
    /// Helper method to get selected value from a column
    /// </summary>
    private int GetSelectedValue(int columnIndex, ObservableCollection<int>? itemsSource, int defaultValue)
    {
        if (DateTimePickerControl?.Columns != null && 
            itemsSource != null && 
            columnIndex < DateTimePickerControl.Columns.Count &&
            itemsSource.Count > 0)
        {
            int selectedIndex = DateTimePickerControl.Columns[columnIndex].SelectedIndex;
            if (selectedIndex >= 0 && selectedIndex < itemsSource.Count)
            {
                return itemsSource[selectedIndex];
            }
        }
        return defaultValue;
    }

    /// <summary>
    /// Update the display label with the formatted DateTime
    /// </summary>
    private void UpdateDisplayLabel()
    {
        try
        {
            var dateTime = viewModel.GetSelectedDateTime();
            SelectedDateTimeLabel.Text = dateTime.ToString("MMMM dd, yyyy HH:mm");
        }
        catch (Exception ex)
        {
            SelectedDateTimeLabel.Text = $"Error: {ex.Message}";
        }
    }

    /// <summary>
    /// Handle SelectedDateTime property changes
    /// </summary>
    private static void OnSelectedDateTimePropertyChanged(BindableObject bindable, object oldValue, object newValue)
    {
        if (bindable is CustomDateTimePicker control && newValue is DateTime dateTime)
        {
            // Update the ViewModel's selected values
            control.viewModel.SelectedYear = dateTime.Year;
            control.viewModel.SelectedMonth = dateTime.Month;
            control.viewModel.SelectedDay = dateTime.Day;
            control.viewModel.SelectedHour = dateTime.Hour;
            control.viewModel.SelectedMinute = dateTime.Minute;

            control.UpdateDisplayLabel();
        }
    }
}

Step 2: Define XAML for the Custom Picker

Define the UI in CustomDateTimePicker.xaml using a VerticalStackLayout, a label for the selected value, and the SfPicker with five columns (Month, Day, Year, Hour, Minute):

XAML:


<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:picker="clr-namespace:Syncfusion.Maui.Picker;assembly=Syncfusion.Maui.Picker"
             x:Class="DateTimePickerSample.Controls.CustomDateTimePicker">

    <VerticalStackLayout Spacing="10">
        <!-- Display Selected DateTime -->
        <Border
            Stroke="#512BD4"
            StrokeThickness="2"
            Padding="15">
            <Border.StrokeShape>
                <RoundRectangle CornerRadius="10"/>
            </Border.StrokeShape>
            <VerticalStackLayout Spacing="5">
                <Label Text="Selected DateTime:" FontAttributes="Bold" />
                <Label 
                    x:Name="SelectedDateTimeLabel"
                    Text="Not selected"
                    FontSize="16"
                    TextColor="#512BD4" />
            </VerticalStackLayout>
        </Border>

        <!-- SfPicker with 5 Columns for Date and Time (Hidden from user) -->
        <picker:SfPicker 
            x:Name="DateTimePickerControl"
            Mode="Dialog"
            Height="350"
            ItemHeight="50"
            SelectionChanged="OnDateTimePickerSelectionChanged">
            
            <picker:SfPicker.HeaderView>
                <picker:PickerHeaderView 
                    Text="Select Date and Time" 
                    Height="40" />
            </picker:SfPicker.HeaderView>
            <picker:SfPicker.ColumnHeaderView>
                <picker:PickerColumnHeaderView Height="40"/>
            </picker:SfPicker.ColumnHeaderView>

            <picker:SfPicker.Columns>
                <!-- Month Column -->
                <picker:PickerColumn 
                    HeaderText="Month"
                    ItemsSource="{Binding Months}" />
                
                <!-- Day Column -->
                <picker:PickerColumn 
                    HeaderText="Day"
                    ItemsSource="{Binding Days}" />
                
                <!-- Year Column -->
                <picker:PickerColumn 
                    HeaderText="Year"
                    ItemsSource="{Binding Years}" />
                
                <!-- Hour Column -->
                <picker:PickerColumn 
                    HeaderText="Hour"
                    ItemsSource="{Binding Hours}" />
                
                <!-- Minute Column -->
                <picker:PickerColumn 
                    HeaderText="Minute"
                    ItemsSource="{Binding Minutes}" />
            </picker:SfPicker.Columns>
        </picker:SfPicker>

        <!-- Open Picker Button -->
        <Button
            x:Name="OpenPickerBtn"
            Text="Open DateTime Picker"
            Clicked="OnOpenPickerClicked"
            BackgroundColor="#512BD4"
            TextColor="White"
            Padding="15"
            CornerRadius="10"
            FontAttributes="Bold"
            HorizontalOptions="Fill" />
    </VerticalStackLayout>

Step 3: Define ObservableCollections in the ViewModel

Define collections for months, days, years, hours, and minutes in a ViewModel (e.g., DateTimePickerViewModel).

C#


public class DateTimePickerViewModel
{
    public ObservableCollection<int>? Months { get; set; }
    public ObservableCollection<int>? Days { get; set; }
    public ObservableCollection<int>? Years { get; set; }
    public ObservableCollection<int>? Hours { get; set; }
    public ObservableCollection<int>? Minutes { get; set; }

    public int SelectedMonth { get; set; }
    public int SelectedDay { get; set; }
    public int SelectedYear { get; set; }
    public int SelectedHour { get; set; }
    public int SelectedMinute { get; set; }

    public DateTimePickerViewModel()
    {
        InitializeCollections();
        SetDefaultValues();
    }

    private void InitializeCollections()
    {
        // Initialize Months (1-12)
        Months = new ObservableCollection<int>();
        for (int i = 1; i <= 12; i++)
        {
            Months.Add(i);
        }

        // Initialize Days (1-31)
        Days = new ObservableCollection<int>();
        for (int i = 1; i <= 31; i++)
        {
            Days.Add(i);
        }

        // Initialize Years (2020-2030)
        Years = new ObservableCollection<int>();
        int currentYear = DateTime.Now.Year;
        for (int i = currentYear - 5; i <= currentYear + 5; i++)
        {
            Years.Add(i);
        }

        // Initialize Hours (0-23)
        Hours = new ObservableCollection<int>();
        for (int i = 0; i < 24; i++)
        {
            Hours.Add(i);
        }

        // Initialize Minutes (0-59)
        Minutes = new ObservableCollection<int>();
        for (int i = 0; i < 60; i++)
        {
            Minutes.Add(i);
        }
    }

    private void SetDefaultValues()
    {
        var now = DateTime.Now;
        SelectedMonth = now.Month;
        SelectedDay = now.Day;
        SelectedYear = now.Year;
        SelectedHour = now.Hour;
        SelectedMinute = now.Minute;
    }

    public DateTime GetSelectedDateTime()
    {
        try
        {
            return new DateTime(SelectedYear, SelectedMonth, SelectedDay, SelectedHour, SelectedMinute,0);
        }
        catch
        {
            return DateTime.Now;
        }
    }
}

Step 4: Handle Selection Changes

In your custom control, handle the SelectionChanged event of the SfPicker to update the ViewModel and propagate the selected DateTime.

C#:

private void OnDateTimePickerSelectionChanged(object sender, PickerSelectionChangedEventArgs e)
{
    if (DateTimePickerControl?.Columns != null)
{
    try
    {
        // Get selected values from each column
        int selectedMonth = GetSelectedValue(0, viewModel.Months, viewModel.SelectedMonth);
        int selectedDay = GetSelectedValue(1, viewModel.Days, viewModel.SelectedDay);
        int selectedYear = GetSelectedValue(2, viewModel.Years, viewModel.SelectedYear);
        int selectedHour = GetSelectedValue(3, viewModel.Hours, viewModel.SelectedHour);
        int selectedMinute = GetSelectedValue(4, viewModel.Minutes, viewModel.SelectedMinute);

        // Update ViewModel
        viewModel.SelectedMonth = selectedMonth;
        viewModel.SelectedDay = selectedDay;
        viewModel.SelectedYear = selectedYear;
        viewModel.SelectedHour = selectedHour;
        viewModel.SelectedMinute = selectedMinute;

        // Create DateTime from selected values
        var selectedDateTime = new DateTime(selectedYear, selectedMonth, selectedDay, selectedHour, selectedMinute, 0);

        // Update the BindableProperty
        SelectedDateTime = selectedDateTime;

        // Update display label
        UpdateDisplayLabel();

        // Raise the DateTimeChanged event
        DateTimeChanged?.Invoke(this, new DateTimeChangedEventArgs(selectedDateTime));
    }
    catch (Exception ex)
    {
        SelectedDateTimeLabel.Text = $"Error: {ex.Message}";
    }
}
}

Step 5: Use the Custom Picker in MainPage

Add the custom picker to your MainPage and bind its SelectedDateTime property to your ViewModel.

XAML:

<ScrollView>
    <VerticalStackLayout
        Padding="30,20"
        Spacing="20">
        
        <Label
            Text="DateTime Picker"
            FontSize="28"
            FontAttributes="Bold"
            TextColor="#512BD4"
            HorizontalOptions="Center" />

        <Label
            Text="Select Date and Time"
            FontSize="16"
            TextColor="#666666"
            HorizontalOptions="Center" />

        <!-- Custom DateTime Picker Component -->
        <controls:CustomDateTimePicker
            x:Name="CustomDateTimePicker"
            SelectedDateTime="{Binding SelectedDateTime, Mode=TwoWay, StringFormat='{0:G}'}"
            DateTimeChanged="OnDateTimeChanged" />

    </VerticalStackLayout>
</ScrollView>

C#

public partial class MainPage : ContentPage
{
    private MainPageViewModel viewModel;

    public MainPage()
    {
        InitializeComponent();

        // Initialize the ViewModel
        viewModel = new MainPageViewModel();
        this.BindingContext = viewModel;
    }

    /// <summary>
    /// Handle the DateTimeChanged event from the custom DateTime picker component
    /// </summary>
    private void OnDateTimeChanged(object sender, DateTimeChangedEventArgs e)
    {
        // Update the ViewModel's SelectedDateTime property for binding
        viewModel.SelectedDateTime = e.SelectedDateTime;
    }
}

ViewModel

public class MainPageViewModel : INotifyPropertyChanged
{
    private DateTime selectedDateTime = DateTime.Now;

    public DateTime SelectedDateTime
    {
        get => selectedDateTime;
        set
        {
            if (selectedDateTime != value)
            {
                selectedDateTime = value;
                OnPropertyChanged();
            }
        }
    }

    public event PropertyChangedEventHandler? PropertyChanged;

    protected void OnPropertyChanged([CallerMemberName] string? name = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }
}

Output

Output

Download the complete sample on GitHub.

Conclusion:

This article demonstrated how to build a custom DateTimePicker using the Syncfusion .NET MAUI SfPicker in dialog mode. It covers creating a CustomDateTimePicker control, preparing ViewModel collections for months, days, years, hours, and minutes, defining the XAML layout, handling SelectionChanged to update a bindable SelectedDateTime property, and using the control in MainPage.

For more information, refer to the following resources:

For current customers, check out our .NET MAUI components from the License and Downloads page. If you are new to Syncfusion®, try our 30-day free trial to check out our .NET MAUI Picker and other .NET MAUI components.

Please let us know in the following comments section if you have any queries or require clarification. You can also contact us through our support forums, Direct-Trac, or feedback portal. We are always happy to assist you!

Did you find this information helpful?
Yes
No
Help us improve this page
Please provide feedback or comments
Comments (0)
Access denied
Access denied