MultiBindingExpression.cs source code in C# .NET

Source code for the .NET framework in C#

                        

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 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;
            } 
    }
 
    ///  
    ///     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 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;
    } 

    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 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;
            } 
    }
 
    ///  
    ///     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 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;
    } 

    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

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