Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Framework / System / Windows / Data / MultiBindingExpression.cs / 1458001 / MultiBindingExpression.cs
//---------------------------------------------------------------------------- // //// Copyright (C) Microsoft Corporation. All rights reserved. // // // Description: Defines MultiBindingExpression object, uses a collection of BindingExpressions together. // // See spec at http://avalon/connecteddata/Specs/Data%20Binding.mht // //--------------------------------------------------------------------------- using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Globalization; using System.Windows.Threading; using System.Threading; using System.Windows.Controls; using System.Windows.Input; // FocusChangedEvent using System.Windows.Markup; using MS.Internal.Controls; // Validation using MS.Internal.KnownBoxes; using MS.Internal.Data; using MS.Utility; using MS.Internal; // Invariant.Assert namespace System.Windows.Data { ////// Describes a collection of BindingExpressions attached to a single property. /// The inner BindingExpressions contribute their values to the MultiBindingExpression, /// which combines/converts them into a resultant final value. /// In the reverse direction, the target value is tranlated to /// a set of values that are fed back into the inner BindingExpressions. /// public sealed class MultiBindingExpression: BindingExpressionBase, IDataBindEngineClient { //----------------------------------------------------- // // Constructors // //----------------------------------------------------- ///Constructor private MultiBindingExpression(MultiBinding binding, BindingExpressionBase owner) : base(binding, owner) { int count = binding.Bindings.Count; // reduce repeated allocations _tempValues = new object[count]; _tempTypes = new Type[count]; } //------------------------------------------------------ // // Interfaces // //----------------------------------------------------- void IDataBindEngineClient.TransferValue() { TransferValue(); } void IDataBindEngineClient.UpdateValue() { UpdateValue(); } bool IDataBindEngineClient.AttachToContext(bool lastChance) { AttachToContext(lastChance); return !TransferIsDeferred; } void IDataBindEngineClient.VerifySourceReference(bool lastChance) { } void IDataBindEngineClient.OnTargetUpdated() { OnTargetUpdated(); } DependencyObject IDataBindEngineClient.TargetElement { get { return TargetElement; } } //------------------------------------------------------ // // Public Properties // //------------------------------------------------------ ///Binding from which this expression was created public MultiBinding ParentMultiBinding { get { return (MultiBinding)ParentBindingBase; } } ///List of inner BindingExpression public ReadOnlyCollectionBindingExpressions { get { return new ReadOnlyCollection (MutableBindingExpressions); } } //----------------------------------------------------- // // Public Methods // //------------------------------------------------------ /// Send the current value back to the source(s) ///Does nothing when binding's Mode is not TwoWay or OneWayToSource public override void UpdateSource() { // ultimately, what would be better would be to have a status flag that // indicates that this MultiBindingExpression has been Detached, as opposed to a // MultiBindingExpression that doesn't have anything in its BindingExpressions collection // in the first place. Added to which, there should be distinct error // messages for both of these error conditions. if (MutableBindingExpressions.Count == 0) throw new InvalidOperationException(SR.Get(SRID.BindingExpressionIsDetached)); NeedsUpdate = true; // force update Update(true); // update synchronously } ///Force a data transfer from sources to target ///Will transfer data even if binding's Mode is OneWay public override void UpdateTarget() { // ultimately, what would be better would be to have a status flag that // indicates that this MultiBindingExpression has been Detached, as opposed to a // MultiBindingExpression that doesn't have anything in its BindingExpressions collection // in the first place. Added to which, there should be distinct error // messages for both of these error conditions. if (MutableBindingExpressions.Count == 0) throw new InvalidOperationException(SR.Get(SRID.BindingExpressionIsDetached)); UpdateTarget(true); } #region Expression overrides ////// Called to evaluate the Expression value /// /// DependencyObject being queried /// Property being queried ///Computed value. Unset if unavailable. internal override object GetValue(DependencyObject d, DependencyProperty dp) { return Value; } ////// Allows Expression to store set values /// /// DependencyObject being set /// Property being set /// Value being set ///true if Expression handled storing of the value internal override bool SetValue(DependencyObject d, DependencyProperty dp, object value) { if (IsReflective) { Value = value; return true; } else { // if the binding doesn't push values back to the source, allow // SetValue to overwrite the binding with a local value return false; } } #endregion Expression overrides //----------------------------------------------------- // // Internal Properties // //----------------------------------------------------- internal override bool IsParentBindingUpdateTriggerDefault { get { return (ParentMultiBinding.UpdateSourceTrigger == UpdateSourceTrigger.Default); } } //----------------------------------------------------- // // Internal Methods // //------------------------------------------------------ // Create a new BindingExpression from the given Binding description internal static MultiBindingExpression CreateBindingExpression(DependencyObject d, DependencyProperty dp, MultiBinding binding, BindingExpressionBase owner) { FrameworkPropertyMetadata fwMetaData = dp.GetMetadata(d.DependencyObjectType) as FrameworkPropertyMetadata; if ((fwMetaData != null && !fwMetaData.IsDataBindingAllowed) || dp.ReadOnly) throw new ArgumentException(SR.Get(SRID.PropertyNotBindable, dp.Name), "dp"); // create the BindingExpression MultiBindingExpression bindExpr = new MultiBindingExpression(binding, owner); bindExpr.ResolvePropertyDefaultSettings(binding.Mode, binding.UpdateSourceTrigger, fwMetaData); return bindExpr; } // Attach to things that may require tree context (parent, root, etc.) void AttachToContext(bool lastChance) { DependencyObject target = TargetElement; if (target == null) return; Debug.Assert(ParentMultiBinding.Converter != null || !String.IsNullOrEmpty(EffectiveStringFormat), "MultiBindingExpression should not exist if its bind does not have a valid converter."); bool isExtendedTraceEnabled = TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.AttachToContext); _converter = ParentMultiBinding.Converter; if (_converter == null && String.IsNullOrEmpty(EffectiveStringFormat)) { TraceData.Trace(TraceEventType.Error, TraceData.MultiBindingHasNoConverter, ParentMultiBinding); } if (isExtendedTraceEnabled) { TraceData.Trace(TraceEventType.Warning, TraceData.AttachToContext( TraceData.Identify(this), lastChance ? " (last chance)" : String.Empty)); } TransferIsDeferred = true; bool attached = true; // true if all child bindings have attached int count = MutableBindingExpressions.Count; for (int i = 0; i < count; ++i) { if (MutableBindingExpressions[i].Status == BindingStatus.Unattached) attached = false; } // if the child bindings aren't ready yet, try again later. Leave // TransferIsDeferred set, to indicate we're not ready yet. if (!attached && !lastChance) { if (isExtendedTraceEnabled) { TraceData.Trace(TraceEventType.Warning, TraceData.ChildNotAttached( TraceData.Identify(this))); } return; } // initial transfer bool initialTransferIsUpdate = IsOneWayToSource; object currentValue; if (ShouldUpdateWithCurrentValue(target, out currentValue)) { initialTransferIsUpdate = true; ChangeValue(currentValue, /*notify*/false); NeedsUpdate = true; } SetStatus(BindingStatus.Active); if (!initialTransferIsUpdate) { UpdateTarget(false); } else { UpdateValue(); } } //----------------------------------------------------- // // Public Properties // //------------------------------------------------------ ////// The ValidationError that caused this /// BindingExpression to be invalid. /// public override ValidationError ValidationError { get { ValidationError validationError = base.ValidationError; if (validationError == null) { for ( int i = 0; i < MutableBindingExpressions.Count; i++ ) { validationError = MutableBindingExpressions[i].ValidationError; if (validationError != null) break; } } return validationError; } } ////// HasError returns true if any of the ValidationRules /// of any of its inner bindings failed its validation rule /// or the Multi-/PriorityBinding itself has a failing validation rule. /// public override bool HasError { get { bool hasError = base.HasError; if (!hasError) { for ( int i = 0; i < MutableBindingExpressions.Count; i++ ) { if (MutableBindingExpressions[i].HasError) return true; } } return hasError; } } //------------------------------------------------------ // // Protected Internal Methods // //----------------------------------------------------- ////// Attach a BindingExpression to the given target (element, property) /// /// DependencyObject being set /// Property being set internal override bool AttachOverride(DependencyObject d, DependencyProperty dp) { if (!base.AttachOverride(d, dp)) return false; DependencyObject target = TargetElement; if (target == null) return false; // listen for lost focus if (IsUpdateOnLostFocus) { LostFocusEventManager.AddListener(target, this); } TransferIsDeferred = true; // Defer data transfer until after we activate all the BindingExpressions int count = ParentMultiBinding.Bindings.Count; for (int i = 0; i < count; ++i) { // ISSUE: It may be possible to have _attachedBindingExpressions be non-zero // at the end of Detach if the conditions for the increment on Attach // and the decrement on Detach are not precisely the same. AttachBindingExpression(i, false); // create new binding and have it added to end } // attach to things that need tree context. Do it synchronously // if possible, otherwise post a task. This gives the parser et al. // a chance to assemble the tree before we start walking it. AttachToContext(false /* lastChance */); if (TransferIsDeferred) { Engine.AddTask(this, TaskOps.AttachToContext); if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.AttachToContext)) { TraceData.Trace(TraceEventType.Warning, TraceData.DeferAttachToContext( TraceData.Identify(this))); } } return true; } ///sever all connections internal override void DetachOverride() { DependencyObject target = TargetElement; if (target != null && IsUpdateOnLostFocus) { LostFocusEventManager.RemoveListener(target, this); } // Theoretically, we only need to detach number of AttentiveBindingExpressions, // but we'll traverse the whole list anyway and do aggressive clean-up. int count = MutableBindingExpressions.Count; for (int i = count - 1; i >= 0; i--) { BindingExpressionBase b = MutableBindingExpressions[i]; if (b != null) { b.Detach(); MutableBindingExpressions.RemoveAt(i); } } ChangeSources(null); base.DetachOverride(); } ////// Invalidate the given child expression. /// internal override void InvalidateChild(BindingExpressionBase bindingExpression) { int index = MutableBindingExpressions.IndexOf(bindingExpression); // do a sanity check that we care about this BindingExpression if (0 <= index && IsDynamic) { NeedsDataTransfer = true; Transfer(); // this will Invalidate target property. } } ////// Change the dependency sources for the given child expression. /// internal override void ChangeSourcesForChild(BindingExpressionBase bindingExpression, WeakDependencySource[] newSources) { int index = MutableBindingExpressions.IndexOf(bindingExpression); if (index >= 0) { WeakDependencySource[] combinedSources = CombineSources(index, MutableBindingExpressions, MutableBindingExpressions.Count, newSources); ChangeSources(combinedSources); } } ////// Replace the given child expression with a new one. /// internal override void ReplaceChild(BindingExpressionBase bindingExpression) { int index = MutableBindingExpressions.IndexOf(bindingExpression); DependencyObject target = TargetElement; if (index >= 0 && target != null) { // detach and clean up the old binding bindingExpression.Detach(); // replace BindingExpression AttachBindingExpression(index, true); } } // register the leaf bindings with the binding group internal override void UpdateBindingGroup(BindingGroup bg) { for (int i=0, n=MutableBindingExpressions.Count-1; i/// Get the converted proposed value /// internal override object ConvertProposedValue(object value) { object result; bool success = ConvertProposedValueImpl(value, out result); // if the conversion failed, signal a validation error if (!success) { result = DependencyProperty.UnsetValue; ValidationError validationError = new ValidationError(ConversionValidationRule.Instance, this, SR.Get(SRID.Validation_ConversionFailed, value), null); UpdateValidationError(validationError); } return result; } private bool ConvertProposedValueImpl(object value, out object result) { DependencyObject target = TargetElement; if (target == null) { result = DependencyProperty.UnsetValue; return false; } result = GetValuesForChildBindings(value); if (result == DependencyProperty.UnsetValue) { SetStatus(BindingStatus.UpdateSourceError); return false; } object[] values = (object[])result; if (values == null) { if (TraceData.IsEnabled) { TraceData.Trace(TraceEventType.Error, TraceData.BadMultiConverterForUpdate( Converter.GetType().Name, AvTrace.ToStringHelper(value), AvTrace.TypeName(value)), this); } result = DependencyProperty.UnsetValue; return false; } if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.Update)) { for (int i=0; i /// Get the converted proposed value and inform the binding group /// internal override bool ObtainConvertedProposedValue(BindingGroup bindingGroup) { bool result = true; if (NeedsUpdate) { object value = bindingGroup.GetValue(this); if (value != DependencyProperty.UnsetValue) { object[] values; value = ConvertProposedValue(value); if (value == DependencyProperty.UnsetValue) { result = false; } else if ((values = value as object[]) != null) { for (int i=0; i /// Update the source value /// internal override object UpdateSource(object convertedValue) { if (convertedValue == DependencyProperty.UnsetValue) { SetStatus(BindingStatus.UpdateSourceError); return convertedValue; } object[] values = convertedValue as object[]; int count = MutableBindingExpressions.Count; if (values.Length < count) count = values.Length; BeginSourceUpdate(); for (int i = 0; i < count; ++i) { object value = values[i]; if (value != Binding.DoNothing) { BindingExpressionBase bindExpr = MutableBindingExpressions[i]; bindExpr.UpdateSource(value); if (bindExpr.Status == BindingStatus.UpdateSourceError) { SetStatus(BindingStatus.UpdateSourceError); } } } EndSourceUpdate(); OnSourceUpdated(); return convertedValue; } /// /// Update the source value and inform the binding group /// internal override bool UpdateSource(BindingGroup bindingGroup) { bool result = true; if (NeedsUpdate) { object value = bindingGroup.GetValue(this); UpdateSource(value); if (value == DependencyProperty.UnsetValue) { result = false; } } return result; } /// /// Store the value in the binding group /// internal override void StoreValueInBindingGroup(object value, BindingGroup bindingGroup) { bindingGroup.SetValue(this, value); object[] values = value as object[]; if (values != null) { int count = MutableBindingExpressions.Count; if (values.Length < count) count = values.Length; for (int i=0; i=0; --i) { MutableBindingExpressions[i].StoreValueInBindingGroup(DependencyProperty.UnsetValue, bindingGroup); } } } /// /// Run validation rules for the given step /// internal override bool Validate(object value, ValidationStep validationStep) { if (value == Binding.DoNothing) return true; if (value == DependencyProperty.UnsetValue) { SetStatus(BindingStatus.UpdateSourceError); return false; } // run rules attached to this multibinding bool result = base.Validate(value, validationStep); // run rules attached to the child bindings switch (validationStep) { case ValidationStep.RawProposedValue: // the child bindings don't get raw values until the Convert step break; default: object[] values = value as object[]; int count = MutableBindingExpressions.Count; if (values.Length < count) count = values.Length; for (int i=0; i /// Run validation rules for the given step, and inform the binding group /// internal override bool CheckValidationRules(BindingGroup bindingGroup, ValidationStep validationStep) { if (!NeedsValidation) return true; object value; switch (validationStep) { case ValidationStep.RawProposedValue: case ValidationStep.ConvertedProposedValue: case ValidationStep.UpdatedValue: case ValidationStep.CommittedValue: value = bindingGroup.GetValue(this); break; default: throw new InvalidOperationException(SR.Get(SRID.ValidationRule_UnknownStep, validationStep, bindingGroup)); } bool result = Validate(value, validationStep); if (result && validationStep == ValidationStep.CommittedValue) { NeedsValidation = false; } return result; } /// private Collection/// Get the proposed value(s) that would be written to the source(s), applying /// conversion and checking UI-side validation rules. /// internal override bool ValidateAndConvertProposedValue(out Collectionvalues) { Debug.Assert(NeedsValidation, "check NeedsValidation before calling this"); values = null; // validate raw proposed value object rawValue = GetRawProposedValue(); bool isValid = Validate(rawValue, ValidationStep.RawProposedValue); if (!isValid) { return false; } // apply conversion object conversionResult = GetValuesForChildBindings(rawValue); if (conversionResult == DependencyProperty.UnsetValue || conversionResult == null) { return false; } int count = MutableBindingExpressions.Count; object[] convertedValues = (object[])conversionResult; if (convertedValues.Length < count) count = convertedValues.Length; values = new Collection (); bool result = true; // validate child bindings for (int i = 0; i < count; ++i) { object value = convertedValues[i]; if (value == Binding.DoNothing) { } else if (value == DependencyProperty.UnsetValue) { // conversion failure result = false; } else { // send converted value to child binding BindingExpressionBase bindExpr = MutableBindingExpressions[i]; bindExpr.Value = value; // validate child binding if (bindExpr.NeedsValidation) { Collection childValues; bool childResult = bindExpr.ValidateAndConvertProposedValue(out childValues); // append child's values to our values if (childValues != null) { for (int k=0, n=childValues.Count; k /// expose a mutable version of the list of all BindingExpressions; /// derived internal classes need to be able to populate this list /// MutableBindingExpressions { get { return _list; } } IMultiValueConverter Converter { get { return _converter; } set { _converter = value; } } //----------------------------------------------------- // // Private Methods // //----------------------------------------------------- // Create a BindingExpression for position i BindingExpressionBase AttachBindingExpression(int i, bool replaceExisting) { DependencyObject target = TargetElement; if (target == null) return null; BindingBase binding = ParentMultiBinding.Bindings[i]; // Check if replacement bindings have the correct UpdateSourceTrigger MultiBinding.CheckTrigger(binding); BindingExpressionBase bindExpr = binding.CreateBindingExpression(target, TargetProperty, this); if (replaceExisting) // replace exisiting or add as new binding? MutableBindingExpressions[i] = bindExpr; else MutableBindingExpressions.Add(bindExpr); bindExpr.Attach(target, TargetProperty); return bindExpr; } internal override void HandlePropertyInvalidation(DependencyObject d, DependencyPropertyChangedEventArgs args) { DependencyProperty dp = args.Property; int n = MutableBindingExpressions.Count; if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.Events)) { TraceData.Trace(TraceEventType.Warning, TraceData.GotPropertyChanged( TraceData.Identify(this), TraceData.Identify(d), dp.Name)); } bool isConnected = true; TransferIsDeferred = true; for (int i = 0; i < n; ++i) { BindingExpressionBase bindExpr = MutableBindingExpressions[i]; if (bindExpr != null) { DependencySource[] sources = bindExpr.GetSources(); if (sources != null) { for (int j = 0; j < sources.Length; ++j) { DependencySource source = sources[j]; if (source.DependencyObject == d && source.DependencyProperty == dp) { bindExpr.OnPropertyInvalidation(d, args); break; } } } if (bindExpr.IsDisconnected) { isConnected = false; } } } TransferIsDeferred = false; if (isConnected) { Transfer(); // Transfer if inner BindingExpressions have called Invalidate(binding) } else { Disconnect(); } } /// /// Handle events from the centralized event table /// internal override bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e) { if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.Events)) { TraceData.Trace(TraceEventType.Warning, TraceData.GotEvent( TraceData.Identify(this), TraceData.IdentifyWeakEvent(managerType), TraceData.Identify(sender))); } if (managerType == typeof(LostFocusEventManager)) { Update(true); } else { return base.ReceiveWeakEvent(managerType, sender, e); } return true; } #region Value ///Force a data transfer from source(s) to target /// /// use true to propagate UpdateTarget call to all inner BindingExpressions; /// use false to avoid forcing data re-transfer from one-time inner BindingExpressions /// void UpdateTarget(bool includeInnerBindings) { TransferIsDeferred = true; if (includeInnerBindings) { foreach (BindingExpressionBase b in MutableBindingExpressions) { b.UpdateTarget(); } } TransferIsDeferred = false; NeedsDataTransfer = true; // force data transfer Transfer(); } // transfer a value from the source to the target void Transfer() { // required state for transfer if ( NeedsDataTransfer // Transfer is needed && Status != BindingStatus.Unattached // All bindings are attached && !TransferIsDeferred) // Not aggregating transfers { TransferValue(); } } // transfer a value from the source to the target void TransferValue() { IsInTransfer = true; NeedsDataTransfer = false; DependencyObject target = TargetElement; if (target == null) goto Done; bool isExtendedTraceEnabled = TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.Transfer); object value = DependencyProperty.UnsetValue; object preFormattedValue = _tempValues; CultureInfo culture = GetCulture(); // gather values from inner BindingExpressions int count = MutableBindingExpressions.Count; for (int i = 0; i < count; ++i) { _tempValues[i] = MutableBindingExpressions[i].GetValue(target, TargetProperty); // could pass (null, null) if (isExtendedTraceEnabled) { TraceData.Trace(TraceEventType.Warning, TraceData.GetRawValueMulti( TraceData.Identify(this), i, TraceData.Identify(_tempValues[i]))); } } // apply the converter if (Converter != null) { // MultiValueConverters are always user-defined, so don't catch exceptions (bug 992237) preFormattedValue = Converter.Convert(_tempValues, TargetProperty.PropertyType, ParentMultiBinding.ConverterParameter, culture); if (isExtendedTraceEnabled) { TraceData.Trace(TraceEventType.Warning, TraceData.UserConverter( TraceData.Identify(this), TraceData.Identify(preFormattedValue))); } } else if (EffectiveStringFormat != null) { // preFormattedValue = _tempValues; // But check for child binding conversion errors for (int i=0; i<_tempValues.Length; ++i) { if (_tempValues[i] == DependencyProperty.UnsetValue) { preFormattedValue = DependencyProperty.UnsetValue; break; } } } else // no converter (perhaps user specified it in error) { if (TraceData.IsEnabled) { TraceData.Trace(TraceEventType.Error, TraceData.MultiValueConverterMissingForTransfer, this); } goto Done; } // apply string formatting if (EffectiveStringFormat == null || preFormattedValue == Binding.DoNothing || preFormattedValue == DependencyProperty.UnsetValue) { value = preFormattedValue; } else { try { // we call String.Format either with multiple values (obtained from // the child bindings) or a single value (as produced by the converter). // The if-test is needed to avoid wrapping _tempValues inside another object[]. if (preFormattedValue == _tempValues) { value = String.Format(culture, EffectiveStringFormat, _tempValues); } else { value = String.Format(culture, EffectiveStringFormat, preFormattedValue); } if (isExtendedTraceEnabled) { TraceData.Trace(TraceEventType.Warning, TraceData.FormattedValue( TraceData.Identify(this), TraceData.Identify(value))); } } catch (FormatException) { // formatting didn't work value = DependencyProperty.UnsetValue; if (isExtendedTraceEnabled) { TraceData.Trace(TraceEventType.Warning, TraceData.FormattingFailed( TraceData.Identify(this), EffectiveStringFormat)); } } } Array.Clear(_tempValues, 0, _tempValues.Length); // the special value DoNothing means no error, but no data transfer if (value == Binding.DoNothing) goto Done; // ultimately, TargetNullValue should get assigned implicitly, // even if the user doesn't declare it. We can't do this yet because // of back-compat. I wrote it both ways, and #if'd out the breaking // change. #if TargetNullValueBC //BreakingChange if (IsNullValue(value)) #else if (EffectiveTargetNullValue != DependencyProperty.UnsetValue && IsNullValue(value)) #endif { value = EffectiveTargetNullValue; if (isExtendedTraceEnabled) { TraceData.Trace(TraceEventType.Warning, TraceData.NullConverter( TraceData.Identify(this), TraceData.Identify(value))); } } // if the value isn't acceptable to the target property, don't use it if (value != DependencyProperty.UnsetValue && !TargetProperty.IsValidValue(value)) { if (TraceData.IsEnabled) { TraceData.Trace(TraceLevel, TraceData.BadValueAtTransfer, value, this); } if (isExtendedTraceEnabled) { TraceData.Trace(TraceEventType.Warning, TraceData.BadValueAtTransferExtended( TraceData.Identify(this), TraceData.Identify(value))); } value = DependencyProperty.UnsetValue; } // if we can't obtain a value, try the fallback value. if (value == DependencyProperty.UnsetValue) { value = UseFallbackValue(); if (isExtendedTraceEnabled) { TraceData.Trace(TraceEventType.Warning, TraceData.UseFallback( TraceData.Identify(this), TraceData.Identify(value))); } } if (isExtendedTraceEnabled) { TraceData.Trace(TraceEventType.Warning, TraceData.TransferValue( TraceData.Identify(this), TraceData.Identify(value))); } // if this is a re-transfer after a source update and the value // hasn't changed, don't do any more work. if (IsInUpdate && Object.Equals(value, Value)) { goto Done; } // update the cached value ChangeValue(value, true); Invalidate(false); OnTargetUpdated(); Validation.ClearInvalid(this); Done: IsInTransfer = false; } void OnTargetUpdated() { if (NotifyOnTargetUpdated) { DependencyObject target = TargetElement; if (target != null) { // while attaching a normal (not style-defined) BindingExpression, // we must defer raising the event until after the // property has been invalidated, so that the event handler // gets the right value if it asks (bug 1036862) if (IsAttaching && this == target.ReadLocalValue(TargetProperty)) { Engine.AddTask(this, TaskOps.RaiseTargetUpdatedEvent); } else { BindingExpression.OnTargetUpdated(target, TargetProperty); } } } } void OnSourceUpdated() { if (NotifyOnSourceUpdated) { DependencyObject target = TargetElement; if (target != null) { BindingExpression.OnSourceUpdated(target, TargetProperty); } } } // transfer a value from the target to the source internal override void Update(bool synchronous) { // various reasons not to update: if ( !NeedsUpdate // nothing to do || !IsReflective // no update desired || IsInTransfer // in a transfer || Status == BindingStatus.Unattached // not ready yet ) return; if (synchronous) { UpdateValue(); } else { Engine.AddTask(this, TaskOps.UpdateValue); } } #endregion Value //------------------------------------------------------ // // Private Fields // //----------------------------------------------------- Collection_list = new Collection (); IMultiValueConverter _converter; object[] _tempValues; Type[] _tempTypes; } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. //---------------------------------------------------------------------------- // // // Copyright (C) Microsoft Corporation. All rights reserved. // // // Description: Defines MultiBindingExpression object, uses a collection of BindingExpressions together. // // See spec at http://avalon/connecteddata/Specs/Data%20Binding.mht // //--------------------------------------------------------------------------- using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Globalization; using System.Windows.Threading; using System.Threading; using System.Windows.Controls; using System.Windows.Input; // FocusChangedEvent using System.Windows.Markup; using MS.Internal.Controls; // Validation using MS.Internal.KnownBoxes; using MS.Internal.Data; using MS.Utility; using MS.Internal; // Invariant.Assert namespace System.Windows.Data { ////// Describes a collection of BindingExpressions attached to a single property. /// The inner BindingExpressions contribute their values to the MultiBindingExpression, /// which combines/converts them into a resultant final value. /// In the reverse direction, the target value is tranlated to /// a set of values that are fed back into the inner BindingExpressions. /// public sealed class MultiBindingExpression: BindingExpressionBase, IDataBindEngineClient { //----------------------------------------------------- // // Constructors // //----------------------------------------------------- ///Constructor private MultiBindingExpression(MultiBinding binding, BindingExpressionBase owner) : base(binding, owner) { int count = binding.Bindings.Count; // reduce repeated allocations _tempValues = new object[count]; _tempTypes = new Type[count]; } //------------------------------------------------------ // // Interfaces // //----------------------------------------------------- void IDataBindEngineClient.TransferValue() { TransferValue(); } void IDataBindEngineClient.UpdateValue() { UpdateValue(); } bool IDataBindEngineClient.AttachToContext(bool lastChance) { AttachToContext(lastChance); return !TransferIsDeferred; } void IDataBindEngineClient.VerifySourceReference(bool lastChance) { } void IDataBindEngineClient.OnTargetUpdated() { OnTargetUpdated(); } DependencyObject IDataBindEngineClient.TargetElement { get { return TargetElement; } } //------------------------------------------------------ // // Public Properties // //------------------------------------------------------ ///Binding from which this expression was created public MultiBinding ParentMultiBinding { get { return (MultiBinding)ParentBindingBase; } } ///List of inner BindingExpression public ReadOnlyCollectionBindingExpressions { get { return new ReadOnlyCollection (MutableBindingExpressions); } } //----------------------------------------------------- // // Public Methods // //------------------------------------------------------ /// Send the current value back to the source(s) ///Does nothing when binding's Mode is not TwoWay or OneWayToSource public override void UpdateSource() { // ultimately, what would be better would be to have a status flag that // indicates that this MultiBindingExpression has been Detached, as opposed to a // MultiBindingExpression that doesn't have anything in its BindingExpressions collection // in the first place. Added to which, there should be distinct error // messages for both of these error conditions. if (MutableBindingExpressions.Count == 0) throw new InvalidOperationException(SR.Get(SRID.BindingExpressionIsDetached)); NeedsUpdate = true; // force update Update(true); // update synchronously } ///Force a data transfer from sources to target ///Will transfer data even if binding's Mode is OneWay public override void UpdateTarget() { // ultimately, what would be better would be to have a status flag that // indicates that this MultiBindingExpression has been Detached, as opposed to a // MultiBindingExpression that doesn't have anything in its BindingExpressions collection // in the first place. Added to which, there should be distinct error // messages for both of these error conditions. if (MutableBindingExpressions.Count == 0) throw new InvalidOperationException(SR.Get(SRID.BindingExpressionIsDetached)); UpdateTarget(true); } #region Expression overrides ////// Called to evaluate the Expression value /// /// DependencyObject being queried /// Property being queried ///Computed value. Unset if unavailable. internal override object GetValue(DependencyObject d, DependencyProperty dp) { return Value; } ////// Allows Expression to store set values /// /// DependencyObject being set /// Property being set /// Value being set ///true if Expression handled storing of the value internal override bool SetValue(DependencyObject d, DependencyProperty dp, object value) { if (IsReflective) { Value = value; return true; } else { // if the binding doesn't push values back to the source, allow // SetValue to overwrite the binding with a local value return false; } } #endregion Expression overrides //----------------------------------------------------- // // Internal Properties // //----------------------------------------------------- internal override bool IsParentBindingUpdateTriggerDefault { get { return (ParentMultiBinding.UpdateSourceTrigger == UpdateSourceTrigger.Default); } } //----------------------------------------------------- // // Internal Methods // //------------------------------------------------------ // Create a new BindingExpression from the given Binding description internal static MultiBindingExpression CreateBindingExpression(DependencyObject d, DependencyProperty dp, MultiBinding binding, BindingExpressionBase owner) { FrameworkPropertyMetadata fwMetaData = dp.GetMetadata(d.DependencyObjectType) as FrameworkPropertyMetadata; if ((fwMetaData != null && !fwMetaData.IsDataBindingAllowed) || dp.ReadOnly) throw new ArgumentException(SR.Get(SRID.PropertyNotBindable, dp.Name), "dp"); // create the BindingExpression MultiBindingExpression bindExpr = new MultiBindingExpression(binding, owner); bindExpr.ResolvePropertyDefaultSettings(binding.Mode, binding.UpdateSourceTrigger, fwMetaData); return bindExpr; } // Attach to things that may require tree context (parent, root, etc.) void AttachToContext(bool lastChance) { DependencyObject target = TargetElement; if (target == null) return; Debug.Assert(ParentMultiBinding.Converter != null || !String.IsNullOrEmpty(EffectiveStringFormat), "MultiBindingExpression should not exist if its bind does not have a valid converter."); bool isExtendedTraceEnabled = TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.AttachToContext); _converter = ParentMultiBinding.Converter; if (_converter == null && String.IsNullOrEmpty(EffectiveStringFormat)) { TraceData.Trace(TraceEventType.Error, TraceData.MultiBindingHasNoConverter, ParentMultiBinding); } if (isExtendedTraceEnabled) { TraceData.Trace(TraceEventType.Warning, TraceData.AttachToContext( TraceData.Identify(this), lastChance ? " (last chance)" : String.Empty)); } TransferIsDeferred = true; bool attached = true; // true if all child bindings have attached int count = MutableBindingExpressions.Count; for (int i = 0; i < count; ++i) { if (MutableBindingExpressions[i].Status == BindingStatus.Unattached) attached = false; } // if the child bindings aren't ready yet, try again later. Leave // TransferIsDeferred set, to indicate we're not ready yet. if (!attached && !lastChance) { if (isExtendedTraceEnabled) { TraceData.Trace(TraceEventType.Warning, TraceData.ChildNotAttached( TraceData.Identify(this))); } return; } // initial transfer bool initialTransferIsUpdate = IsOneWayToSource; object currentValue; if (ShouldUpdateWithCurrentValue(target, out currentValue)) { initialTransferIsUpdate = true; ChangeValue(currentValue, /*notify*/false); NeedsUpdate = true; } SetStatus(BindingStatus.Active); if (!initialTransferIsUpdate) { UpdateTarget(false); } else { UpdateValue(); } } //----------------------------------------------------- // // Public Properties // //------------------------------------------------------ ////// The ValidationError that caused this /// BindingExpression to be invalid. /// public override ValidationError ValidationError { get { ValidationError validationError = base.ValidationError; if (validationError == null) { for ( int i = 0; i < MutableBindingExpressions.Count; i++ ) { validationError = MutableBindingExpressions[i].ValidationError; if (validationError != null) break; } } return validationError; } } ////// HasError returns true if any of the ValidationRules /// of any of its inner bindings failed its validation rule /// or the Multi-/PriorityBinding itself has a failing validation rule. /// public override bool HasError { get { bool hasError = base.HasError; if (!hasError) { for ( int i = 0; i < MutableBindingExpressions.Count; i++ ) { if (MutableBindingExpressions[i].HasError) return true; } } return hasError; } } //------------------------------------------------------ // // Protected Internal Methods // //----------------------------------------------------- ////// Attach a BindingExpression to the given target (element, property) /// /// DependencyObject being set /// Property being set internal override bool AttachOverride(DependencyObject d, DependencyProperty dp) { if (!base.AttachOverride(d, dp)) return false; DependencyObject target = TargetElement; if (target == null) return false; // listen for lost focus if (IsUpdateOnLostFocus) { LostFocusEventManager.AddListener(target, this); } TransferIsDeferred = true; // Defer data transfer until after we activate all the BindingExpressions int count = ParentMultiBinding.Bindings.Count; for (int i = 0; i < count; ++i) { // ISSUE: It may be possible to have _attachedBindingExpressions be non-zero // at the end of Detach if the conditions for the increment on Attach // and the decrement on Detach are not precisely the same. AttachBindingExpression(i, false); // create new binding and have it added to end } // attach to things that need tree context. Do it synchronously // if possible, otherwise post a task. This gives the parser et al. // a chance to assemble the tree before we start walking it. AttachToContext(false /* lastChance */); if (TransferIsDeferred) { Engine.AddTask(this, TaskOps.AttachToContext); if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.AttachToContext)) { TraceData.Trace(TraceEventType.Warning, TraceData.DeferAttachToContext( TraceData.Identify(this))); } } return true; } ///sever all connections internal override void DetachOverride() { DependencyObject target = TargetElement; if (target != null && IsUpdateOnLostFocus) { LostFocusEventManager.RemoveListener(target, this); } // Theoretically, we only need to detach number of AttentiveBindingExpressions, // but we'll traverse the whole list anyway and do aggressive clean-up. int count = MutableBindingExpressions.Count; for (int i = count - 1; i >= 0; i--) { BindingExpressionBase b = MutableBindingExpressions[i]; if (b != null) { b.Detach(); MutableBindingExpressions.RemoveAt(i); } } ChangeSources(null); base.DetachOverride(); } ////// Invalidate the given child expression. /// internal override void InvalidateChild(BindingExpressionBase bindingExpression) { int index = MutableBindingExpressions.IndexOf(bindingExpression); // do a sanity check that we care about this BindingExpression if (0 <= index && IsDynamic) { NeedsDataTransfer = true; Transfer(); // this will Invalidate target property. } } ////// Change the dependency sources for the given child expression. /// internal override void ChangeSourcesForChild(BindingExpressionBase bindingExpression, WeakDependencySource[] newSources) { int index = MutableBindingExpressions.IndexOf(bindingExpression); if (index >= 0) { WeakDependencySource[] combinedSources = CombineSources(index, MutableBindingExpressions, MutableBindingExpressions.Count, newSources); ChangeSources(combinedSources); } } ////// Replace the given child expression with a new one. /// internal override void ReplaceChild(BindingExpressionBase bindingExpression) { int index = MutableBindingExpressions.IndexOf(bindingExpression); DependencyObject target = TargetElement; if (index >= 0 && target != null) { // detach and clean up the old binding bindingExpression.Detach(); // replace BindingExpression AttachBindingExpression(index, true); } } // register the leaf bindings with the binding group internal override void UpdateBindingGroup(BindingGroup bg) { for (int i=0, n=MutableBindingExpressions.Count-1; i/// Get the converted proposed value /// internal override object ConvertProposedValue(object value) { object result; bool success = ConvertProposedValueImpl(value, out result); // if the conversion failed, signal a validation error if (!success) { result = DependencyProperty.UnsetValue; ValidationError validationError = new ValidationError(ConversionValidationRule.Instance, this, SR.Get(SRID.Validation_ConversionFailed, value), null); UpdateValidationError(validationError); } return result; } private bool ConvertProposedValueImpl(object value, out object result) { DependencyObject target = TargetElement; if (target == null) { result = DependencyProperty.UnsetValue; return false; } result = GetValuesForChildBindings(value); if (result == DependencyProperty.UnsetValue) { SetStatus(BindingStatus.UpdateSourceError); return false; } object[] values = (object[])result; if (values == null) { if (TraceData.IsEnabled) { TraceData.Trace(TraceEventType.Error, TraceData.BadMultiConverterForUpdate( Converter.GetType().Name, AvTrace.ToStringHelper(value), AvTrace.TypeName(value)), this); } result = DependencyProperty.UnsetValue; return false; } if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.Update)) { for (int i=0; i /// Get the converted proposed value and inform the binding group /// internal override bool ObtainConvertedProposedValue(BindingGroup bindingGroup) { bool result = true; if (NeedsUpdate) { object value = bindingGroup.GetValue(this); if (value != DependencyProperty.UnsetValue) { object[] values; value = ConvertProposedValue(value); if (value == DependencyProperty.UnsetValue) { result = false; } else if ((values = value as object[]) != null) { for (int i=0; i /// Update the source value /// internal override object UpdateSource(object convertedValue) { if (convertedValue == DependencyProperty.UnsetValue) { SetStatus(BindingStatus.UpdateSourceError); return convertedValue; } object[] values = convertedValue as object[]; int count = MutableBindingExpressions.Count; if (values.Length < count) count = values.Length; BeginSourceUpdate(); for (int i = 0; i < count; ++i) { object value = values[i]; if (value != Binding.DoNothing) { BindingExpressionBase bindExpr = MutableBindingExpressions[i]; bindExpr.UpdateSource(value); if (bindExpr.Status == BindingStatus.UpdateSourceError) { SetStatus(BindingStatus.UpdateSourceError); } } } EndSourceUpdate(); OnSourceUpdated(); return convertedValue; } /// /// Update the source value and inform the binding group /// internal override bool UpdateSource(BindingGroup bindingGroup) { bool result = true; if (NeedsUpdate) { object value = bindingGroup.GetValue(this); UpdateSource(value); if (value == DependencyProperty.UnsetValue) { result = false; } } return result; } /// /// Store the value in the binding group /// internal override void StoreValueInBindingGroup(object value, BindingGroup bindingGroup) { bindingGroup.SetValue(this, value); object[] values = value as object[]; if (values != null) { int count = MutableBindingExpressions.Count; if (values.Length < count) count = values.Length; for (int i=0; i=0; --i) { MutableBindingExpressions[i].StoreValueInBindingGroup(DependencyProperty.UnsetValue, bindingGroup); } } } /// /// Run validation rules for the given step /// internal override bool Validate(object value, ValidationStep validationStep) { if (value == Binding.DoNothing) return true; if (value == DependencyProperty.UnsetValue) { SetStatus(BindingStatus.UpdateSourceError); return false; } // run rules attached to this multibinding bool result = base.Validate(value, validationStep); // run rules attached to the child bindings switch (validationStep) { case ValidationStep.RawProposedValue: // the child bindings don't get raw values until the Convert step break; default: object[] values = value as object[]; int count = MutableBindingExpressions.Count; if (values.Length < count) count = values.Length; for (int i=0; i /// Run validation rules for the given step, and inform the binding group /// internal override bool CheckValidationRules(BindingGroup bindingGroup, ValidationStep validationStep) { if (!NeedsValidation) return true; object value; switch (validationStep) { case ValidationStep.RawProposedValue: case ValidationStep.ConvertedProposedValue: case ValidationStep.UpdatedValue: case ValidationStep.CommittedValue: value = bindingGroup.GetValue(this); break; default: throw new InvalidOperationException(SR.Get(SRID.ValidationRule_UnknownStep, validationStep, bindingGroup)); } bool result = Validate(value, validationStep); if (result && validationStep == ValidationStep.CommittedValue) { NeedsValidation = false; } return result; } /// private Collection/// Get the proposed value(s) that would be written to the source(s), applying /// conversion and checking UI-side validation rules. /// internal override bool ValidateAndConvertProposedValue(out Collectionvalues) { Debug.Assert(NeedsValidation, "check NeedsValidation before calling this"); values = null; // validate raw proposed value object rawValue = GetRawProposedValue(); bool isValid = Validate(rawValue, ValidationStep.RawProposedValue); if (!isValid) { return false; } // apply conversion object conversionResult = GetValuesForChildBindings(rawValue); if (conversionResult == DependencyProperty.UnsetValue || conversionResult == null) { return false; } int count = MutableBindingExpressions.Count; object[] convertedValues = (object[])conversionResult; if (convertedValues.Length < count) count = convertedValues.Length; values = new Collection (); bool result = true; // validate child bindings for (int i = 0; i < count; ++i) { object value = convertedValues[i]; if (value == Binding.DoNothing) { } else if (value == DependencyProperty.UnsetValue) { // conversion failure result = false; } else { // send converted value to child binding BindingExpressionBase bindExpr = MutableBindingExpressions[i]; bindExpr.Value = value; // validate child binding if (bindExpr.NeedsValidation) { Collection childValues; bool childResult = bindExpr.ValidateAndConvertProposedValue(out childValues); // append child's values to our values if (childValues != null) { for (int k=0, n=childValues.Count; k /// expose a mutable version of the list of all BindingExpressions; /// derived internal classes need to be able to populate this list /// MutableBindingExpressions { get { return _list; } } IMultiValueConverter Converter { get { return _converter; } set { _converter = value; } } //----------------------------------------------------- // // Private Methods // //----------------------------------------------------- // Create a BindingExpression for position i BindingExpressionBase AttachBindingExpression(int i, bool replaceExisting) { DependencyObject target = TargetElement; if (target == null) return null; BindingBase binding = ParentMultiBinding.Bindings[i]; // Check if replacement bindings have the correct UpdateSourceTrigger MultiBinding.CheckTrigger(binding); BindingExpressionBase bindExpr = binding.CreateBindingExpression(target, TargetProperty, this); if (replaceExisting) // replace exisiting or add as new binding? MutableBindingExpressions[i] = bindExpr; else MutableBindingExpressions.Add(bindExpr); bindExpr.Attach(target, TargetProperty); return bindExpr; } internal override void HandlePropertyInvalidation(DependencyObject d, DependencyPropertyChangedEventArgs args) { DependencyProperty dp = args.Property; int n = MutableBindingExpressions.Count; if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.Events)) { TraceData.Trace(TraceEventType.Warning, TraceData.GotPropertyChanged( TraceData.Identify(this), TraceData.Identify(d), dp.Name)); } bool isConnected = true; TransferIsDeferred = true; for (int i = 0; i < n; ++i) { BindingExpressionBase bindExpr = MutableBindingExpressions[i]; if (bindExpr != null) { DependencySource[] sources = bindExpr.GetSources(); if (sources != null) { for (int j = 0; j < sources.Length; ++j) { DependencySource source = sources[j]; if (source.DependencyObject == d && source.DependencyProperty == dp) { bindExpr.OnPropertyInvalidation(d, args); break; } } } if (bindExpr.IsDisconnected) { isConnected = false; } } } TransferIsDeferred = false; if (isConnected) { Transfer(); // Transfer if inner BindingExpressions have called Invalidate(binding) } else { Disconnect(); } } /// /// Handle events from the centralized event table /// internal override bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e) { if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.Events)) { TraceData.Trace(TraceEventType.Warning, TraceData.GotEvent( TraceData.Identify(this), TraceData.IdentifyWeakEvent(managerType), TraceData.Identify(sender))); } if (managerType == typeof(LostFocusEventManager)) { Update(true); } else { return base.ReceiveWeakEvent(managerType, sender, e); } return true; } #region Value ///Force a data transfer from source(s) to target /// /// use true to propagate UpdateTarget call to all inner BindingExpressions; /// use false to avoid forcing data re-transfer from one-time inner BindingExpressions /// void UpdateTarget(bool includeInnerBindings) { TransferIsDeferred = true; if (includeInnerBindings) { foreach (BindingExpressionBase b in MutableBindingExpressions) { b.UpdateTarget(); } } TransferIsDeferred = false; NeedsDataTransfer = true; // force data transfer Transfer(); } // transfer a value from the source to the target void Transfer() { // required state for transfer if ( NeedsDataTransfer // Transfer is needed && Status != BindingStatus.Unattached // All bindings are attached && !TransferIsDeferred) // Not aggregating transfers { TransferValue(); } } // transfer a value from the source to the target void TransferValue() { IsInTransfer = true; NeedsDataTransfer = false; DependencyObject target = TargetElement; if (target == null) goto Done; bool isExtendedTraceEnabled = TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.Transfer); object value = DependencyProperty.UnsetValue; object preFormattedValue = _tempValues; CultureInfo culture = GetCulture(); // gather values from inner BindingExpressions int count = MutableBindingExpressions.Count; for (int i = 0; i < count; ++i) { _tempValues[i] = MutableBindingExpressions[i].GetValue(target, TargetProperty); // could pass (null, null) if (isExtendedTraceEnabled) { TraceData.Trace(TraceEventType.Warning, TraceData.GetRawValueMulti( TraceData.Identify(this), i, TraceData.Identify(_tempValues[i]))); } } // apply the converter if (Converter != null) { // MultiValueConverters are always user-defined, so don't catch exceptions (bug 992237) preFormattedValue = Converter.Convert(_tempValues, TargetProperty.PropertyType, ParentMultiBinding.ConverterParameter, culture); if (isExtendedTraceEnabled) { TraceData.Trace(TraceEventType.Warning, TraceData.UserConverter( TraceData.Identify(this), TraceData.Identify(preFormattedValue))); } } else if (EffectiveStringFormat != null) { // preFormattedValue = _tempValues; // But check for child binding conversion errors for (int i=0; i<_tempValues.Length; ++i) { if (_tempValues[i] == DependencyProperty.UnsetValue) { preFormattedValue = DependencyProperty.UnsetValue; break; } } } else // no converter (perhaps user specified it in error) { if (TraceData.IsEnabled) { TraceData.Trace(TraceEventType.Error, TraceData.MultiValueConverterMissingForTransfer, this); } goto Done; } // apply string formatting if (EffectiveStringFormat == null || preFormattedValue == Binding.DoNothing || preFormattedValue == DependencyProperty.UnsetValue) { value = preFormattedValue; } else { try { // we call String.Format either with multiple values (obtained from // the child bindings) or a single value (as produced by the converter). // The if-test is needed to avoid wrapping _tempValues inside another object[]. if (preFormattedValue == _tempValues) { value = String.Format(culture, EffectiveStringFormat, _tempValues); } else { value = String.Format(culture, EffectiveStringFormat, preFormattedValue); } if (isExtendedTraceEnabled) { TraceData.Trace(TraceEventType.Warning, TraceData.FormattedValue( TraceData.Identify(this), TraceData.Identify(value))); } } catch (FormatException) { // formatting didn't work value = DependencyProperty.UnsetValue; if (isExtendedTraceEnabled) { TraceData.Trace(TraceEventType.Warning, TraceData.FormattingFailed( TraceData.Identify(this), EffectiveStringFormat)); } } } Array.Clear(_tempValues, 0, _tempValues.Length); // the special value DoNothing means no error, but no data transfer if (value == Binding.DoNothing) goto Done; // ultimately, TargetNullValue should get assigned implicitly, // even if the user doesn't declare it. We can't do this yet because // of back-compat. I wrote it both ways, and #if'd out the breaking // change. #if TargetNullValueBC //BreakingChange if (IsNullValue(value)) #else if (EffectiveTargetNullValue != DependencyProperty.UnsetValue && IsNullValue(value)) #endif { value = EffectiveTargetNullValue; if (isExtendedTraceEnabled) { TraceData.Trace(TraceEventType.Warning, TraceData.NullConverter( TraceData.Identify(this), TraceData.Identify(value))); } } // if the value isn't acceptable to the target property, don't use it if (value != DependencyProperty.UnsetValue && !TargetProperty.IsValidValue(value)) { if (TraceData.IsEnabled) { TraceData.Trace(TraceLevel, TraceData.BadValueAtTransfer, value, this); } if (isExtendedTraceEnabled) { TraceData.Trace(TraceEventType.Warning, TraceData.BadValueAtTransferExtended( TraceData.Identify(this), TraceData.Identify(value))); } value = DependencyProperty.UnsetValue; } // if we can't obtain a value, try the fallback value. if (value == DependencyProperty.UnsetValue) { value = UseFallbackValue(); if (isExtendedTraceEnabled) { TraceData.Trace(TraceEventType.Warning, TraceData.UseFallback( TraceData.Identify(this), TraceData.Identify(value))); } } if (isExtendedTraceEnabled) { TraceData.Trace(TraceEventType.Warning, TraceData.TransferValue( TraceData.Identify(this), TraceData.Identify(value))); } // if this is a re-transfer after a source update and the value // hasn't changed, don't do any more work. if (IsInUpdate && Object.Equals(value, Value)) { goto Done; } // update the cached value ChangeValue(value, true); Invalidate(false); OnTargetUpdated(); Validation.ClearInvalid(this); Done: IsInTransfer = false; } void OnTargetUpdated() { if (NotifyOnTargetUpdated) { DependencyObject target = TargetElement; if (target != null) { // while attaching a normal (not style-defined) BindingExpression, // we must defer raising the event until after the // property has been invalidated, so that the event handler // gets the right value if it asks (bug 1036862) if (IsAttaching && this == target.ReadLocalValue(TargetProperty)) { Engine.AddTask(this, TaskOps.RaiseTargetUpdatedEvent); } else { BindingExpression.OnTargetUpdated(target, TargetProperty); } } } } void OnSourceUpdated() { if (NotifyOnSourceUpdated) { DependencyObject target = TargetElement; if (target != null) { BindingExpression.OnSourceUpdated(target, TargetProperty); } } } // transfer a value from the target to the source internal override void Update(bool synchronous) { // various reasons not to update: if ( !NeedsUpdate // nothing to do || !IsReflective // no update desired || IsInTransfer // in a transfer || Status == BindingStatus.Unattached // not ready yet ) return; if (synchronous) { UpdateValue(); } else { Engine.AddTask(this, TaskOps.UpdateValue); } } #endregion Value //------------------------------------------------------ // // Private Fields // //----------------------------------------------------- Collection_list = new Collection (); IMultiValueConverter _converter; object[] _tempValues; Type[] _tempTypes; } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- ConfigurationStrings.cs
- BindingSourceDesigner.cs
- StylusOverProperty.cs
- SettingsBindableAttribute.cs
- MetadataArtifactLoaderFile.cs
- QueryCacheKey.cs
- RowBinding.cs
- MenuEventArgs.cs
- TraceContext.cs
- GridItem.cs
- NativeMethods.cs
- ElementHostPropertyMap.cs
- CapabilitiesUse.cs
- WindowsToolbarItemAsMenuItem.cs
- Evaluator.cs
- DockPattern.cs
- Visual3D.cs
- MailSettingsSection.cs
- PermissionRequestEvidence.cs
- SecurityChannelFactory.cs
- InheritanceContextHelper.cs
- MembershipUser.cs
- DomainUpDown.cs
- GetPageCompletedEventArgs.cs
- StatusBarAutomationPeer.cs
- WebProxyScriptElement.cs
- MailMessage.cs
- UriSchemeKeyedCollection.cs
- CodeCompiler.cs
- AuthenticatingEventArgs.cs
- EDesignUtil.cs
- ReaderContextStackData.cs
- DomainConstraint.cs
- XmlMapping.cs
- BufferedReadStream.cs
- AbandonedMutexException.cs
- KeyboardEventArgs.cs
- TextEditorLists.cs
- NamespaceList.cs
- TreeNodeEventArgs.cs
- MethodRental.cs
- IntegerValidatorAttribute.cs
- XmlWriterSettings.cs
- SignedXmlDebugLog.cs
- Pkcs9Attribute.cs
- LocalizeDesigner.cs
- XNodeValidator.cs
- ThaiBuddhistCalendar.cs
- WebSysDescriptionAttribute.cs
- XmlEntityReference.cs
- ToolStripDesignerAvailabilityAttribute.cs
- DataColumnMappingCollection.cs
- MediaTimeline.cs
- CheckPair.cs
- MeshGeometry3D.cs
- Trace.cs
- TreeNodeSelectionProcessor.cs
- StringPropertyBuilder.cs
- TransformationRules.cs
- CqlLexerHelpers.cs
- SelectionUIHandler.cs
- PropertyPathConverter.cs
- OletxVolatileEnlistment.cs
- MessageEncodingBindingElement.cs
- UdpDuplexChannel.cs
- CodeConstructor.cs
- DivideByZeroException.cs
- Accessible.cs
- ToolStripContainerDesigner.cs
- DrawingImage.cs
- LogSwitch.cs
- RegexGroupCollection.cs
- AuthStoreRoleProvider.cs
- EmbossBitmapEffect.cs
- TryExpression.cs
- Char.cs
- Query.cs
- ConstructorBuilder.cs
- HwndTarget.cs
- LicFileLicenseProvider.cs
- StylusPoint.cs
- GeneralTransform.cs
- ToolboxComponentsCreatingEventArgs.cs
- OrderPreservingPipeliningSpoolingTask.cs
- InputScope.cs
- SourceFileBuildProvider.cs
- SrgsGrammarCompiler.cs
- TextEditorCharacters.cs
- HuffCodec.cs
- RegexStringValidatorAttribute.cs
- ToolBarOverflowPanel.cs
- ProtocolViolationException.cs
- coordinatorscratchpad.cs
- _AutoWebProxyScriptHelper.cs
- TextBlockAutomationPeer.cs
- ClientConfigurationHost.cs
- Range.cs
- pingexception.cs
- ToolboxCategory.cs
- RenderingBiasValidation.cs