using System.Collections.Specialized; using System.ComponentModel; namespace TerminalUI.ExpressionTrackers; public abstract class ExpressionTrackerBase : IExpressionTracker { private object? _currentValue; private readonly List _propertyProxyTrackers = new(); private readonly List _trackedPropertyNames = new(); protected bool SubscribeToValueChanges { get; set; } = true; public event Action? PropertyChanged; public event Action? Update; public object? GetValue() => _currentValue; protected abstract object? ComputeValue(); protected void SubscribeToTracker(IExpressionTracker? expressionTracker, bool proxyPropertyChanged = false) { if (expressionTracker is null) return; expressionTracker.Update += UpdateValueAndChangeTrackers; if (proxyPropertyChanged) { expressionTracker.PropertyChanged += OnPropertyChanged; _propertyProxyTrackers.Add(expressionTracker); foreach (var propertyName in _trackedPropertyNames) { expressionTracker.TrackProperty(propertyName); } } } protected void UpdateValueAndChangeTrackers() => UpdateValueAndChangeTrackers(true); private void UpdateValueAndChangeTrackers(bool couldCompute) { if (SubscribeToValueChanges) { if (_currentValue is INotifyPropertyChanged oldNotifyPropertyChanged) oldNotifyPropertyChanged.PropertyChanged -= OnPropertyChanged; if (_currentValue is INotifyCollectionChanged collectionChanged) collectionChanged.CollectionChanged -= OnCollectionChanged; } var useNull = false; try { if (couldCompute) { _currentValue = ComputeValue(); if (SubscribeToValueChanges) { if (_currentValue is INotifyPropertyChanged notifyPropertyChanged) notifyPropertyChanged.PropertyChanged += OnPropertyChanged; if (_currentValue is INotifyCollectionChanged collectionChanged) collectionChanged.CollectionChanged += OnCollectionChanged; } Update?.Invoke(true); } else { useNull = true; } } catch (Exception e) { useNull = true; } if (useNull) { _currentValue = null; Update?.Invoke(false); } } private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) => UpdateValueAndChangeTrackers(); private void OnPropertyChanged(object? sender, PropertyChangedEventArgs e) { if (e.PropertyName is null) return; if (_trackedPropertyNames.Contains(e.PropertyName)) { PropertyChanged?.Invoke(e.PropertyName); } } protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(propertyName); public void TrackProperty(string propertyName) { if (!_trackedPropertyNames.Contains(propertyName)) { _trackedPropertyNames.Add(propertyName); } foreach (var propertyProxyTracker in _propertyProxyTrackers) { propertyProxyTracker.TrackProperty(propertyName); } } }