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?
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;
}
}
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}");
}