Xamarin.Forms & MVVM In C#: PropertyChanged Event Handler Is Always Null When OnPropertyChanged Call

wbergerwberger USMember

I've written a simple application using Xamarin.Forms in C# with Visual Studio 2017, and have a problem with data binding working to update the contents of a Label child control to display the selected date value from a DatePicker child control in the same ContentPage. Although all of the project elements compile, build and deploy without any errors, and the OnPropertyChanged method in the ViewModel is called each and every time when I change the date selected in the DatePicker, the PropertyChanged event handler is always a null object reference when the OnPropertyChanged method is called, and in turn, does not update the contents of the Label's Text property as expected with the new selected date value.

The following are my MainPage implementation in addition to the backing ViewModel class, SelectedDateViewModel.cs.

C# [MainPage.xaml.cs]

using System;
using Xamarin.Forms;

namespace DataBoundDatePicker
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();

            BindingContext = new SelectedDateViewModel();
        }
    }
}

XAML [MainPage.xaml]

<?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:DataBoundDatePicker"
             x:Class="DataBoundDatePicker.MainPage">
    <StackLayout Padding="20" VerticalOptions="Center" HorizontalOptions="Center">
        <Label Text="Data Bound DatePicker" FontAttributes="Bold" HorizontalOptions="Center" />
        <Label Text="Select a Date:" />
        <DatePicker x:Name="SelectedDatePicker" Date="{Binding SelectedDate}"/>
        <StackLayout Orientation="Horizontal">
            <Label Text="Formatted Date:" />
            <Label x:Name="FormattedDateLabel" Text="{Binding SelectedDate, StringFormat='{0:dddd, MMMM d, yyyy}'}" />
        </StackLayout>
    </StackLayout>
</ContentPage>

C# [SelectedDateViewModel.cs]

using System;
using System.ComponentModel;
using System.Diagnostics;

namespace DataBoundDatePicker
{
    public class SelectedDateViewModel
    {
        private readonly string FullDateFormat = "dddd, MMMM d, yyyy";

        private DateTime selectedDate;

        public event PropertyChangedEventHandler PropertyChanged;

        public SelectedDateViewModel()
        {
            Debug.WriteLine("Entering SelectedDateViewModel.SelectedDateViewModel() - Constructor");

            SelectedDate = DateTime.Now;

            Debug.WriteLine("Leaving SelectedDateViewModel.SelectedDateViewModel() - Constructor");
        }

        public DateTime SelectedDate
        {
            get
            {
                return selectedDate;
            }

            set
            {
                if (selectedDate != value)
                {
                    selectedDate = value;
                    OnPropertyChanged("SelectedDate");
                }
            }
        }

        protected virtual void OnPropertyChanged(string propertyName)
        {
            Debug.WriteLine("Inside SelectedDateViewModel.OnPropertyChanged()");

            Debug.WriteLine($"SelectedDate = {selectedDate.ToString(FullDateFormat)}");

            var trace =
            $"PropertyChanged Is Null: {(PropertyChanged == null ? "Yes" : "No")}";
            Debug.WriteLine(trace);

            var propertyChangedCallback = PropertyChanged;
            propertyChangedCallback?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

The current implementation does the following:

  • On startup, the FormattedDateLabel displays the correct formatted date string based on the SelectedDate DateTime property in the SelectedDateViewModel. However, any subsequent changes to the Date property in the SelectedDatePicker control.

  • Without fail, each and every time the Date value is changed in the SelectedDatePicker control, the SelectedDateViewModel.OnPropertyChanged() method is always called, even on startup through the instantiation of MainPage and SelectedDateViewModel object instances (e.g., constructor calls).

  • Although the SelectedDateViewModel.OnPropertyChanged() is called any time when the Date value is changed in the DatePicker,
    SelectedDateViewModel's PropertyChanged event reference is always null, even on startup (again, when instantiated through constructor calls). This is shown with the following Debug trace statements emitted as the program is running in Debug mode through Visual Studio:

    'DataBoundDatePicker.UWP.exe' (CoreCLR: CoreCLR_UWP_Domain): Loaded 'c:\work\Xamarin\DataBoundDatePicker\DataBoundDatePicker\DataBoundDatePicker.UWP\bin\x86\Debug\AppX\Xamarin.Forms.Xaml.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
    Entering MainPage.MainPage() - Constructor
    Entering SelectedDateViewModel.SelectedDateViewModel() - Constructor
    Inside SelectedDateViewModel.OnPropertyChanged()
    SelectedDate = Tuesday, July 4, 2017
    PropertyChanged Is Null: Yes
    Leaving SelectedDateViewModel.SelectedDateViewModel() - Constructor
    Leaving MainPage.MainPage() - Constructor
    The thread 0x1630 has exited with code 0 (0x0).
    Inside SelectedDateViewModel.OnPropertyChanged()
    SelectedDate = Sunday, March 4, 2012
    PropertyChanged Is Null: Yes

  • The code exhibits the same behavior, regardless of deploying and debugging the UWP or Android version of the application.

Any help or insights and explanations you can share on how I can get the Label Text property to have its Text property bound to the
DatePicker Date property using the ViewModel where the PropertyChanged event is not a null reference would be greatly appreciated.

Thank you in advance for your time and help.

Best Answers

  • HunumanHunuman GB ✭✭✭✭
    Accepted Answer

    Hi @wberger

    From the code posted it seems your view models class declaration is incorrect.

    You have:

        public class SelectedDateViewModel
    

    It should be:

        public class SelectedDateViewModel: INotifyPropertyChanged
    

    Hope this helps,

    Tim

Answers

  • HunumanHunuman GBMember ✭✭✭✭
    Accepted Answer

    Hi @wberger

    From the code posted it seems your view models class declaration is incorrect.

    You have:

        public class SelectedDateViewModel
    

    It should be:

        public class SelectedDateViewModel: INotifyPropertyChanged
    

    Hope this helps,

    Tim

  • wbergerwberger USMember

    Spot on, Tim! My omission of the : INotifyPropertyChanged interface in the View Model deserves the following:

    Again, many thanks. :)

Sign In or Register to comment.