Search code examples
mauimaui-community-toolkit

How can I handle DatePicker date change?


I my .Net MAUI app, I use CommunityToolkit.mvvm. I have a DatePicker, and I cannot figure out how to handle the selected date change in a ViewModel command.

Here is my xaml:

<DatePicker
        Date = "{Binding AppointmentDate, Mode=TwoWay}"
        Margin="10" 
        TextColor="{Binding AppointmentDate, Converter={StaticResource NullDateTimeToColorConverter}}" >
    <DatePicker.Behaviors>
        <toolkit:EventToCommandBehavior
            EventName = "DateSelected"
            Command="{Binding AppointmentDateChangedCommand}" 
            CommandParameter="AppointmentDate" />  
    </DatePicker.Behaviors>
</DatePicker>

In ViewModel:

[ObservableProperty]
[Required(ErrorMessage = "Please select the date of your appointment.")]
private DateTime? _appointmentDate = null;

[RelayCommand]
private void AppointmentDateChanged(object obj)
{
    throw new NotImplementedException();
}

This gives me the following error:

The selected debug engine does not support any code executing on the current thread (e.g. only native runtime code is executing). You can switch to another thread to see if there is compatible code running.

What is wrong?

Added

This is the full xaml code:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:sys="clr-namespace:System;assembly=netstandard"
             xmlns:viewmodel="clr-namespace:LATICRETE_MobileApp.Features.VideoChat"
             xmlns:converters="clr-namespace:LATICRETE_MobileApp.Converters"
             xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
             x:DataType="viewmodel:AppointmentSchedulePageModel"
             xmlns:local="clr-namespace:LATICRETE_MobileApp.Features.VideoChat"
             x:Class="LATICRETE_MobileApp.Features.VideoChat.AppointmentSchedulePage"

    ControlTemplate="{StaticResource MainPageTemplate}">
    <ContentPage.Resources>
        <converters:NullDateTimeToColorConverter x:Key="NullDateTimeToColorConverter" />

        <Style TargetType="ContentView">
            <Setter Property="VisualStateManager.VisualStateGroups">
                <VisualStateGroupList>
                    <VisualStateGroup x:Name="CommonStates">
                        <VisualState x:Name="Normal">
                            <VisualState.Setters>
                                <Setter Property="BackgroundColor"
                                        Value="Transparent" />
                            </VisualState.Setters>
                        </VisualState>
                        <VisualState x:Name="Selected">
                            <VisualState.Setters>
                                <Setter Property="BackgroundColor"
                                        Value="{StaticResource LaticreteColor}" />
                            </VisualState.Setters>
                        </VisualState>
                    </VisualStateGroup>
                </VisualStateGroupList>
            </Setter>
        </Style>
    </ContentPage.Resources>
    <ContentPage.Content>
        <Grid RowDefinitions="*,auto,auto">
            <ScrollView Margin="30" Grid.Row="0">
                <StackLayout>
                    <Label Text="30-min meeting" HorizontalTextAlignment="Center" FontAttributes="Bold" Margin="5"/>
                    <Label Text="Grab some time with us for video call" HorizontalTextAlignment="Center" Margin="5, 5, 5, 15 "/>

                    <Label Text="DATE:" />
                    <DatePicker 
                        x:Name="AppointmentDatePicker"
                        Date="{Binding AppointmentDate, Mode=TwoWay}"                         
                        Margin="10" 
                        TextColor="{Binding AppointmentDate, Converter={StaticResource NullDateTimeToColorConverter}}" >

                    </DatePicker>

                    <toolkit:Expander Margin="0,25">
                        <toolkit:Expander.Header>
                            <StackLayout Orientation="Horizontal">
                                <Label Text="{Binding LabelSelectTimeText}" FontFamily = "FontAwesome" Margin="0,0,25,0" VerticalTextAlignment="Center" />
                                <Label Text="{Binding SelectedTimeDisplay, Mode=TwoWay, StringFormat='{0:hh:mm tt}'}" FontSize="Medium" VerticalTextAlignment="Center" />                                
                            </StackLayout>
                        </toolkit:Expander.Header>
                        <HorizontalStackLayout Padding="10">
                            <CollectionView 
                                Margin="5,5,5,15"
                                ItemSizingStrategy="MeasureAllItems"
                                ItemsSource="{Binding Times, Mode=OneTime}"
                                SelectedItem="{Binding SelectedTime, Mode=TwoWay}"
                                SelectionMode="Single">
                                <CollectionView.ItemsLayout>
                                    <GridItemsLayout Orientation="Vertical" Span="3" />
                                </CollectionView.ItemsLayout>
                                <CollectionView.ItemTemplate>
                                    <DataTemplate>                                      
                                        <ContentView Padding="5" >
                                            <Border
                                                StrokeThickness="1"
                                                StrokeShape="RoundRectangle 10">
                                                <Label 
                                                    Text="{Binding ., StringFormat='{0:hh:mm tt}'}"
                                                    Padding="10"
                                                    FontAttributes="Bold"
                                                    HorizontalTextAlignment="Center"
                                                    BackgroundColor="Transparent"
                                                    TextColor="Black" />
                                            </Border>
                                        </ContentView>
                                    </DataTemplate>
                                </CollectionView.ItemTemplate>
                            </CollectionView>
                        </HorizontalStackLayout>
                    </toolkit:Expander>

                    <Label Text="Phone Number:" />
                    <Entry Placeholder="Add your phone number" VerticalOptions="Center" Text="{Binding CustomerPhone, Mode=TwoWay}" />
                    <Label Text="Notes:" />
                    <Editor x:Name="notes"
                        Placeholder="Add any special requests"
                        Text="{Binding Notes, Mode=TwoWay}"
                        HeightRequest="250" />        
                </StackLayout>
            </ScrollView>
            <StackLayout 
                Grid.Row="1"
                x:Name="Validation" 
                IsVisible="{Binding HasErrors}">
                    <Label Text="Please fix the following errors:" TextColor="Red" Margin="0,0,0,10" />
                    <Label Text="{Binding Errors}" TextColor="Red" />
            </StackLayout>
            <Button
                Grid.Row="2"
                Command="{Binding ScheduleAppointmentCommand}" 
                Text="{Binding ButtonScheduleAppointmentText}" 
                Style="{StaticResource ButtonStyle}" />
        </Grid>
    </ContentPage.Content>
</ContentPage>

And code behind:

public partial class AppointmentSchedulePage : ContentPage
{
    public AppointmentSchedulePage(AppointmentSchedulePageModel pageModel)
    {
        InitializeComponent();
        BindingContext = pageModel;
    }
}

Solution

  • There are at least two solutions to this. You can Use OnPropertyChanged and use the AppointmentDate you already using, or you can use EventToCommandBehavior. No need to use both.

    Using OnPropertyChanged looks like this. In the ViewModel you have

    [ObservableProperty]
    private DateTime _appointmentDate = DateTime.Now;
    
    protected override void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        base.OnPropertyChanged(e);
    
        if (e.PropertyName == nameof(AppointmentDate))
        {
            Debug.WriteLine($"AppointmentDate: {AppointmentDate}");
        }
    
    }
    

    And your xaml looks like this:

    <DatePicker Margin="10" Date="{Binding AppointmentDate}" />
    

    or you go with

    <DatePicker Margin="10" x:Name="MyDatePicker" Date = "{Binding AppointmentDate}" >
        <DatePicker.Behaviors>
            <toolkit:EventToCommandBehavior
                        Command="{Binding DatePickedCommand}"
                        CommandParameter="{Binding Date, Source={x:Reference MyDatePicker}}"
                        EventName="DateSelected" />
        </DatePicker.Behaviors>
    </DatePicker>  
    

    With a ViewModel like

    public DateTime AppointmentDate { get; } = DateTime.Now;
    
    [RelayCommand]
    private async Task DatePicked(DateTime date)
    {
        Debug.WriteLine($"AppointmentDate: {date}");
    }