예제 #1
0
        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);
            }
        }
예제 #2
0
        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.");
                }
            }
        }
예제 #3
0
        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);
        }