protected Binding CreateTheBinding(BindingTarget bindingTarget, MyBindingInfo bInfo, Type sourceType, OSCollection pathListeners, out bool isCustom) { System.Diagnostics.Debug.Assert(sourceType != null, "The SourceType should never be null here."); // One node: 1 parent, 0 intervening objects, the terminal path element is "." -- The binding binds to the source data itself. // Two nodes: 1 parent, 0 intervening objects, 1 terminal path element. // Three nodes: 1 parent, 1 intervening object, 1 terminal path element. // Four nodes: 1 parent, 2 intervening objects, 1 terminal path element. // The next to last node, must have data, in order to avoid binding warnings. ObservableSource lastParent = pathListeners[pathListeners.Count - 2]; string strNewPath = pathListeners.GetNewPath(justForDiag: false); ObservableSourceStatusEnum lastParentStatus = lastParent.Status; System.Diagnostics.Debug.WriteLine($"Path = {bInfo.PropertyPath.Path}, NewPath = {strNewPath}, " + $"Terminal Node Status = {lastParentStatus}."); if (lastParentStatus.IsReadyOrWatching()) { // Check to see if DataContext holds source property. // When a DataContext is set via a binding, in some cases, // bindings that rely on that DataContext may get set // before the binding that provides the correct DataContext is set. ObservableSource root = pathListeners[ROOT_INDEX]; if (root.SourceKind == SourceKindEnum.FrameworkElement || root.SourceKind == SourceKindEnum.FrameworkContentElement) { string firstChildPathElement = pathListeners[ROOT_INDEX + 1].PathElement; if (!root.DoesChildExist(firstChildPathElement)) { System.Diagnostics.Debug.WriteLine($"No Binding is being created. Data is present, but doesn't contain source property: {firstChildPathElement}."); isCustom = false; return(null); } } // TODO: What about the original PropertyPath parameters? PropertyPath newPath = new PropertyPath(strNewPath); isCustom = lastParent.IsPropBagBased; // .IsPropBagBasedAndNoCustomTypeDescriptors; return(CreateBinding(bindingTarget, bInfo, sourceType, newPath, isCustom)); } else { System.Diagnostics.Debug.Assert(pathListeners[0].IsListeningForNewDC, "There are no active listeners!"); System.Diagnostics.Debug.WriteLine("No Binding is being created, not enough data."); isCustom = false; return(null); } }
protected virtual void DataSourceHasChanged(object sender, DataSourceChangedEventArgs e) { BindingBase oldBinding; if (BindingTarget.IsDependencyProperty) { oldBinding = GetTheBindingBase(BindingTarget.DependencyObject, BindingTarget.DependencyProperty); } else { oldBinding = null; } bool hadBinding = oldBinding != null; if (!e.DataWasUpdated && oldBinding != null) { return; } ObservableSource os = (ObservableSource)sender; bool bindingInfoChanged = RefreshPathListeners(BindingTarget, BindingInfo, SourceType, _dataSourceChangeListeners, e, os); if (!bindingInfoChanged && oldBinding != null) { return; } Binding newBinding = CreateTheBinding(BindingTarget, BindingInfo, SourceType, _dataSourceChangeListeners, out bool isCustom); if (hadBinding && BindingTarget.IsDependencyProperty) { ClearTheBinding(BindingTarget.DependencyObject, BindingTarget.DependencyProperty); } if (newBinding != null) { try { //BindingExpressionBase bExp = BindingOperations.SetBinding(_targetObject, // _targetProperty, newBinding); if (BindingTarget.IsDependencyProperty) { ClearTheBinding(BindingTarget.DependencyObject, BindingTarget.DependencyProperty); BindingExpressionBase bExp = SetTheBinding(BindingTarget.DependencyObject, BindingTarget.DependencyProperty, newBinding); string bType = isCustom ? "PropBag-Based" : "Standard"; System.Diagnostics.Debug.WriteLine($"CREATING {bType} BINDING from {newBinding.Path.Path} to {BindingTarget.PropertyName} on object: {BindingTarget.ObjectName}."); } } catch { System.Diagnostics.Debug.WriteLine("Attempt to SetBinding failed."); } if (hadBinding) { System.Diagnostics.Debug.WriteLine($"{BinderName} set a new binding on some target that is replacing an existing binding."); } else { System.Diagnostics.Debug.WriteLine($"{BinderName} set a new binding on some target that had no binding previously."); } } else { if (hadBinding) { System.Diagnostics.Debug.WriteLine($"{BinderName} removed binding on some target and replaced it with no binding."); } } }
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); }