protected override void Attach()
        {
            TrackedObject.PropertyChanged += OnPropertyChanged;

            // Scan through the properties to see if we need to
            // observe nested objects; for each property add an
            // ObjectTracker.
            var properties = TrackedObject.GetType()
                             .GetProperties(BindingFlags.Public | BindingFlags.Instance)
                             .Where(prop => prop.GetIndexParameters().Length == 0)
                             .Where(prop => !prop.CustomAttributes.Any(o => o.AttributeType == typeof(DoNotTrackAttribute)))
                             .ToArray();

            foreach (var prop in properties)
            {
                RefreshPropertyTracker(prop.Name, raiseEvent: false); // Do not raise events on initialization
            }
        }
        private void RefreshPropertyTracker(string propertyName, bool raiseEvent)
        {
            // Determine the new value
            var prop = TrackedObject.GetType().GetProperty(propertyName);

            if (prop == null)
            {
                return; // Was probably an indexer that changed
            }
            var newValue = prop.GetValue(TrackedObject);

            // If no ObjectTracker exists for the property, the old value was null
            TrackerToken trackerToken;
            object       oldValue = null;

            if (_propertyTrackers.TryGetValue(propertyName, out trackerToken))
            {
                // Detach old tracker from old value and remove it
                oldValue = trackerToken.Tracker.TrackedObject;
                trackerToken.Dispose();
                _propertyTrackers.Remove(propertyName);
            }

            if (newValue != null)
            {
                // Add a new tracker for the new value
                var token = new TrackerToken(newValue, propertyName, Tracker);
                _propertyTrackers.Add(propertyName, token);
            }

            // Notify that property changed
            if (raiseEvent)
            {
                Tracker.RaisePropertyPathChanged(new PropertyPathChangedEventArgs(propertyName, TrackedObject, oldValue, newValue));
            }
        }
        protected override void Attach()
        {
            if (!(TrackedObject is IEnumerable))
            {
                throw new ArgumentException($"Failed to track collection changes on object of type {TrackedObject.GetType()}. The object does not implement {nameof(IEnumerable)}.");
            }

            TrackedObject.CollectionChanged += OnCollectionChanged;

            // Scan through the items and create an ObjectTracker
            // for each item.
            var i = 0;

            foreach (var item in TrackedObject as IEnumerable)
            {
                InsertTrackerAt(i++, item);
            }
        }