protected bool RefreshPathListeners(BindingTarget bindingTarget, MyBindingInfo bInfo, Type sourceType, OSCollection pathListeners, DataSourceChangedEventArgs changeInfo, ObservableSource signalingOs) { // Assume that this operation will not require the binding to be updated, // until proven otherwise. bool bindingInfoChanged = false; int nodeIndex = _dataSourceChangeListeners.IndexOf(signalingOs); if (nodeIndex == -1) { throw new InvalidOperationException($"Could not get pointer to path element while processing " + $"DataSourceChanged event for {BinderName} in PropBagControlsWPF.Binders."); } ObservableSource parentOs = pathListeners[nodeIndex]; ObservableSourceStatusEnum status; // Initializing // Attempt to get a reference to the Source Root (Object with DataContext property or DataSourceProvider.) if (changeInfo.ChangeType == DataSourceChangeTypeEnum.Initializing) { System.Diagnostics.Debug.Assert(nodeIndex == ROOT_INDEX, $"The node index should refer to the ObservableSource for " + $"the root when DataSourceChangeType = {nameof(DataSourceChangeTypeEnum.Initializing)}."); string rootElementName = GetRootPathElementName(pathListeners); ObservableSourceProvider osp = GetSourceRoot(bindingTarget, bInfo.Source, rootElementName, BinderName); if (osp == null) { throw new InvalidOperationException($"{BinderName} could not locate a data source."); } status = pathListeners.ReplaceListener(ROOT_INDEX, ref osp, this.DataSourceHasChanged, out parentOs); if (parentOs?.IsListeningForNewDC != true) { throw new InvalidOperationException($"{BinderName} could not locate a data source."); } } // DataContextUpdated // Refresh the Source Root else if (changeInfo.ChangeType == DataSourceChangeTypeEnum.DataContextUpdated) { System.Diagnostics.Debug.Assert(nodeIndex == ROOT_INDEX, $"The node index should refer to the ObservableSource for" + $" the root when DataSourceChangeType = {nameof(DataSourceChangeTypeEnum.DataContextUpdated)}."); status = parentOs.Status; pathListeners.ResetListeners(ROOT_INDEX + 1, this.DataSourceHasChanged); bindingInfoChanged = true; } // Handle Collection Change else if (changeInfo.ChangeType == DataSourceChangeTypeEnum.CollectionChanged) { return(bindingInfoChanged); } // Handle Property Changed else if (changeInfo.ChangeType == DataSourceChangeTypeEnum.PropertyChanged) { // The PropModel of the value of this node's child may have changed. // Replace the child with a new SourceListner, if appropriate // and then fall-through to normal processing, at signaled step + 2. string changedPropName = changeInfo.PropertyName; nodeIndex++; ObservableSource child = pathListeners[nodeIndex]; bool matched = changedPropName == child.NewPathElement || changedPropName == "Item[]" && child.NewPathElement.StartsWith("["); if (!matched || !(nodeIndex < pathListeners.Count - 1)) { // Action is not required if // The updated property is not part of our path, // or our child in the terminal node, return(bindingInfoChanged); } // Replace the child, if // 1. It was null and now is not, or // 2. It is now null and was not null, or // 3. It was PropBag-based and is now is not, or // 4. It was a regular CLR object and is now PropBag-based, or // 5. It is still PropBag-Based and the prop item corresponding // to the grandchild node had a change in type. if (!child.Status.IsReadyOrWatching()) { string prevValueForNewPathElement = child.NewPathElement; ObservableSourceProvider newChildProvider = parentOs.GetChild(child.PathElement); if (newChildProvider?.Data == null) { // Still no data ?? System.Diagnostics.Debug.WriteLine("Child was null and is still null."); //ResetPathListeners(pathListeners, nodeIndex); pathListeners.ResetListeners(nodeIndex, this.DataSourceHasChanged); bindingInfoChanged = true; return(bindingInfoChanged); } //ObservableSourceStatusEnum newCStatus = // ReplaceListener(pathListeners, nodeIndex, ref newChildProvider, // this.DataSourceHasChanged, out ObservableSource newChild); ObservableSourceStatusEnum newCStatus = pathListeners.ReplaceListener(nodeIndex, ref newChildProvider, this.DataSourceHasChanged, out ObservableSource newChild); if (newChild != null) { string newPathElement = GetNewPathElement(newChild.PathElement, newChild.Type, parentOs.IsPropBagBased); newChild.NewPathElement = newPathElement; bindingInfoChanged = (newPathElement != prevValueForNewPathElement); } } // If the child was updated, begin processing our grand children. parentOs = pathListeners[nodeIndex]; status = parentOs?.Status ?? ObservableSourceStatusEnum.NoType; } else { throw new ApplicationException($"The DataSourceChangedType: {changeInfo.ChangeType} is not recognized."); } // Now, starting at step: stepNo + 1, replace or refresh each listener. int nPtr = nodeIndex + 1; for (; nPtr < pathListeners.Count - 1 && status.IsReadyOrWatching(); nPtr++) { // This is an itermediate step and the path has at least two components. parentOs.BeginListeningToSource(); if (nPtr > 1) { // Make sure we are listening to events that this ObservableSource may raise. // We know that we are subscribed to the root. parentOs.Subscribe(this.DataSourceHasChanged); } string pathElement = pathListeners[nPtr].PathElement; string prevValueForNewPathElement = pathListeners[nPtr].NewPathElement; ObservableSourceProvider osp = parentOs.GetChild(pathElement); //status = ReplaceListener(pathListeners, nPtr, ref osp, this.DataSourceHasChanged, // out ObservableSource os); status = pathListeners.ReplaceListener(nPtr, ref osp, this.DataSourceHasChanged, out ObservableSource os); if (os != null) { string newPathElement = GetNewPathElement(pathElement, os.Type, parentOs.IsPropBagBased); os.NewPathElement = newPathElement; bindingInfoChanged = (newPathElement != prevValueForNewPathElement); } // Note: if os is null, status will be "NoType", i.e. not ready. if (status == ObservableSourceStatusEnum.Ready) { System.Diagnostics.Debug.WriteLine($"Listener for {pathElement} is ready."); } else { System.Diagnostics.Debug.WriteLine($"Listener for {pathElement} is not ready."); } parentOs = os; //status = os.Status; -- Not needed, ReplaceListener sets this variable. } if (nPtr == pathListeners.Count - 1) { // Process terminal node. ObservableSource lastNode = pathListeners[nPtr]; if (status.IsReadyOrWatching()) { string newPathElement = GetNewPathElement(lastNode.PathElement, lastNode.Type, parentOs.IsPropBagBased); if (lastNode.NewPathElement != newPathElement) { lastNode.NewPathElement = newPathElement; bindingInfoChanged = true; } if (!status.IsWatching()) { parentOs.BeginListeningToSource(); } } } else { // Don't have all of the data present required to create a binding. System.Diagnostics.Debug.WriteLine($"RefreshPathListeners claims that no binding should be created for path = {bInfo.PropertyPath.Path}."); //ResetPathListeners(pathListeners, nPtr); pathListeners.ResetListeners(nPtr, this.DataSourceHasChanged); } return(bindingInfoChanged); }