IndexerNodeBase.cs 3.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Collections.Specialized;
  5. using System.ComponentModel;
  6. using System.Globalization;
  7. using System.Linq;
  8. using System.Reactive.Linq;
  9. using System.Reflection;
  10. using System.Text;
  11. using Avalonia.Data;
  12. using Avalonia.Utilities;
  13. namespace Avalonia.Data.Core
  14. {
  15. public abstract class IndexerNodeBase : SettableNode
  16. {
  17. private IDisposable _subscription;
  18. protected override void StartListeningCore(WeakReference reference)
  19. {
  20. var target = reference.Target;
  21. var incc = target as INotifyCollectionChanged;
  22. var inpc = target as INotifyPropertyChanged;
  23. var inputs = new List<IObservable<object>>();
  24. if (incc != null)
  25. {
  26. inputs.Add(WeakObservable.FromEventPattern<INotifyCollectionChanged, NotifyCollectionChangedEventArgs>(
  27. incc,
  28. nameof(incc.CollectionChanged))
  29. .Where(x => ShouldUpdate(x.Sender, x.EventArgs))
  30. .Select(_ => GetValue(target)));
  31. }
  32. if (inpc != null)
  33. {
  34. inputs.Add(WeakObservable.FromEventPattern<INotifyPropertyChanged, PropertyChangedEventArgs>(
  35. inpc,
  36. nameof(inpc.PropertyChanged))
  37. .Where(x => ShouldUpdate(x.Sender, x.EventArgs))
  38. .Select(_ => GetValue(target)));
  39. }
  40. _subscription = Observable.Merge(inputs).StartWith(GetValue(target)).Subscribe(ValueChanged);
  41. }
  42. protected override void StopListeningCore()
  43. {
  44. _subscription.Dispose();
  45. }
  46. protected abstract object GetValue(object target);
  47. protected abstract int? TryGetFirstArgumentAsInt();
  48. private bool ShouldUpdate(object sender, NotifyCollectionChangedEventArgs e)
  49. {
  50. if (sender is IList)
  51. {
  52. var index = TryGetFirstArgumentAsInt();
  53. if (index == null)
  54. {
  55. return false;
  56. }
  57. switch (e.Action)
  58. {
  59. case NotifyCollectionChangedAction.Add:
  60. return index >= e.NewStartingIndex;
  61. case NotifyCollectionChangedAction.Remove:
  62. return index >= e.OldStartingIndex;
  63. case NotifyCollectionChangedAction.Replace:
  64. return index >= e.NewStartingIndex &&
  65. index < e.NewStartingIndex + e.NewItems.Count;
  66. case NotifyCollectionChangedAction.Move:
  67. return (index >= e.NewStartingIndex &&
  68. index < e.NewStartingIndex + e.NewItems.Count) ||
  69. (index >= e.OldStartingIndex &&
  70. index < e.OldStartingIndex + e.OldItems.Count);
  71. case NotifyCollectionChangedAction.Reset:
  72. return true;
  73. }
  74. }
  75. return true; // Implementation defined meaning for the index, so just try to update anyway
  76. }
  77. protected abstract bool ShouldUpdate(object sender, PropertyChangedEventArgs e);
  78. }
  79. }