PropertyObserverNode.cs 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. using System;
  2. using System.ComponentModel;
  3. using System.Reflection;
  4. namespace HandyControl.Interactivity.Commands
  5. {
  6. /// <summary>
  7. /// Represents each node of nested properties expression and takes care of
  8. /// subscribing/unsubscribing INotifyPropertyChanged.PropertyChanged listeners on it.
  9. /// </summary>
  10. internal class PropertyObserverNode
  11. {
  12. private readonly Action _action;
  13. private INotifyPropertyChanged? _inpcObject;
  14. public PropertyInfo PropertyInfo { get; }
  15. public PropertyObserverNode? Next { get; set; }
  16. public PropertyObserverNode(PropertyInfo propertyInfo, Action action)
  17. {
  18. PropertyInfo = propertyInfo ?? throw new ArgumentNullException(nameof(propertyInfo));
  19. _action = () =>
  20. {
  21. action?.Invoke();
  22. if (Next == null) return;
  23. Next.UnsubscribeListener();
  24. GenerateNextNode();
  25. };
  26. }
  27. public void SubscribeListenerFor(INotifyPropertyChanged inpcObject)
  28. {
  29. _inpcObject = inpcObject;
  30. _inpcObject.PropertyChanged += OnPropertyChanged;
  31. if (Next != null) GenerateNextNode();
  32. }
  33. private void GenerateNextNode()
  34. {
  35. var nextProperty = PropertyInfo.GetValue(_inpcObject);
  36. if (nextProperty == null) return;
  37. if (nextProperty is not INotifyPropertyChanged nextInpcObject)
  38. throw new InvalidOperationException("Trying to subscribe PropertyChanged listener in object that " +
  39. $"owns '{Next?.PropertyInfo.Name}' property, but the object does not implements INotifyPropertyChanged.");
  40. Next?.SubscribeListenerFor(nextInpcObject);
  41. }
  42. private void UnsubscribeListener()
  43. {
  44. if (_inpcObject != null)
  45. _inpcObject.PropertyChanged -= OnPropertyChanged;
  46. Next?.UnsubscribeListener();
  47. }
  48. private void OnPropertyChanged(object? sender, PropertyChangedEventArgs e)
  49. {
  50. if (e?.PropertyName == PropertyInfo.Name || string.IsNullOrEmpty(e?.PropertyName))
  51. {
  52. _action?.Invoke();
  53. }
  54. }
  55. }
  56. }