MultiBindingExpression.cs source code in C# .NET

Source code for the .NET framework in C#

                        

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 ReadOnlyCollection   BindingExpressions 
    { 
        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;
    } 

    ///  
    /// 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 Collection values)
    {
        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
    ///  
    private Collection 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 ReadOnlyCollection   BindingExpressions 
    { 
        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;
    } 

    ///  
    /// 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 Collection values)
    {
        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
    ///  
    private Collection 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

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK