Xamarin.Forms & C# 中的 MVVM:调用 OnPropertyChanged 时,PropertyChanged 事件处理程序始终为空
Xamarin.Forms & MVVM In C#: PropertyChanged Event Handler Is Always Null When OnPropertyChanged Called
我用 Visual Studio 2017 在 C# 中使用 Xamarin.Forms 编写了一个简单的应用程序,但在更新 Label 子控件的内容以显示所选日期的数据绑定方面遇到问题来自同一 ContentPage 中 DatePicker 子控件的值。尽管所有项目元素都编译、构建和部署没有任何错误,并且每次我更改 DatePicker 中选择的日期时都会调用 ViewModel 中的 OnPropertyChanged 方法,但 PropertyChanged 事件处理程序始终是空对象引用调用 OnPropertyChanged 方法后,不会按预期使用新的选定日期值更新标签文本的内容 属性。
以下是我的 MainPage 实现以及支持 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));
}
}
}
当前实现执行以下操作:
启动时,FormattedDateLabel 显示正确的格式
基于 SelectedDate DateTime 属性 中的日期字符串
选择日期视图模型。但是,该日期的任何后续更改
属性在SelectedDatePicker控件中。
毫无疑问,每次更改日期值时
SelectedDatePicker 控件,
SelectedDateViewModel.OnPropertyChanged() 方法总是被调用,
即使在启动时通过 MainPage 的实例化和
SelectedDateViewModel 对象实例(例如,构造函数调用)。
尽管 SelectedDateViewModel.OnPropertyChanged() 被称为任何
DatePicker中Date值改变的时间,
SelectedDateViewModel 的 PropertyChanged 事件参考总是
null,即使在启动时(同样,当通过构造函数实例化时
调用)。这显示在以下调试跟踪语句中
当程序通过 Visual 在调试模式下 运行 时发出
工作室:
'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
- 无论部署和调试如何,代码都表现出相同的行为
应用程序的 UWP 或 Android 版本。
关于如何让文本 属性 将其文本 属性 绑定到
在 PropertyChanged 事件不是空引用的情况下使用 ViewModel 的日期 属性 将不胜感激。
提前感谢您的宝贵时间和帮助。
使用 INotifyPropertyChanged 接口。像这样。
public class SelectedDateViewModel : INotifyPropertyChanged
{
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)
{
if (PropertyChanged != null)
{
PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
}
}
我用 Visual Studio 2017 在 C# 中使用 Xamarin.Forms 编写了一个简单的应用程序,但在更新 Label 子控件的内容以显示所选日期的数据绑定方面遇到问题来自同一 ContentPage 中 DatePicker 子控件的值。尽管所有项目元素都编译、构建和部署没有任何错误,并且每次我更改 DatePicker 中选择的日期时都会调用 ViewModel 中的 OnPropertyChanged 方法,但 PropertyChanged 事件处理程序始终是空对象引用调用 OnPropertyChanged 方法后,不会按预期使用新的选定日期值更新标签文本的内容 属性。
以下是我的 MainPage 实现以及支持 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));
}
}
}
当前实现执行以下操作:
启动时,FormattedDateLabel 显示正确的格式 基于 SelectedDate DateTime 属性 中的日期字符串 选择日期视图模型。但是,该日期的任何后续更改 属性在SelectedDatePicker控件中。
毫无疑问,每次更改日期值时 SelectedDatePicker 控件, SelectedDateViewModel.OnPropertyChanged() 方法总是被调用, 即使在启动时通过 MainPage 的实例化和 SelectedDateViewModel 对象实例(例如,构造函数调用)。
尽管 SelectedDateViewModel.OnPropertyChanged() 被称为任何 DatePicker中Date值改变的时间, SelectedDateViewModel 的 PropertyChanged 事件参考总是 null,即使在启动时(同样,当通过构造函数实例化时 调用)。这显示在以下调试跟踪语句中 当程序通过 Visual 在调试模式下 运行 时发出 工作室:
'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
- 无论部署和调试如何,代码都表现出相同的行为 应用程序的 UWP 或 Android 版本。
关于如何让文本 属性 将其文本 属性 绑定到 在 PropertyChanged 事件不是空引用的情况下使用 ViewModel 的日期 属性 将不胜感激。
提前感谢您的宝贵时间和帮助。
使用 INotifyPropertyChanged 接口。像这样。
public class SelectedDateViewModel : INotifyPropertyChanged
{
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)
{
if (PropertyChanged != null)
{
PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
}
}