/// <summary> /// Called whenever the datacontext of one of the inner source binding changed. /// </summary> /// <param name="sender">A framework element for which the property changed.</param> /// <param name="e">Information about the datacontext changed event.</param> private void DatacontextTarget_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e) { if (sender is FrameworkElement casted) { // Only if extension is still alive: if (IsExtensionValid) { // Loop through every bindings set on datacontext that we registered, // and hook to collection change event for the new datacontext value: foreach (var binding in DataContextBindings.Keys) { if (DataContextBindings[binding] == casted) { if (BindingHelpers.ResolvePathValue(casted.DataContext, binding.Path) is INotifyCollectionChanged collection) { CollectionChangedEventManager.AddListener(collection, this); } } } } else { casted.DataContextChanged -= DatacontextTarget_DataContextChanged; } } }
/// <summary> /// Gets the current value of the binding targeted property. /// </summary> /// <param name="binding">The binding description indicating where to find the source and the value.</param> /// <param name="target">A target object where the source might be found.</param> /// <returns>The property value that is described by the binding.</returns> protected object getBindingSourcePropertyValue(Binding binding, FrameworkElement target) { var sourceElement = getBindingSource(binding, target); // Process source element to retrieve target property: if (sourceElement != null) { return(BindingHelpers.ResolvePathValue(sourceElement, binding.Path)); } else { return(null); } }
/// <summary> /// Resolves path binding and prepares the inner custom binding with resolved path value. /// </summary> /// <param name="serviceProvider">Service provider given by the framework.</param> /// <returns>True if successfully prepared path binding, false if cannot resolve its value.</returns> private bool ResolveBoundPathBinding(IServiceProvider serviceProvider = null) { if (PathValueBinding == null) { return(false); } // If source is not already resolved: if (boundPathSource == null) { // Try to get the source of value for the path value binding using base mechanisms: boundPathSource = serviceProvider != null?BindingHelpers.GetBindingSource(PathValueBinding, serviceProvider, out bool source_is_resolved, out bool source_is_datacontext) : BindingHelpers.GetBindingSource(PathValueBinding, TargetObject, out source_is_resolved, out source_is_datacontext); if (!source_is_resolved || boundPathSource == null) { return(false); } // If source is datacontext then track it for future changes: if (source_is_datacontext) { (TargetObject as FrameworkElement).DataContextChanged += TargetObject_DataContextChanged; } } // Try to resolve path binding value on the source: var value = BindingHelpers.ResolvePathValue(boundPathSource, PathValueBinding.Path); // If not given a proper property path format: if (!(value is string) && !(value is PropertyPath)) { StopTrackingPathBindingUpdates(); // as we did not resolved binding, stop tracking changes on current bind if any. return(false); } // Update bound path binding by getting a copy of the precedent value: if (!providing_value) { boundPathBinding = boundPathBinding?.Clone(); if (boundPathBinding == null) { return(false); } } // Build new inner binding path based on resolved value + OverridePath property: if (value is string as_string) { if (!OverridePath && Path != null) { boundPathBinding.Path = new PropertyPath(Path.Path + "." + as_string); // TODO: path parameters? } else { boundPathBinding.Path = new PropertyPath(as_string); } } else if (value is PropertyPath asPropertyPath) { if (!OverridePath && Path != null) { boundPathBinding.Path = new PropertyPath(Path.Path + "." + asPropertyPath.Path, asPropertyPath.PathParameters); } else { boundPathBinding.Path = asPropertyPath; } } // As we resolved binding, track any further changes: StartTrackingPathBindingUpdates(); return(true); }