public static bool IsReadyOrWatching(this ObservableSourceStatusEnum status) { return(status == ObservableSourceStatusEnum.Ready || status == ObservableSourceStatusEnum.IsWatchingColl || status == ObservableSourceStatusEnum.IsWatchingProp || status == ObservableSourceStatusEnum.IsWatchingPropAndColl); }
//public void UpdateData() //{ // if (!(SourceKind == SourceKindEnum.FrameworkElement || SourceKind == SourceKindEnum.FrameworkContentElement || SourceKind == SourceKindEnum.DataSourceProvider || SourceKind == SourceKindEnum.DataGridColumn)) // { // throw new InvalidOperationException($"Only ObservableSources with SourceKind =" + // $" {nameof(SourceKindEnum.FrameworkElement)} or " + // $"{nameof(SourceKindEnum.FrameworkContentElement)} " + // $"{nameof(SourceKindEnum.DataSourceProvider)} " + // $"can have their data updated."); // } // object newData; // Type newType = null; // ObservableSourceStatusEnum newStatus = ObservableSourceStatusEnum.NoType; // if (SourceKind == SourceKindEnum.DataSourceProvider) // { // newData = ((DataSourceProvider)Container).Data; // if (newData != null) // { // newType = newData.GetType(); // } // newStatus = Status.SetReady(newData != null); // } // else // { // DependencyObject anchor = (DependencyObject)this.AnchorElement; // if (!GetDcFromFrameworkElement(anchor, out newData, out newType, out newStatus)) // { // throw new ApplicationException($"TargetObject in {this.BinderName}.ObservableSource was neither a FrameworkElement or a FrameworkContentElement."); // } // } // UpdateData(newData, newType, newStatus); //} private bool UpdateData(object newData, Type newType, ObservableSourceStatusEnum newStatus) { if (!(SourceKind == SourceKindEnum.FrameworkElement || SourceKind == SourceKindEnum.FrameworkContentElement || SourceKind == SourceKindEnum.DataSourceProvider || SourceKind == SourceKindEnum.DataGridColumn)) { throw new InvalidOperationException($"Only ObservableSources with SourceKind =" + $" {nameof(SourceKindEnum.FrameworkElement)} or " + $"{nameof(SourceKindEnum.FrameworkContentElement)} " + $"{nameof(SourceKindEnum.DataSourceProvider)} " + $"{nameof(SourceKindEnum.DataGridColumn)} " + $"can have their data updated."); } bool changed; object oldData = Data; if (oldData != null && newData != null) { if (object.ReferenceEquals(oldData, newData)) { System.Diagnostics.Debug.WriteLine("Update ObservableSource found identical data already present."); changed = false; } else { System.Diagnostics.Debug.WriteLine("Update ObservableSource found (different) data already present."); changed = true; } } else if (!(oldData == null && newData == null) && (oldData == null || newData == null)) { changed = true; } else { changed = false; } // Remove existing subscriptions if any for the existing Data. if (oldData != null && IsDcListening) { RemoveSubscriptions(oldData); } Data = newData; Type = newType; Status = newStatus; return(changed); }
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); } }
private bool UpdateData_Dg(DataGridColumn dgc, string pathElement, string binderName) { if (SourceKind != SourceKindEnum.DataGridColumn) { throw new InvalidOperationException($"Cannot call {nameof(UpdateData_Dg)} " + $"if the ObservableSource is not of SourceKind: {nameof(SourceKindEnum.DataGridColumn)}."); } System.Diagnostics.Debug.WriteLine($"Fetching DataGrid for a DataGridColumn for {pathElement}."); DataGrid dataGrid = LogicalTree.GetDataGridOwner(dgc); Type newType = dataGrid?.GetType(); ObservableSourceStatusEnum newStatus = Status.SetReady(dataGrid != null); bool changed = UpdateData(dataGrid?.SelectedItems, newType, newStatus); return(changed); }
private void AddSubscriptions(object dc) { ObservableSourceStatusEnum workStatus = ObservableSourceStatusEnum.Undetermined; bool addedIt = false; if (dc is INotifyPCGen pcGen) { WeakEventManager <INotifyPCGen, PcGenEventArgs> .AddHandler(pcGen, "PropertyChangedWithGenVals", PCGenEvent_Handler); workStatus = ObservableSourceStatusEnum.IsWatchingProp; addedIt = true; } if (dc is INotifyPropertyChanged pc) { WeakEventManager <INotifyPropertyChanged, PropertyChangedEventArgs> .AddHandler(pc, "PropertyChanged", OnPCEvent); workStatus = ObservableSourceStatusEnum.IsWatchingProp; addedIt = true; } if (dc is INotifyCollectionChanged cc) { WeakEventManager <INotifyCollectionChanged, CollectionChangeEventArgs> .AddHandler(cc, "CollectionChanged", OnCCEvent); workStatus = workStatus.SetWatchingColl(); addedIt = true; } if (!addedIt) { string msg = "Cannot create subscriptions. Object does not implement INotifyPropertyChanged, " + "nor does it implement INotifyCollectionChanged."; System.Diagnostics.Debug.WriteLine(msg); //throw new ApplicationException(msg); } Status = workStatus; }
public static bool IsNowReady(this ObservableSourceStatusEnum status, ObservableSourceStatusEnum oldStatus) { return(status.IsReadyOrWatching() && !oldStatus.IsReadyOrWatching()); }
public static bool NoLongerReady(this ObservableSourceStatusEnum status, ObservableSourceStatusEnum oldStatus) { return(!status.IsReadyOrWatching() && oldStatus.IsReadyOrWatching()); }
public static ObservableSourceStatusEnum SetWatchingColl(this ObservableSourceStatusEnum status) { return((status == ObservableSourceStatusEnum.IsWatchingProp) ? ObservableSourceStatusEnum.IsWatchingPropAndColl : ObservableSourceStatusEnum.IsWatchingColl); }
public static ObservableSourceStatusEnum SetReady(this ObservableSourceStatusEnum status, bool haveData) { return((haveData) ? ObservableSourceStatusEnum.Ready : ObservableSourceStatusEnum.NoType); }
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); }
private bool UpdateWatcherAndData_Fe(DependencyObject targetObject, string pathElement, bool isTargetADc, string binderName) { if (SourceKind != SourceKindEnum.FrameworkElement && SourceKind != SourceKindEnum.FrameworkContentElement) { throw new InvalidOperationException($"Cannot call {nameof(UpdateWatcherAndData_Fe)} " + $"if the ObservableSource does not have a SourceKind of {nameof(SourceKindEnum.FrameworkElement)} " + $"or {nameof(SourceKindEnum.FrameworkContentElement)}."); } string fwElementName = LogicalTree.GetNameFromDepObject(targetObject); System.Diagnostics.Debug.WriteLine($"Fetching DataContext to use from: {fwElementName} for pathElement: {pathElement}."); this.IsTargetADc = isTargetADc; // If this binding sets a DataContext, watch the TargetObject's parent, otherwise watch the TargetObject for DataContext updates // TODO: May want to make sure that the value of Container is a DependencyObject. DependencyObject curContainer = (DependencyObject)Container; DependencyObject newContainer = isTargetADc ? LogicalTreeHelper.GetParent(targetObject) : targetObject; if (!object.ReferenceEquals(curContainer, newContainer)) { SubscribeTo_FcOrFce(newContainer, DataContextChanged_Fe); Container = newContainer; } // Now see if we can find a data context. DependencyObject foundNode = LogicalTree.GetDataContext(targetObject, out bool foundIt, startWithParent: isTargetADc, inspectAncestors: true, stopOnNodeWithBoundDc: true); if (!foundIt) { bool changed = UpdateData(null, null, ObservableSourceStatusEnum.NoType); return(changed); } if (!object.ReferenceEquals(targetObject, foundNode)) { string foundFwElementName = LogicalTree.GetNameFromDepObject(foundNode); System.Diagnostics.Debug.WriteLine($"Found DataContext to watch using an ancestor: {foundFwElementName} for pathElement: {pathElement}."); } else { System.Diagnostics.Debug.WriteLine($"Found DataContext to watch on the target object for pathElement: {pathElement}."); } DependencyObject foundNodeWithBoundDc = LogicalTree.GetDataContextWithBoundDc(targetObject, out bool foundOneWithBoundDc, startWithParent: isTargetADc); if (foundOneWithBoundDc) { System.Diagnostics.Debug.WriteLine("Some parent has a DataContext that is set via a Binding Markup."); } if (foundNode is FrameworkElement fe) { Type newType = fe.DataContext?.GetType(); ObservableSourceStatusEnum newStatus = Status.SetReady(fe.DataContext != null); bool changed = UpdateData(fe.DataContext, newType, newStatus); //if(newType.IsIListSource()) //{ // changed = UpdateData(((IListSource)fe.DataContext).GetList(), newType, newStatus); //} //else //{ // changed = UpdateData(fe.DataContext, newType, newStatus); //} return(changed); } else if (foundNode is FrameworkContentElement fce) { Type newType = fce.DataContext?.GetType(); ObservableSourceStatusEnum newStatus = Status.SetReady(fce.DataContext != null); bool changed = UpdateData(fce.DataContext, newType, newStatus); return(changed); } else { throw new ApplicationException($"Found node in {binderName}.ObservableSourceProvider was neither a FrameworkElement or a FrameworkContentElement."); } }
private bool GetDcFromFrameworkElement(object feOrFce, out object dc, out Type type, out ObservableSourceStatusEnum status) { bool found = LogicalTree.GetDcFromFrameworkElement(feOrFce, out dc, out type); status = Status.SetReady(dc != null); return(found); }