问题
I'm designing a simple model that can be edited on a screen. In this case, it's for Xamarin Forms UWP, but this model class is platform agnostic, and I doubt that it's a UWP/Xamarin specific problem. I need the code to work so that if I edited any property in the model, the event should bubble up to the top, and I should be able to handle the PropertyChanged event on UserPreferences to save the record. But, this event never gets fired no matter which properties are changed. I can see that the properties are getting changed.
If I instantiate the UserPreferences class with its normal constructor this works fine. But, this class is actually created with deserialization. So, it seems that the serialization process is doing something funny with the constructors.
Here are my classes:
[Serializable]
public class TaskNotificationPreferences : INotifyPropertyChanged
{
#region Events
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region Public Properties
public ObservableCollection<int> PriorityKeys { get; set; }
#endregion
#region Constructor
public TaskNotificationPreferences()
{
PriorityKeys = new ObservableCollection<int>();
PriorityKeys.CollectionChanged += PriorityKeys_CollectionChanged;
}
#endregion
#region Event Handlers
private void PriorityKeys_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(PriorityKeys)));
}
#endregion
}
[Serializable]
public class NotificationPreferences : INotifyPropertyChanged
{
#region Fields
private bool _ReceivePushNotifications;
private bool _ReceiveEmails;
#endregion
#region Events
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region Constructor
public NotificationPreferences()
{
TaskNotificationPreferences = new TaskNotificationPreferences();
TaskNotificationPreferences.PropertyChanged += TaskNotificationPreferences_PropertyChanged;
}
#endregion
#region Event Handlers
private void TaskNotificationPreferences_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(TaskNotificationPreferences)));
}
#endregion
#region Public Properties
public TaskNotificationPreferences TaskNotificationPreferences { get; set; }
public bool ReceivePushNotifications
{
get
{
return _ReceivePushNotifications;
}
set
{
_ReceivePushNotifications = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ReceivePushNotifications)));
}
}
public bool ReceiveEmails
{
get
{
return _ReceiveEmails;
}
set
{
_ReceiveEmails = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ReceiveEmails)));
}
}
#endregion
}
[Serializable]
public class UserPreferences : INotifyPropertyChanged
{
#region Events
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region Public Properties
public NotificationPreferences NotificationPreferences { get; set; }
#endregion
#region Constructor
public UserPreferences()
{
NotificationPreferences = new NotificationPreferences();
NotificationPreferences.PropertyChanged += NotificationPreferences_PropertyChanged;
}
#endregion
#region Event Handlers
private void NotificationPreferences_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(NotificationPreferences)));
}
#endregion
}
How the class is being deserialized:
public static SerialiseType DeserialiseObjectWithoutCatch<SerialiseType>(string objectXml)
{
var serializer = new XmlSerializer(typeof(SerialiseType));
var sr = new StringReader(objectXml);
var retVal = serializer.Deserialize(sr);
return (SerialiseType)retVal;
}
Edit: In the end, this is the workaround I got to. It works, but I think that to some extent it highlights the problem with serialization in .NET. Here is my workaround code:
[Serializable]
public class UserPreferences : INotifyPropertyChanged
{
#region Events
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region Public Properties
public NotificationPreferences NotificationPreferences { get; set; } = new NotificationPreferences();
#endregion
#region Event Handlers
private void NotificationPreferences_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(NotificationPreferences)));
}
#endregion
#region Public Methods
public void HandlePropertyChanged()
{
NotificationPreferences.PropertyChanged += NotificationPreferences_PropertyChanged;
NotificationPreferences.HandlePropertyChanged();
}
#endregion
}
[Serializable]
public class NotificationPreferences : INotifyPropertyChanged
{
#region Fields
private bool _ReceivePushNotifications;
private bool _ReceiveEmails;
#endregion
#region Events
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region Event Handlers
private void TaskNotificationPreferences_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(TaskNotificationPreferences)));
}
#endregion
#region Public Properties
public TaskNotificationPreferences TaskNotificationPreferences { get; set; } = new TaskNotificationPreferences();
public bool ReceivePushNotifications
{
get
{
return _ReceivePushNotifications;
}
set
{
_ReceivePushNotifications = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ReceivePushNotifications)));
}
}
public bool ReceiveEmails
{
get
{
return _ReceiveEmails;
}
set
{
_ReceiveEmails = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ReceiveEmails)));
}
}
#endregion
#region Public Methods
internal void HandlePropertyChanged()
{
TaskNotificationPreferences.PropertyChanged += TaskNotificationPreferences_PropertyChanged;
TaskNotificationPreferences.HandlePropertyChanged();
}
#endregion
}
[Serializable]
public class TaskNotificationPreferences : INotifyPropertyChanged
{
#region Events
public event PropertyChangedEventHandler PropertyChanged;
#endregion
private ObservableCollection<int> _PriorityKeys = new ObservableCollection<int>();
#region Public Properties
public ObservableCollection<int> PriorityKeys
{
get { return _PriorityKeys; }
set
{
_PriorityKeys = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(PriorityKeys)));
}
}
#endregion
#region Event Handlers
private void PriorityKeys_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(PriorityKeys)));
}
#endregion
#region Public Methods
internal void HandlePropertyChanged()
{
PriorityKeys.CollectionChanged += PriorityKeys_CollectionChanged;
}
#endregion
}
So, my question really is now, what is the best way to deal with this? Is this a bug in .NET? A bug in serialization? Or, should the model be redesigned somehow?
来源:https://stackoverflow.com/questions/46292590/propertychanged-event-not-bubbling-up-c-sharp