Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / wpf / src / Framework / System / Windows / Data / MultiBindingExpression.cs / 1 / 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.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; } } ////// Notification that a Dependent that this Expression established has /// been invalidated as a result of a Source invalidation /// /// DependencyObject that was invalidated /// Changed event args for the property that was invalidated internal override void OnPropertyInvalidation(DependencyObject d, DependencyPropertyChangedEventArgs args) { // If this method is changed to use d or dp directly, uncomment these lines //if (d == null) // throw new ArgumentNullException(d); //if (dp == null) // throw new ArgumentNullException(dp); // if the notification arrived on the right Dispatcher, handle it now. if (Dispatcher.Thread == Thread.CurrentThread) { HandlePropertyInvalidation(d, args); } else // Otherwise, marshal it to the right Dispatcher. { Dispatcher.BeginInvoke( DispatcherPriority.DataBind, new DispatcherOperationCallback(HandlePropertyInvalidationOperation), new object[]{d, args}); } } #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); // prevert transfers and updates until multiBindingExpression is ready bindExpr.IsInTransfer = true; bindExpr.IsInUpdate = true; 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 TransferIsDeferred = false; EndSourceUpdate(); // now the MultiBindingExpression is ready for Transfers SetStatus(BindingStatus.Active); if (!IsOneWayToSource) { UpdateTarget(false); } else { IsInTransfer = false; // set at creation time 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 void AttachOverride(DependencyObject d, DependencyProperty dp) { base.AttachOverride(d, dp); DependencyObject target = TargetElement; if (target == null) return; // 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))); } } } ///sever all connections internal override void DetachOverride() { if (IsUpdateOnLostFocus) { LostFocusEventManager.RemoveListener(TargetElement, 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) { DependencyObject target = TargetElement; if (target == null) return DependencyProperty.UnsetValue; if (Converter == null) { if (TraceData.IsEnabled) { TraceData.Trace(TraceEventType.Error, TraceData.MultiValueConverterMissingForUpdate, this); } SetStatus(BindingStatus.UpdateSourceError); return DependencyProperty.UnsetValue; } CultureInfo culture = GetCulture(); int count = MutableBindingExpressions.Count; object[] values = null; Exception exception = null; for (int i = 0; i < count; ++i) { BindingExpressionBase bindExpr = MutableBindingExpressions[i]; BindingExpression be = bindExpr as BindingExpression; if (be != null && be.UseDefaultValueConverter) _tempTypes[i] = be.ConverterSourceType; else _tempTypes[i] = TargetProperty.PropertyType; } // MultiValueConverters are always user-defined, so don't catch exceptions (bug 992237) values = Converter.ConvertBack(value, _tempTypes, ParentMultiBinding.ConverterParameter, culture); if (values == null) { if (TraceData.IsEnabled) { TraceData.Trace(TraceEventType.Error, TraceData.BadMultiConverterForUpdate( Converter.GetType().Name, AvTrace.ToStringHelper(value), AvTrace.TypeName(value)), this, exception); } return DependencyProperty.UnsetValue; } if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.Update)) { for (int i=0; i /// Get the converted proposed value and inform the binding group /// internal override void ObtainConvertedProposedValue(BindingGroup bindingGroup) { if (NeedsUpdate) { object value = bindingGroup.GetValue(this); if (value != DependencyProperty.UnsetValue) { value = ConvertProposedValue(value); } StoreValueInBindingGroup(value, bindingGroup); } else { bindingGroup.UseSourceValue(this); } } /// /// Update the source value /// internal override object UpdateSource(object convertedValue) { if (convertedValue == DependencyProperty.UnsetValue) 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 && value != DependencyProperty.UnsetValue) { BindingExpressionBase bindExpr = MutableBindingExpressions[i]; bindExpr.UpdateSource(value); } } EndSourceUpdate(); OnSourceUpdated(); return convertedValue; } /// /// Update the source value and inform the binding group /// internal override void UpdateSource(BindingGroup bindingGroup) { if (NeedsUpdate) { object value = bindingGroup.GetValue(this); UpdateSource(value); } } /// /// 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 || value == DependencyProperty.UnsetValue) return true; // 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 Properties // //----------------------------------------------------- /// /// expose a mutable version of the list of all BindingExpressions; /// derived internal classes need to be able to populate this list /// private CollectionMutableBindingExpressions { 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; } private object HandlePropertyInvalidationOperation(object o) { // This is the case where the source of the Binding belonged to a different Dispatcher // than the target. For this scenario the source marshals off the invalidation information // onto the target's Dispatcher queue. This is where we unpack the marshalled information // to fire the invalidation on the target object. object[] args = (object[])o; HandlePropertyInvalidation((DependencyObject)args[0], (DependencyPropertyChangedEventArgs)args[1]); return null; } private 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)); } 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; } } } } } TransferIsDeferred = false; Transfer(); // Transfer if inner BindingExpressions have called Invalidate(binding) } // invalidate the target property void Invalidate() { // don't invalidate during Attach. The property engine does it // already, and it would interfere with the on-demand activation // of style-defined BindingExpressions. if (IsAttaching) return; DependencyObject target = TargetElement; if (target != null) { // recompute expression target.InvalidateProperty(TargetProperty); } } /// /// 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 && !IsInUpdate // Not in an update && !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; } 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))); } // update the cached value ChangeValue(value, true); Invalidate(); 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 ) 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.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; } } ////// Notification that a Dependent that this Expression established has /// been invalidated as a result of a Source invalidation /// /// DependencyObject that was invalidated /// Changed event args for the property that was invalidated internal override void OnPropertyInvalidation(DependencyObject d, DependencyPropertyChangedEventArgs args) { // If this method is changed to use d or dp directly, uncomment these lines //if (d == null) // throw new ArgumentNullException(d); //if (dp == null) // throw new ArgumentNullException(dp); // if the notification arrived on the right Dispatcher, handle it now. if (Dispatcher.Thread == Thread.CurrentThread) { HandlePropertyInvalidation(d, args); } else // Otherwise, marshal it to the right Dispatcher. { Dispatcher.BeginInvoke( DispatcherPriority.DataBind, new DispatcherOperationCallback(HandlePropertyInvalidationOperation), new object[]{d, args}); } } #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); // prevert transfers and updates until multiBindingExpression is ready bindExpr.IsInTransfer = true; bindExpr.IsInUpdate = true; 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 TransferIsDeferred = false; EndSourceUpdate(); // now the MultiBindingExpression is ready for Transfers SetStatus(BindingStatus.Active); if (!IsOneWayToSource) { UpdateTarget(false); } else { IsInTransfer = false; // set at creation time 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 void AttachOverride(DependencyObject d, DependencyProperty dp) { base.AttachOverride(d, dp); DependencyObject target = TargetElement; if (target == null) return; // 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))); } } } ///sever all connections internal override void DetachOverride() { if (IsUpdateOnLostFocus) { LostFocusEventManager.RemoveListener(TargetElement, 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) { DependencyObject target = TargetElement; if (target == null) return DependencyProperty.UnsetValue; if (Converter == null) { if (TraceData.IsEnabled) { TraceData.Trace(TraceEventType.Error, TraceData.MultiValueConverterMissingForUpdate, this); } SetStatus(BindingStatus.UpdateSourceError); return DependencyProperty.UnsetValue; } CultureInfo culture = GetCulture(); int count = MutableBindingExpressions.Count; object[] values = null; Exception exception = null; for (int i = 0; i < count; ++i) { BindingExpressionBase bindExpr = MutableBindingExpressions[i]; BindingExpression be = bindExpr as BindingExpression; if (be != null && be.UseDefaultValueConverter) _tempTypes[i] = be.ConverterSourceType; else _tempTypes[i] = TargetProperty.PropertyType; } // MultiValueConverters are always user-defined, so don't catch exceptions (bug 992237) values = Converter.ConvertBack(value, _tempTypes, ParentMultiBinding.ConverterParameter, culture); if (values == null) { if (TraceData.IsEnabled) { TraceData.Trace(TraceEventType.Error, TraceData.BadMultiConverterForUpdate( Converter.GetType().Name, AvTrace.ToStringHelper(value), AvTrace.TypeName(value)), this, exception); } return DependencyProperty.UnsetValue; } if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.Update)) { for (int i=0; i /// Get the converted proposed value and inform the binding group /// internal override void ObtainConvertedProposedValue(BindingGroup bindingGroup) { if (NeedsUpdate) { object value = bindingGroup.GetValue(this); if (value != DependencyProperty.UnsetValue) { value = ConvertProposedValue(value); } StoreValueInBindingGroup(value, bindingGroup); } else { bindingGroup.UseSourceValue(this); } } /// /// Update the source value /// internal override object UpdateSource(object convertedValue) { if (convertedValue == DependencyProperty.UnsetValue) 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 && value != DependencyProperty.UnsetValue) { BindingExpressionBase bindExpr = MutableBindingExpressions[i]; bindExpr.UpdateSource(value); } } EndSourceUpdate(); OnSourceUpdated(); return convertedValue; } /// /// Update the source value and inform the binding group /// internal override void UpdateSource(BindingGroup bindingGroup) { if (NeedsUpdate) { object value = bindingGroup.GetValue(this); UpdateSource(value); } } /// /// 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 || value == DependencyProperty.UnsetValue) return true; // 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 Properties // //----------------------------------------------------- /// /// expose a mutable version of the list of all BindingExpressions; /// derived internal classes need to be able to populate this list /// private CollectionMutableBindingExpressions { 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; } private object HandlePropertyInvalidationOperation(object o) { // This is the case where the source of the Binding belonged to a different Dispatcher // than the target. For this scenario the source marshals off the invalidation information // onto the target's Dispatcher queue. This is where we unpack the marshalled information // to fire the invalidation on the target object. object[] args = (object[])o; HandlePropertyInvalidation((DependencyObject)args[0], (DependencyPropertyChangedEventArgs)args[1]); return null; } private 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)); } 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; } } } } } TransferIsDeferred = false; Transfer(); // Transfer if inner BindingExpressions have called Invalidate(binding) } // invalidate the target property void Invalidate() { // don't invalidate during Attach. The property engine does it // already, and it would interfere with the on-demand activation // of style-defined BindingExpressions. if (IsAttaching) return; DependencyObject target = TargetElement; if (target != null) { // recompute expression target.InvalidateProperty(TargetProperty); } } /// /// 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 && !IsInUpdate // Not in an update && !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; } 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))); } // update the cached value ChangeValue(value, true); Invalidate(); 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 ) 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
- ElementHostPropertyMap.cs
- BuilderInfo.cs
- RsaSecurityToken.cs
- WebBrowserProgressChangedEventHandler.cs
- Control.cs
- BrowserCapabilitiesFactory.cs
- MaskedTextBox.cs
- StackOverflowException.cs
- IteratorFilter.cs
- MediaTimeline.cs
- TextLineBreak.cs
- ButtonBase.cs
- TimeSpanFormat.cs
- DataAdapter.cs
- XmlIlVisitor.cs
- VariableAction.cs
- ExtendedPropertyDescriptor.cs
- QueryReaderSettings.cs
- SchemaComplexType.cs
- DesignerSerializationManager.cs
- ObjectHandle.cs
- ItemsPanelTemplate.cs
- DragDrop.cs
- SolidBrush.cs
- TemplateBaseAction.cs
- ServiceBuildProvider.cs
- MessageSmuggler.cs
- SimpleWebHandlerParser.cs
- HyperLinkColumn.cs
- RequestCache.cs
- RightsManagementEncryptedStream.cs
- Vector3DValueSerializer.cs
- ExecutedRoutedEventArgs.cs
- SmtpReplyReader.cs
- XmlUtf8RawTextWriter.cs
- DeflateStream.cs
- FastEncoderStatics.cs
- ComponentChangingEvent.cs
- xamlnodes.cs
- CompilerError.cs
- TypeLoadException.cs
- CheckBoxList.cs
- WebConvert.cs
- ParserStack.cs
- CodeTypeMemberCollection.cs
- ErrorWrapper.cs
- FixedSOMTable.cs
- RightsManagementEncryptionTransform.cs
- TextRenderer.cs
- RelationshipDetailsRow.cs
- StringCollection.cs
- safemediahandle.cs
- TreeViewItem.cs
- ButtonChrome.cs
- GreenMethods.cs
- UmAlQuraCalendar.cs
- SubpageParaClient.cs
- ToolBarButtonDesigner.cs
- NumericPagerField.cs
- MemberDescriptor.cs
- IgnoreSection.cs
- Path.cs
- DesigntimeLicenseContext.cs
- ZipPackagePart.cs
- DataKey.cs
- DynamicDiscoveryDocument.cs
- HierarchicalDataBoundControlAdapter.cs
- SignerInfo.cs
- SoapAttributeAttribute.cs
- BulletedList.cs
- PeerTransportElement.cs
- SurrogateEncoder.cs
- XomlCompiler.cs
- CacheMemory.cs
- HttpListenerRequest.cs
- Paragraph.cs
- XmlSchemaSubstitutionGroup.cs
- EntityParameterCollection.cs
- RSAPKCS1KeyExchangeDeformatter.cs
- RIPEMD160.cs
- TabControlAutomationPeer.cs
- ResizeGrip.cs
- Blend.cs
- SerializableTypeCodeDomSerializer.cs
- MissingSatelliteAssemblyException.cs
- PixelShader.cs
- SectionInput.cs
- SqlNotificationEventArgs.cs
- SignatureConfirmationElement.cs
- securestring.cs
- Graph.cs
- TypeReference.cs
- WhitespaceRule.cs
- XmlMembersMapping.cs
- ToolbarAUtomationPeer.cs
- RoutedEventValueSerializer.cs
- DragCompletedEventArgs.cs
- TagNameToTypeMapper.cs
- serverconfig.cs
- KeyInstance.cs