/// <summary> /// Unsubscribe from model object changes /// </summary> /// <param name="model">Model object to unsubscribe from</param> private static void UnsubscribeFromModelObject(ModelObject model) { if (model == null) { return; } PropertyChangedEventHandler changeHandler = _propertyChangedHandlers[model]; if (changeHandler == null) { // Already unregistered - don't bother to continue return; } model.PropertyChanged -= changeHandler; _propertyChangedHandlers.Remove(model); bool unregisterPropertyChanging = false; foreach (PropertyInfo property in model.GetType().GetProperties()) { object value = property.GetValue(model); if (property.PropertyType.IsSubclassOf(typeof(ModelObject))) { if (value != null) { UnsubscribeFromModelObject((ModelObject)value); } unregisterPropertyChanging |= (property.SetMethod != null); } else if (ModelCollection.GetItemType(property.PropertyType, out Type itemType)) { if (ModelGrowingCollection.TypeMatches(property.PropertyType)) { UnsubscribeFromGrowingModelCollection(value); } else { UnsubscribeFromModelCollection(value, property.PropertyType.GetGenericArguments()[0]); } } else if (property.PropertyType.IsGenericType && typeof(ModelDictionary <>) == property.PropertyType.GetGenericTypeDefinition()) { UnsubscribeFromModelDictionary(value); } } if (unregisterPropertyChanging) { // Same here - unregister the event handler only where required model.PropertyChanging -= VariableModelObjectChanging; } }
/// <summary> /// Subscribe to changes of the given model object /// </summary> /// <param name="model">Object to subscribe to</param> /// <param name="path">Collection path</param> private static void SubscribeToModelObject(ModelObject model, object[] path) { if (model == null) { return; } bool hasVariableModelObjects = false; foreach (PropertyInfo property in model.GetType().GetProperties()) { string propertyName = JsonNamingPolicy.CamelCase.ConvertName(property.Name); object value = property.GetValue(model); if (property.PropertyType.IsSubclassOf(typeof(ModelObject))) { if (value != null) { SubscribeToModelObject((ModelObject)value, AddToPath(path, propertyName)); } hasVariableModelObjects |= (property.SetMethod != null); } else if (ModelCollection.GetItemType(property.PropertyType, out Type itemType)) { if (ModelGrowingCollection.TypeMatches(property.PropertyType)) { SubscribeToGrowingModelCollection(value, propertyName, path); } else { SubscribeToModelCollection(value, itemType, propertyName, path); } } else if (property.PropertyType.IsGenericType && typeof(ModelDictionary <>) == property.PropertyType.GetGenericTypeDefinition()) { SubscribeToModelDictionary(value, AddToPath(path, propertyName)); } } PropertyChangedEventHandler changeHandler = PropertyChanged(hasVariableModelObjects, path); model.PropertyChanged += changeHandler; _propertyChangedHandlers[model] = changeHandler; if (hasVariableModelObjects) { // This is barely needed so only register it where it is actually required. // It makes sure that events are removed again when a ModelObject instance is replaced model.PropertyChanging += VariableModelObjectChanging; } }