Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Framework / System / Windows / Data / BindingExpressionBase.cs / 4 / BindingExpressionBase.cs
//----------------------------------------------------------------------------
//
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
//
// Description: Defines BindingExpressionBase object,
// base class for BindingExpression, PriorityBindingExpression,
// and MultiBindingExpression.
//
// See spec at http://avalon/connecteddata/Specs/Data%20Binding.mht
//
//---------------------------------------------------------------------------
// build with this symbol defined to catch errors about not using
// BindingExpression.GetReference correctly
//#define USE_ITEM_REFERENCE
using System;
using System.Collections.ObjectModel; // Collection
using System.ComponentModel; // TypeConverter
using System.Diagnostics; // StackTrace
using System.Globalization; // CultureInfo
using System.Windows; // FrameworkElement
using System.Windows.Controls; // Validation
using System.Windows.Markup; // XmlLanguage
using System.Windows.Threading; // Dispatcher
using MS.Internal; // Invariant.Assert
using MS.Internal.Controls; // ValidationErrorCollection
using MS.Internal.Data; // DataBindEngine
using MS.Internal.KnownBoxes; // BooleanBoxes
using MS.Internal.Utility; // TraceLog
namespace System.Windows.Data
{
///
/// Base class for Binding Expressions.
///
public abstract class BindingExpressionBase : Expression, IWeakEventListener
{
// Flags indicating run-time properties of a BindingExpression
[Flags]
internal enum BindingFlags
{
// names used by Binding
OneWay = PrivateFlags.iSourceToTarget,
TwoWay = PrivateFlags.iSourceToTarget | PrivateFlags.iTargetToSource,
OneWayToSource = PrivateFlags.iTargetToSource,
OneTime = 0,
PropDefault = PrivateFlags.iPropDefault,
NotifyOnTargetUpdated = PrivateFlags.iNotifyOnTargetUpdated,
NotifyOnSourceUpdated = PrivateFlags.iNotifyOnSourceUpdated,
NotifyOnValidationError = PrivateFlags.iNotifyOnValidationError,
UpdateOnPropertyChanged = 0,
UpdateOnLostFocus = PrivateFlags.iUpdateOnLostFocus,
UpdateExplicitly = PrivateFlags.iUpdateExplicitly,
UpdateDefault = PrivateFlags.iUpdateDefault,
PathGeneratedInternally = PrivateFlags.iPathGeneratedInternally,
ValidatesOnExceptions = PrivateFlags.iValidatesOnExceptions,
ValidatesOnDataErrors = PrivateFlags.iValidatesOnDataErrors,
Default = PropDefault | UpdateDefault,
/// Error value, returned by FlagsFrom to indicate faulty input
IllegalInput = PrivateFlags.iIllegalInput,
PropagationMask = OneWay | TwoWay | OneWayToSource | OneTime | PropDefault,
UpdateMask = UpdateOnPropertyChanged | UpdateOnLostFocus | UpdateExplicitly | UpdateDefault,
}
[Flags]
private enum PrivateFlags
{
// internal use
iSourceToTarget = 0x00000001,
iTargetToSource = 0x00000002,
iPropDefault = 0x00000004,
iNotifyOnTargetUpdated = 0x00000008,
iDefaultValueConverter = 0x00000010,
iInTransfer = 0x00000020,
iInUpdate = 0x00000040,
iTransferPending = 0x00000080,
iNeedDataTransfer = 0x00000100,
iTransferDeferred = 0x00000200, // used by MultiBindingExpression
iUpdateOnLostFocus = 0x00000400,
iUpdateExplicitly = 0x00000800,
iUpdateDefault = iUpdateExplicitly | iUpdateOnLostFocus,
iNeedUpdate = 0x00001000,
iPathGeneratedInternally = 0x00002000,
iUsingMentor = 0x00004000,
iResolveNamesInTemplate = 0x00008000,
iDetaching = 0x00010000,
iNeedsCollectionView = 0x00020000,
iInPriorityBindingExpression= 0x00040000,
iInMultiBindingExpression = 0x00080000,
iUsingFallbackValue = 0x00100000,
iNotifyOnValidationError = 0x00200000,
iAttaching = 0x00400000,
iNotifyOnSourceUpdated = 0x00800000,
iValidatesOnExceptions = 0x01000000,
iValidatesOnDataErrors = 0x02000000,
iIllegalInput = 0x04000000,
iNeedsValidation = 0x08000000,
iPropagationMask = iSourceToTarget | iTargetToSource | iPropDefault,
iUpdateMask = iUpdateOnLostFocus | iUpdateExplicitly,
}
//-----------------------------------------------------
//
// Constructors
//
//-----------------------------------------------------
/// Constructor
internal BindingExpressionBase(BindingBase binding, BindingExpressionBase parent) : base(ExpressionMode.SupportsUnboundSources)
{
if (binding == null)
throw new ArgumentNullException("binding");
_binding = binding;
_parentBindingExpression = parent;
_flags = (PrivateFlags)binding.Flags;
if (parent != null)
{
ResolveNamesInTemplate = parent.ResolveNamesInTemplate;
Type type = parent.GetType();
if (type == typeof(MultiBindingExpression))
ChangeFlag(PrivateFlags.iInMultiBindingExpression, true);
else if (type == typeof(PriorityBindingExpression))
ChangeFlag(PrivateFlags.iInPriorityBindingExpression, true);
}
// initialize tracing information
PresentationTraceLevel traceLevel = PresentationTraceSources.GetTraceLevel(binding);
if (traceLevel > 0)
{
// copy TraceLevel from parent BindingBase - it can be changed later
PresentationTraceSources.SetTraceLevel(this, traceLevel);
}
if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.CreateExpression))
{
if (parent == null)
{
TraceData.Trace(TraceEventType.Warning,
TraceData.CreatedExpression(
TraceData.Identify(this),
TraceData.Identify(binding)));
}
else
{
TraceData.Trace(TraceEventType.Warning,
TraceData.CreatedExpressionInParent(
TraceData.Identify(this),
TraceData.Identify(binding),
TraceData.Identify(parent)));
}
}
if (LookupValidationRule(typeof(ExceptionValidationRule)) != null)
{
ChangeFlag(PrivateFlags.iValidatesOnExceptions, true);
}
if (LookupValidationRule(typeof(DataErrorValidationRule)) != null)
{
ChangeFlag(PrivateFlags.iValidatesOnDataErrors, true);
}
}
//------------------------------------------------------
//
// Public Properties
//
//-----------------------------------------------------
/// Binding from which this expression was created
public BindingBase ParentBindingBase { get { return _binding; } }
/// Status of the BindingExpression
public BindingStatus Status { get { return _status; } }
///
/// The ValidationError that caused this
/// BindingExpression to be invalid.
///
public virtual ValidationError ValidationError
{
get
{
return _validationError;
}
}
///
/// HasError returns true if any of the ValidationRules
/// in the ParentBinding failed its validation rule.
///
public virtual bool HasError
{
get
{
return _validationError != null;
}
}
//------------------------------------------------------
//
// Public Methods
//
//------------------------------------------------------
/// Force a data transfer from source to target
public virtual void UpdateTarget()
{
}
/// Send the current value back to the source
/// Does nothing when binding's Mode is not TwoWay or OneWayToSource
public virtual void UpdateSource()
{
}
#region Expression overrides
///
/// Notification that the Expression has been set as a property's value
///
///
/// Subclasses should not override OnAttach(), but must override Attach()
///
/// DependencyObject being set
/// Property being set
internal sealed override void OnAttach(DependencyObject d, DependencyProperty dp)
{
if (d == null)
throw new ArgumentNullException("d");
if (dp == null)
throw new ArgumentNullException("dp");
Attach(d, dp);
}
///
/// Notification that the Expression has been removed as a property's value
///
///
/// Subclasses should not override OnDetach(), but must override Detach()
///
/// DependencyObject being cleared
/// Property being cleared
internal sealed override void OnDetach(DependencyObject d, DependencyProperty dp)
{
Detach();
}
///
/// List of sources of the Expression
///
/// Sources list
internal override DependencySource[] GetSources()
{
int j, k;
int n = (_sources != null) ? _sources.Length : 0;
if (n == 0)
return null;
DependencySource[] result = new DependencySource[n];
// convert the weak references into strong ones
for (j=0, k=0; k
/// Handle events from the centralized event table
///
bool IWeakEventListener.ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
{
return ReceiveWeakEvent(managerType, sender, e);
}
#region BindingExpressions with no target DP
//-----------------------------------------------------
//
// API for BindingExpressions with no target DP (task 20769)
// This is internal for now, until we review whether we wish to
// make it public
//
//------------------------------------------------------
/// Create an untargeted BindingExpression
internal static BindingExpressionBase CreateUntargetedBindingExpression(DependencyObject d, BindingBase binding)
{
return binding.CreateBindingExpression(d, NoTargetProperty);
}
/// Attach the BindingExpression to its target element
///
/// This method must be called once during the initialization.
///
/// The target element
internal void Attach(DependencyObject d)
{
Attach(d, NoTargetProperty);
}
/// This event is raised when the BindingExpression's value changes
internal event EventHandler ValueChanged;
/* The following APIs are also needed for untargeted bindings, but they already exist
for other reasons.
/// The current value of the BindingExpression
internal object Value { get; set; }
/// Activate the BindingExpression, using the given item as its root item.
internal void Activate(object item) {}
/// Deactivate the BindingExpression.
internal void Deactivate() {}
/// Detach the BindingExpression from its target element
///
/// This method must be called once when the BindingExpression is no longer needed.
///
internal void Detach() {}
*/
#endregion BindingExpressions with no target DP
//-----------------------------------------------------
//
// Protected Properties
//
//-----------------------------------------------------
/// True if this binding expression is attaching
internal bool IsAttaching
{
get { return TestFlag(PrivateFlags.iAttaching); }
set { ChangeFlag(PrivateFlags.iAttaching, value); }
}
/// True if this binding expression is detaching
internal bool IsDetaching
{
get { return TestFlag(PrivateFlags.iDetaching); }
set { ChangeFlag(PrivateFlags.iDetaching, value); }
}
/// True if this binding expression updates the target
internal bool IsDynamic
{
get
{
return ( TestFlag(PrivateFlags.iSourceToTarget)
&& (!IsInMultiBindingExpression || ParentMultiBindingExpression.IsDynamic));
}
}
/// True if this binding expression updates the source
internal bool IsReflective
{
get
{
return ( TestFlag(PrivateFlags.iTargetToSource)
&& (!IsInMultiBindingExpression || ParentMultiBindingExpression.IsReflective));
}
set { ChangeFlag(PrivateFlags.iTargetToSource, value); }
}
/// True if this binding expression uses a default ValueConverter
internal bool UseDefaultValueConverter
{
get { return TestFlag(PrivateFlags.iDefaultValueConverter); }
set { ChangeFlag(PrivateFlags.iDefaultValueConverter, value); }
}
/// True if this binding expression is OneWayToSource
internal bool IsOneWayToSource
{
get { return (_flags & PrivateFlags.iPropagationMask) == PrivateFlags.iTargetToSource; }
}
/// True if this binding expression updates on PropertyChanged
internal bool IsUpdateOnPropertyChanged
{
get { return (_flags & PrivateFlags.iUpdateMask) == 0; }
}
/// True if this binding expression updates on LostFocus
internal bool IsUpdateOnLostFocus
{
get { return TestFlag(PrivateFlags.iUpdateOnLostFocus); }
}
/// True if this binding expression has a pending target update
internal bool IsTransferPending
{
get { return TestFlag(PrivateFlags.iTransferPending); }
set { ChangeFlag(PrivateFlags.iTransferPending, value); }
}
/// True if this binding expression is deferring a target update
internal bool TransferIsDeferred
{
get { return TestFlag(PrivateFlags.iTransferDeferred); }
set { ChangeFlag(PrivateFlags.iTransferDeferred, value); }
}
/// True if this binding expression is updating the target
internal bool IsInTransfer
{
get { return TestFlag(PrivateFlags.iInTransfer); }
set { ChangeFlag(PrivateFlags.iInTransfer, value); }
}
/// True if this binding expression is updating the source
internal bool IsInUpdate
{
get { return TestFlag(PrivateFlags.iInUpdate); }
set { ChangeFlag(PrivateFlags.iInUpdate, value); }
}
/// True if this binding expression is using the fallback value
internal bool UsingFallbackValue
{
get { return TestFlag(PrivateFlags.iUsingFallbackValue); }
set { ChangeFlag(PrivateFlags.iUsingFallbackValue, value); }
}
/// True if this binding expression uses the mentor of the target element
internal bool UsingMentor
{
get { return TestFlag(PrivateFlags.iUsingMentor); }
set { ChangeFlag(PrivateFlags.iUsingMentor, value); }
}
/// True if this binding expression should resolve ElementName within the template of the target element
internal bool ResolveNamesInTemplate
{
get { return TestFlag(PrivateFlags.iResolveNamesInTemplate); }
set { ChangeFlag(PrivateFlags.iResolveNamesInTemplate, value); }
}
/// True if this binding expression has a pending target update
internal bool NeedsDataTransfer
{
get { return TestFlag(PrivateFlags.iNeedDataTransfer); }
set { ChangeFlag(PrivateFlags.iNeedDataTransfer, value); }
}
/// True if this binding expression has a pending source update
internal bool NeedsUpdate
{
get { return TestFlag(PrivateFlags.iNeedUpdate); }
set
{
ChangeFlag(PrivateFlags.iNeedUpdate, value);
if (value)
{
NeedsValidation = true;
}
}
}
/// True if this binding expression needs validation
internal bool NeedsValidation
{
get { return TestFlag(PrivateFlags.iNeedsValidation); }
set { ChangeFlag(PrivateFlags.iNeedsValidation, value); }
}
/// True if this binding expression should raise the TargetUpdated event
internal bool NotifyOnTargetUpdated
{
get { return TestFlag(PrivateFlags.iNotifyOnTargetUpdated); }
set { ChangeFlag(PrivateFlags.iNotifyOnTargetUpdated, value); }
}
/// True if this binding expression should raise the SourceUpdated event
internal bool NotifyOnSourceUpdated
{
get { return TestFlag(PrivateFlags.iNotifyOnSourceUpdated); }
set { ChangeFlag(PrivateFlags.iNotifyOnSourceUpdated, value); }
}
/// True if this binding expression should raise the ValidationError event
internal bool NotifyOnValidationError
{
get { return TestFlag(PrivateFlags.iNotifyOnValidationError); }
set { ChangeFlag(PrivateFlags.iNotifyOnValidationError, value); }
}
/// True if this binding expression belongs to a PriorityBinding
internal bool IsInPriorityBindingExpression
{
get { return TestFlag(PrivateFlags.iInPriorityBindingExpression); }
}
/// True if this binding expression belongs to a MultiBinding
internal bool IsInMultiBindingExpression
{
get { return TestFlag(PrivateFlags.iInMultiBindingExpression); }
}
/// True if this binding expression belongs to a PriorityBinding or MultiBinding
internal bool IsInBindingExpressionCollection
{
get { return TestFlag(PrivateFlags.iInPriorityBindingExpression | PrivateFlags.iInMultiBindingExpression); }
}
/// True if this binding expression validates on exceptions
internal bool ValidatesOnExceptions
{
get { return TestFlag(PrivateFlags.iValidatesOnExceptions); }
}
/// True if this binding expression validates on data errors
internal bool ValidatesOnDataErrors
{
get { return TestFlag(PrivateFlags.iValidatesOnDataErrors); }
}
/// The parent MultiBindingExpression (if any)
internal MultiBindingExpression ParentMultiBindingExpression
{
get { return _parentBindingExpression as MultiBindingExpression; }
}
/// The parent PriorityBindingExpression (if any)
internal PriorityBindingExpression ParentPriorityBindingExpression
{
get { return _parentBindingExpression as PriorityBindingExpression; }
}
/// The parent PriorityBindingExpression or MultiBindingExpression (if any)
internal BindingExpressionBase ParentBindingExpressionBase
{
get { return _parentBindingExpression; }
}
/// The FallbackValue (from the parent Binding), possibly converted
/// to a type suitable for the target property.
internal object FallbackValue
{
// perf note: we recompute the value every time it's needed. This is
// a good decision if we seldom need the value. Alternatively we could
// cache it. Wait until we know what the perf impact really is.
get { return ConvertFallbackValue(ParentBindingBase.FallbackValue, TargetProperty, this); }
}
/// The default value of the target property
internal object DefaultValue
{
// perf note: we recompute the value every time it's needed. This is
// a good decision if we seldom need the value. Alternatively we could
// cache it. Wait until we know what the perf impact really is.
get
{
DependencyObject target = TargetElement;
if (target != null)
{
return TargetProperty.GetDefaultValue(target.DependencyObjectType);
}
return DependencyProperty.UnsetValue;
}
}
/// The effective string format, taking into account the target
/// property, parent bindings, convenience syntax, etc.
internal string EffectiveStringFormat
{
get { return _effectiveStringFormat; }
}
/// The effective TargetNullValue, taking into account the target
/// property, parent bindings, etc.
internal object EffectiveTargetNullValue
{
get { return _effectiveTargetNullValue; }
}
/// return the root of the tree of {Multi/Priority}BindingExpressions
internal BindingExpressionBase RootBindingExpression
{
get
{
BindingExpressionBase child = this;
BindingExpressionBase parent = this.ParentBindingExpressionBase;
while (parent != null)
{
child = parent;
parent = child.ParentBindingExpressionBase;
}
return child;
}
}
/// return the binding group to which this expression belongs (or null)
internal BindingGroup BindingGroup
{
get
{
BindingExpressionBase root = RootBindingExpression;
WeakReference wr = root._bindingGroup;
return (wr == null) ? null : (BindingGroup)wr.Target;
}
}
internal virtual bool IsParentBindingUpdateTriggerDefault
{
get { return false; }
}
//-----------------------------------------------------
//
// Protected Methods
//
//------------------------------------------------------
///
/// Attach the binding expression to the given target object and property.
/// Derived classes should call base.AttachOverride before doing their work.
///
internal virtual void AttachOverride(DependencyObject target, DependencyProperty dp)
{
_targetElement = new WeakReference(target);
_targetProperty = dp;
// get the engine
_engine = DataBindEngine.CurrentDataBindEngine;
DetermineEffectiveStringFormat();
DetermineEffectiveTargetNullValue();
if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.Attach))
{
TraceData.Trace(TraceEventType.Warning,
TraceData.AttachExpression(
TraceData.Identify(this),
target.GetType().FullName, dp.Name, AvTrace.GetHashCodeHelper(target)));
}
}
///
/// Detach the binding expression from its target object and property.
/// Derived classes should call base.DetachOverride after doing their work.
///
internal virtual void DetachOverride()
{
_engine = null;
_targetElement = null;
_targetProperty = null;
SetStatus(BindingStatus.Detached);
if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.Attach))
{
TraceData.Trace(TraceEventType.Warning,
TraceData.DetachExpression(
TraceData.Identify(this)));
}
}
///
/// Invalidate the given child expression.
///
internal abstract void InvalidateChild(BindingExpressionBase bindingExpression);
///
/// Change the dependency sources for the given child expression.
///
internal abstract void ChangeSourcesForChild(BindingExpressionBase bindingExpression, WeakDependencySource[] newSources);
///
/// Replace the given child expression with a new one.
///
internal abstract void ReplaceChild(BindingExpressionBase bindingExpression);
// handle joining or leaving a binding group
internal void OnBindingGroupChanged(bool joining)
{
if (joining)
{
// joining a binding group:
// update Explicitly, unless declared otherwise
if (IsParentBindingUpdateTriggerDefault)
{
if (IsUpdateOnLostFocus)
{
LostFocusEventManager.RemoveListener(TargetElement, this);
}
SetUpdateSourceTrigger(UpdateSourceTrigger.Explicit);
}
}
else
{
// leaving a binding group:
// restore update trigger
if (IsParentBindingUpdateTriggerDefault)
{
FrameworkPropertyMetadata fwMetaData = TargetProperty.GetMetadata(TargetElement.DependencyObjectType) as FrameworkPropertyMetadata;
UpdateSourceTrigger ust = GetDefaultUpdateSourceTrigger(fwMetaData);
SetUpdateSourceTrigger(ust);
if (IsUpdateOnLostFocus)
{
LostFocusEventManager.AddListener(TargetElement, this);
}
}
}
}
// register the leaf bindings with the binding group
internal abstract void UpdateBindingGroup(BindingGroup bg);
// transfer a value from target to source
internal void UpdateValue()
{
UpdateValidationError(null);
object value = GetRawProposedValue();
if (!Validate(value, ValidationStep.RawProposedValue))
return;
value = ConvertProposedValue(value);
if (!Validate(value, ValidationStep.ConvertedProposedValue))
return;
value = UpdateSource(value);
if (!Validate(value, ValidationStep.UpdatedValue))
return;
value = CommitSource(value);
if (!Validate(value, ValidationStep.CommittedValue))
return;
EndSourceUpdate();
}
///
/// Get the raw proposed value
///
internal virtual object GetRawProposedValue()
{
object value = Value;
// TargetNullValue is the UI representation of a "null" value. Use null internally.
if (Object.Equals(value, EffectiveTargetNullValue))
{
value = null;
}
return value;
}
///
/// Get the converted proposed value
///
internal abstract object ConvertProposedValue(object rawValue);
///
/// Get the converted proposed value and inform the binding group
///
internal abstract void ObtainConvertedProposedValue(BindingGroup bindingGroup);
///
/// Update the source value
///
internal abstract object UpdateSource(object convertedValue);
///
/// Update the source value and inform the binding group
///
internal abstract void UpdateSource(BindingGroup bindingGroup);
///
/// Commit the source value
///
internal virtual object CommitSource(object value)
{
return value;
}
///
/// Store the value in the binding group
///
internal abstract void StoreValueInBindingGroup(object value, BindingGroup bindingGroup);
///
/// Run validation rules for the given step
///
internal virtual bool Validate(object value, ValidationStep validationStep)
{
if (value == Binding.DoNothing || value == DependencyProperty.UnsetValue)
return true;
// clear errors from this step - we're about to reevaluate those rules
ClearValidationErrors(validationStep);
Collection validationRules = ParentBindingBase.ValidationRulesInternal;
if (validationRules != null)
{
CultureInfo culture = GetCulture();
switch (validationStep)
{
case ValidationStep.UpdatedValue:
case ValidationStep.CommittedValue:
// rules at these steps get passed the rule owner
value = this;
break;
}
foreach (ValidationRule validationRule in validationRules)
{
if (validationRule.ValidationStep == validationStep)
{
ValidationResult validationResult = validationRule.Validate(value, culture);
if (!validationResult.IsValid)
{
if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.Update))
{
TraceData.Trace(TraceEventType.Warning,
TraceData.ValidationRuleFailed(
TraceData.Identify(this),
TraceData.Identify(validationRule)));
}
UpdateValidationError( new ValidationError(validationRule, this, validationResult.ErrorContent, null));
return false; // kenlai:
}
}
}
}
return true;
}
///
/// Run validation rules for the given step, and inform the binding group
///
internal abstract bool CheckValidationRules(BindingGroup bindingGroup, ValidationStep validationStep);
///
/// Compute the culture, either from the parent Binding, or from the target element.
///
internal CultureInfo GetCulture()
{
// lazy initialization, to let the target element acquire all its properties
if (_culture == DefaultValueObject)
{
// explicit culture set in Binding
_culture = ParentBindingBase.ConverterCultureInternal;
// if that doesn't work, use target element's xml:lang property
if (_culture == null)
{
DependencyObject target = TargetElement;
if (target != null)
{
if (IsInTransfer && (TargetProperty == FrameworkElement.LanguageProperty))
{
// A binding for the Language property needs the value
// of the Language property. This circularity is not
// supported (bug 1274874).
if (TraceData.IsEnabled)
{
TraceData.Trace(TraceEventType.Critical, TraceData.RequiresExplicitCulture, TargetProperty.Name, this);
}
throw new InvalidOperationException(SR.Get(SRID.RequiresExplicitCulture, TargetProperty.Name));
}
// cache CultureInfo since requerying an inheritable property on every Transfer/Update can be quite expensive
// CultureInfo DP rarely changes once a XAML document is loaded.
// To be 100% correct, changes to the CultureInfo attached DP should be tracked
// and cause a re-evaluation of this binding.
_culture = ((XmlLanguage) target.GetValue(FrameworkElement.LanguageProperty)).GetSpecificCulture();
}
}
}
return (CultureInfo)_culture;
}
/// Begin a source update
internal void BeginSourceUpdate()
{
ChangeFlag(PrivateFlags.iInUpdate, true);
}
/// End a source update
internal void EndSourceUpdate()
{
ChangeFlag(PrivateFlags.iInUpdate | PrivateFlags.iNeedUpdate, false);
}
/// change the value to the new value, and notify listeners
internal void ChangeValue(object newValue, bool notify)
{
object oldValue = (_value != DefaultValueObject) ? _value : DependencyProperty.UnsetValue;
_value = newValue;
if (notify && ValueChanged != null)
{
ValueChanged(this, new BindingValueChangedEventArgs(oldValue, newValue));
}
}
/// the target value has changed - the source needs to be updated
internal void Dirty()
{
if (!IsInTransfer)
{
NeedsUpdate = true;
if (IsUpdateOnPropertyChanged)
Update(true);
}
}
/// use the Fallback or Default value, called when a real value is not available
internal object UseFallbackValue()
{
object value = FallbackValue;
// if there's a problem with the fallback, use Default instead
if (value == DefaultValueObject)
{
value = DependencyProperty.UnsetValue;
}
if (value != DependencyProperty.UnsetValue)
{
UsingFallbackValue = true;
}
else
{
// if fallback isn't available, use Default (except when in a binding collection)
if (Status == BindingStatus.Active)
SetStatus(BindingStatus.UpdateTargetError);
if (!IsInBindingExpressionCollection)
{
value = DefaultValue;
if (TraceData.IsEnabled)
{
TraceData.Trace(TraceEventType.Information, TraceData.NoValueToTransfer, this);
}
}
}
return value;
}
// determine if the given value is "null" (in a general sense)
internal bool IsNullValue(object value)
{
if (value == null)
return true;
if (Convert.IsDBNull(value))
return true;
if (IsSqlNull(value))
return true;
return false;
}
// return true if the value is null in the SqlTypes sense
bool IsSqlNull(object value)
{
Type type = value.GetType();
return Engine.IsINullable(type) && IsSqlNullImpl(value);
}
// separate function to avoid loading System.Data unnecessarily
[System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
static bool IsSqlNullImpl(object value)
{
System.Data.SqlTypes.INullable nullable = value as System.Data.SqlTypes.INullable;
return (nullable != null && nullable.IsNull);
}
// determine a "null" value appropriate for the given type
internal object NullValueForType(Type type)
{
if (type == null)
return null;
object sqlNull;
if (IsSqlNullableType(type, out sqlNull))
return sqlNull;
if (!type.IsValueType)
return null;
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
return null;
return DependencyProperty.UnsetValue;
}
// return true if the type is nullable in the SqlTypes sense. If so, return the null value.
bool IsSqlNullableType(Type type, out object nullValue)
{
if (Engine.IsINullable(type))
{
return IsSqlNullableTypeImpl(type, out nullValue);
}
else
{
nullValue = null;
return false;
}
}
// separate function to avoid loading System.Data unnecessarily
[System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
static bool IsSqlNullableTypeImpl(Type type, out object nullValue)
{
// some SqlTypes are classes with a Null property. Others are structs with a Null field. Try both.
System.Reflection.FieldInfo nullField = type.GetField("Null", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.FlattenHierarchy);
if (nullField != null)
{
nullValue = nullField.GetValue(null);
return true;
}
System.Reflection.PropertyInfo nullProperty = type.GetProperty("Null", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.FlattenHierarchy);
if (nullProperty != null)
{
nullValue = nullProperty.GetValue(null, null);
return true;
}
nullValue = null;
return false;
}
internal ValidationRule LookupValidationRule(Type type)
{
ValidationRule result = ParentBindingBase.GetValidationRule(type);
if (result == null && _parentBindingExpression != null)
{
result = _parentBindingExpression.LookupValidationRule(type);
}
return result;
}
// discover the binding group (if any) that this binding should join,
// and join it. More precisely, cause the root binding to join.
internal void JoinBindingGroup(bool isReflective, DependencyObject contextElement)
{
BindingGroup bindingGroup = RootBindingExpression.FindBindingGroup(isReflective, contextElement);
if (bindingGroup != null)
{
JoinBindingGroup(bindingGroup, /*explicit*/false);
}
}
// remove the binding from its group
internal void LeaveBindingGroup()
{
BindingExpressionBase root = RootBindingExpression;
BindingGroup bg = root.BindingGroup;
if (bg != null)
{
bg.BindingExpressions.Remove(root);
root._bindingGroup = null;
}
}
// discover the binding group (if any) that this root binding should join.
BindingGroup FindBindingGroup(bool isReflective, DependencyObject contextElement)
{
Debug.Assert(RootBindingExpression == this, "Only call this with a root binding");
// if we've already joined (or failed to join) a group, just return the result
if (_bindingGroup != null)
{
return (BindingGroup)_bindingGroup.Target;
}
BindingGroup bg;
string groupName = ParentBindingBase.BindingGroupName;
// a null group name means "don't join any group".
if (groupName == null)
{
MarkAsNonGrouped();
return null;
}
// an empty group name means join by DataContext
if (String.IsNullOrEmpty(groupName))
{
// check further preconditions:
if (!isReflective || // must have target-to-source data flow
contextElement == null) // must use data context
{
// later child bindings might pass this test, so don't mark
// this root binding as non-grouped yet
return null;
}
// only the innermost binding group is eligible
bg = (BindingGroup)contextElement.GetValue(FrameworkElement.BindingGroupProperty);
if (bg == null)
{
MarkAsNonGrouped();
return null;
}
// the context element must share data context with the group
DependencyProperty dataContextDP = FrameworkElement.DataContextProperty;
DependencyObject groupContextElement = bg.InheritanceContext;
if (groupContextElement == null ||
!Object.Equals( contextElement.GetValue(dataContextDP),
groupContextElement.GetValue(dataContextDP)))
{
MarkAsNonGrouped();
return null;
}
// if the binding survives the gauntlet, return the group
return bg;
}
// a non-empty group name means join by name
else
{
// walk up the tree, looking for a matching binding group
DependencyProperty bindingGroupDP = FrameworkElement.BindingGroupProperty;
FrameworkObject fo = new FrameworkObject(TargetElement);
while (fo.DO != null)
{
BindingGroup bgCandidate = (BindingGroup)fo.DO.GetValue(bindingGroupDP);
if (bgCandidate == null)
{
MarkAsNonGrouped();
return null;
}
if (bgCandidate.Name == groupName)
{
// return the matching group
return bgCandidate;
}
fo = fo.FrameworkParent;
}
// no match - report an error
if (TraceData.IsEnabled)
{
TraceData.Trace(TraceEventType.Error,
TraceData.BindingGroupNameMatchFailed(groupName),
this);
}
MarkAsNonGrouped();
return null;
}
}
// add a binding to the given binding group
internal void JoinBindingGroup(BindingGroup bg, bool explicitJoin)
{
BindingExpressionBase root = null; // set to non-null by the next loop
for ( BindingExpressionBase bindingExpr = this;
bindingExpr != null;
bindingExpr = bindingExpr.ParentBindingExpressionBase)
{
root = bindingExpr;
// bindings in a group update Explicitly, unless declared otherwise
bindingExpr.OnBindingGroupChanged(/*joining*/true);
bg.AddToValueTable(bindingExpr);
}
// add the root binding to the group
if (root._bindingGroup == null)
{
// use WeakReference because the BindingGroup contains a strong reference
// to the visual tree (via InheritanceContext)
root._bindingGroup = new WeakReference(bg);
// when the group is implicitly discovered, always add the root binding to the group's collection.
// When the binding is added explicitly - via BindingGroup.BindingExpressions.Add() -
// check first to see if it has already been added
bool addToGroup = explicitJoin ? !bg.BindingExpressions.Contains(root) : true;
if (addToGroup)
{
bg.BindingExpressions.Add(root);
}
// in the explicit case, register its items and values with the binding group
// (the implicit case does this when the bindings activate)
if (explicitJoin)
{
root.UpdateBindingGroup(bg);
}
}
else
{
if (root.BindingGroup != bg)
throw new InvalidOperationException(SR.Get(SRID.BindingGroup_CannotChangeGroups));
}
}
// mark a binding as non-grouped, so that we avoid doing the discovery again
void MarkAsNonGrouped()
{
// Leaf bindings only get asked once, so there's no need to add a mark
if (!(this is BindingExpression))
{
_bindingGroup = new WeakReference(null);
}
}
///
/// Handle events from the centralized event table
///
internal virtual bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
{
return false; // unrecognized event
}
private bool TestFlag(PrivateFlags flag)
{
return (_flags & flag) != 0;
}
private void ChangeFlag(PrivateFlags flag, bool value)
{
if (value) _flags |= flag;
else _flags &= ~flag;
}
//-----------------------------------------------------
//
// Internal Properties
//
//------------------------------------------------------
internal DependencyProperty TargetProperty { get { return _targetProperty; } }
// A BindingExpression cannot hold a strong reference to the target element - this
// leads to memory leaks (bug 871139). The problem is that BindingExpression and its workers
// register for events from the data item, creating a reference from
// the data item to the BindingExpression. The data item typically has a long lifetime,
// so if the BindingExpression held a SR to the target, the target element would
// also stay alive forever.
// Instead, BindingExpression holds a WeakReference to the target. This means we
// have to check it before dereferencing (here), and cope when the
// reference fails (in callers to this property). Requests for the TargetElement
// are not trivial, so callers should request it once and cache the result
// in a local variable. They should not save it in a global or instance
// variable of course; that would defeat the purpose of the WR.
// This allows the target element to be GC'd when it's no longer in
// use by the tree or application. The next time the BindingExpression asks for
// its TargetElement, the WR will fail. At this point, the BindingExpression is no
// longer useful, so it can sever all connections to the outside world (i.e.
// stop listening for events). This allows the BindingExpression itself to be GC'd.
internal DependencyObject TargetElement
{
get
{
if (_targetElement != null)
{
DependencyObject result = _targetElement.Target as DependencyObject;
if (result != null)
return result;
// target has been GC'd, sever all connections
_targetElement = null; // prevents re-entry from Detach()
Detach();
}
return null;
}
}
internal WeakReference TargetElementReference
{
get { return _targetElement; }
}
internal DataBindEngine Engine
{
get { return _engine; }
}
internal Dispatcher Dispatcher
{
get { return (_engine != null) ? _engine.Dispatcher : null; }
}
internal object Value
{
get
{
if (_value == DefaultValueObject)
{
// don't notify listeners. This isn't a real value change.
ChangeValue(UseFallbackValue(), false /*notify*/);
}
return _value;
}
set
{
ChangeValue(value, true);
Dirty();
}
}
internal WeakDependencySource[] WeakSources
{
get { return _sources; }
}
///
/// NoTarget DependencyProperty, a placeholder used by BindingExpressions with no target property
///
internal static readonly DependencyProperty NoTargetProperty =
DependencyProperty.RegisterAttached("NoTarget", typeof(object), typeof(BindingExpressionBase),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None));
internal TraceLog TraceLog { get { return _traceLog; } }
//------------------------------------------------------
//
// Internal Methods
//
//-----------------------------------------------------
///
/// Attach the binding expression to the given target object and property.
///
internal void Attach(DependencyObject target, DependencyProperty dp)
{
// make sure we're on the right thread to access the target
if (target != null)
{
target.VerifyAccess();
}
IsAttaching = true;
AttachOverride(target, dp);
IsAttaching = false;
}
///
/// Detach the binding expression from its target object and property.
///
internal void Detach()
{
if (_status == BindingStatus.Detached || IsDetaching)
return;
IsDetaching = true;
DetachOverride();
IsDetaching = false;
}
internal void SetStatus(BindingStatus status)
{
if (_status == BindingStatus.Detached && status != _status)
{
throw new InvalidOperationException(SR.Get(SRID.BindingExpressionStatusChanged, _status, status));
}
_status = status;
if (_traceLog != null)
{
_traceLog.Add("Set status = {0} {1}", _status, new StackTrace());
}
}
// convert a user-supplied fallback value to a usable equivalent
// returns: UnsetValue if user did not supply a fallback value
// value if fallback value is legal
// DefaultValueObject otherwise
internal static object ConvertFallbackValue(object value, DependencyProperty dp, object sender)
{
Exception e;
object result = ConvertValue(value, dp, out e);
if (result == DefaultValueObject)
{
if (TraceData.IsEnabled)
{
TraceData.Trace(TraceEventType.Error,
TraceData.FallbackConversionFailed(
AvTrace.ToStringHelper(value),
AvTrace.TypeName(value),
dp.Name,
dp.PropertyType.Name),
sender, e);
}
}
return result;
}
// convert a user-supplied TargetNullValue to a usable equivalent
// returns: UnsetValue if user did not supply a fallback value
// value if fallback value is legal
// DefaultValueObject otherwise
internal static object ConvertTargetNullValue(object value, DependencyProperty dp, object sender)
{
Exception e;
object result = ConvertValue(value, dp, out e);
if (result == DefaultValueObject)
{
if (TraceData.IsEnabled)
{
TraceData.Trace(TraceEventType.Error,
TraceData.TargetNullValueConversionFailed(
AvTrace.ToStringHelper(value),
AvTrace.TypeName(value),
dp.Name,
dp.PropertyType.Name),
sender, e);
}
}
return result;
}
static object ConvertValue(object value, DependencyProperty dp, out Exception e)
{
object result;
e = null;
if (value == DependencyProperty.UnsetValue || dp.IsValidValue(value))
{
result = value;
}
else
{
result = null; // placeholder to keep compiler happy
// if value isn't the right type, use a type converter to get a better value
bool success = false;
TypeConverter converter = DefaultValueConverter.GetConverter(dp.PropertyType);
if (converter != null && converter.CanConvertFrom(value.GetType()))
{
// PreSharp uses message numbers that the C# compiler doesn't know about.
// Disable the C# complaints, per the PreSharp documentation.
#pragma warning disable 1634, 1691
// PreSharp complains about catching NullReference (and other) exceptions.
// It doesn't recognize that IsCriticalException() handles these correctly.
#pragma warning disable 56500
try
{
result = converter.ConvertFrom(null, CultureInfo.InvariantCulture, value);
success = dp.IsValidValue(result);
}
// Catch all exceptions. If we can't convert the fallback value, it doesn't
// matter why not; we should always use the default value instead.
// (See bug 1853628 for an example of a converter that throws
// an exception not mentioned in the documentation for ConvertFrom.)
catch (Exception ex)
{
e = ex;
}
catch // non CLS compliant exception
{
}
#pragma warning restore 56500
#pragma warning restore 1634, 1691
}
if (!success)
{
// if can't convert it, don't use it
result = DefaultValueObject;
}
}
return result;
}
// Certain trace reports should be marked as 'error' unless the binding
// is prepared to handle it in some way (e.g. FallbackValue), in which
// case 'warning'.
internal TraceEventType TraceLevel
{
get
{
// FallbackValue is present
if (ParentBindingBase.FallbackValue != DependencyProperty.UnsetValue)
return TraceEventType.Warning;
// Binding is a member of MultiBinding or PriorityBinding
if (IsInBindingExpressionCollection)
return TraceEventType.Warning;
// all other cases - error
return TraceEventType.Error;
}
}
internal virtual void Activate()
{
}
internal virtual void Deactivate()
{
}
internal virtual void Update(bool synchronous)
{
}
internal void UpdateValidationError(ValidationError validationError)
{
if (_validationError != null)
{
ValidationError oldValidationError = _validationError;
_validationError = null; // clear before raising events, so that HasError is correct
RemoveValidationError(oldValidationError);
}
_validationError = validationError;
if (_validationError != null)
{
AddValidationError(_validationError);
}
}
internal void AddValidationError(ValidationError validationError)
{
// add the error to the target element
Validation.AddValidationError(validationError, TargetElement, NotifyOnValidationError);
// add the error to the binding group's target element
BindingGroup bindingGroup = BindingGroup;
if (bindingGroup != null)
{
bindingGroup.AddValidationError(validationError);
}
}
internal void RemoveValidationError(ValidationError validationError)
{
// remove the error from the target element
Validation.RemoveValidationError(validationError, TargetElement, NotifyOnValidationError);
// remove the error from the binding group's target element
BindingGroup bindingGroup = BindingGroup;
if (bindingGroup != null)
{
bindingGroup.RemoveValidationError(validationError);
}
}
// remove all errors raised at the given step, in preparation for running
// the rules at that step
internal void ClearValidationErrors(ValidationStep validationStep)
{
if (_validationError == null || _validationError.RuleInError.ValidationStep != validationStep)
return;
RemoveValidationError(_validationError);
_validationError = null;
}
internal void ChangeSources(WeakDependencySource[] newSources)
{
if (IsInBindingExpressionCollection)
ParentBindingExpressionBase.ChangeSourcesForChild(this, newSources);
else
ChangeSources(TargetElement, TargetProperty, newSources);
// store the sources with weak refs, so they don't cause memory leaks (bug 980041)
_sources = newSources;
}
///
/// combine the sources of BindingExpressions, using new sources for
/// the BindingExpression at the given index
///
/// -1 to indicate no new sources
/// collection of child binding expressions
/// how many child expressions to include
/// use null when no new sources
///
internal static WeakDependencySource[] CombineSources(int index, Collection bindingExpressions, int count, WeakDependencySource[] newSources)
{
if (index == count)
{
// Be sure to include newSources if they are being appended
count++;
}
Collection tempList = new Collection();
for (int i = 0; i < count; ++i)
{
BindingExpressionBase bindExpr = bindingExpressions[i];
WeakDependencySource[] sources = (i==index) ? newSources :
(bindExpr != null) ? bindExpr.WeakSources :
null;
int m = (sources == null) ? 0 : sources.Length;
for (int j = 0; j < m; ++j)
{
WeakDependencySource candidate = sources[j];
// don't add duplicate source
for (int k = 0; k < tempList.Count; ++k)
{
WeakDependencySource prior = tempList[k];
if (candidate.DependencyObject == prior.DependencyObject &&
candidate.DependencyProperty == prior.DependencyProperty)
{
candidate = null;
break;
}
}
if (candidate != null)
tempList.Add(candidate);
}
}
WeakDependencySource[] result;
if (tempList.Count > 0)
{
result = new WeakDependencySource[tempList.Count];
tempList.CopyTo(result, 0);
tempList.Clear();
}
else
{
result = null;
}
return result;
}
internal void ResolvePropertyDefaultSettings(BindingMode mode, UpdateSourceTrigger updateTrigger, FrameworkPropertyMetadata fwMetaData)
{
// resolve "property-default" dataflow
if (mode == BindingMode.Default)
{
BindingFlags f = BindingFlags.OneWay;
if (fwMetaData != null && fwMetaData.BindsTwoWayByDefault)
{
f = BindingFlags.TwoWay;
}
ChangeFlag(PrivateFlags.iPropagationMask, false);
ChangeFlag((PrivateFlags)f, true);
if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.ResolveDefaults))
{
TraceData.Trace(TraceEventType.Warning,
TraceData.ResolveDefaultMode(
TraceData.Identify(this),
(f == BindingFlags.OneWay) ? BindingMode.OneWay : BindingMode.TwoWay));
}
}
Debug.Assert((_flags & PrivateFlags.iPropagationMask) != PrivateFlags.iPropDefault,
"BindingExpression should not have Default propagation");
// resolve "property-default" update trigger
if (updateTrigger == UpdateSourceTrigger.Default)
{
UpdateSourceTrigger ust = GetDefaultUpdateSourceTrigger(fwMetaData);
SetUpdateSourceTrigger(ust);
if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.ResolveDefaults))
{
TraceData.Trace(TraceEventType.Warning,
TraceData.ResolveDefaultUpdate(
TraceData.Identify(this),
ust));
}
}
Invariant.Assert((_flags & PrivateFlags.iUpdateMask) != PrivateFlags.iUpdateDefault,
"BindingExpression should not have Default update trigger");
}
// return the effective update trigger, used when binding doesn't set one explicitly
internal UpdateSourceTrigger GetDefaultUpdateSourceTrigger(FrameworkPropertyMetadata fwMetaData)
{
UpdateSourceTrigger ust =
IsInMultiBindingExpression ? UpdateSourceTrigger.Explicit :
(fwMetaData != null) ? fwMetaData.DefaultUpdateSourceTrigger :
UpdateSourceTrigger.PropertyChanged;
return ust;
}
internal void SetUpdateSourceTrigger(UpdateSourceTrigger ust)
{
ChangeFlag(PrivateFlags.iUpdateMask, false);
ChangeFlag((PrivateFlags)BindingBase.FlagsFrom(ust), true);
}
internal void DetermineEffectiveStringFormat()
{
Type targetType = TargetProperty.PropertyType;
if (targetType != typeof(String))
{
// if the target type isn't String, we don't need a string format
return; // _effectiveStringFormat is already null
}
// determine the effective target type and the declared string format
// by looking up the tree of binding expressions
string stringFormat = ParentBindingBase.StringFormat;
BindingExpressionBase be = this.ParentBindingExpressionBase;
while (be != null)
{
if (be is MultiBindingExpression)
{
// MultiBindings should receive object values, not string
targetType = typeof(Object);
break;
}
else if (stringFormat == null && be is PriorityBindingExpression)
{
// use a PriorityBinding's string format, unless we already
// have a more specific one
stringFormat = be.ParentBindingBase.StringFormat;
}
be = be.ParentBindingExpressionBase;
}
// if we need a string format, cache it
if (targetType == typeof(String))
{
#if NotToday
// special case: when these conditions all apply
// a) target element belongs to a DataTemplate
// b) template was found by implicit (type-based) lookup
// c) container (ContentPresenter) has a string format
// d) binding has no effective string format
// then use the CP's string format. This enables scenarios like
//
//
//
// where you'd like to control the format at the point of use,
// rather than at the point of template declaration. This is
// especially useful when the template is declared in a global place
// like app resources or a theme file. In particular it makes the
// GroupStyle.HeaderStringFormat property work; the template for
// GroupItem is defined in the theme, but needs to pick up formatting
// from the app's markup.
if (stringFormat == null) // (d)
{
FrameworkObject fo = new FrameworkObject(TargetElement);
ContentPresenter cp = fo.TemplatedParent as ContentPresenter;
if (cp != null && // (a)
cp.ContentStringFormat != null) // (c)
{
DataTemplate dt = cp.TemplateInternal as DataTemplate;
if (dt != null && // (a)
dt.DataType != null) // (b)
{
stringFormat = cp.ContentStringFormat;
}
}
}
#endif
if (!String.IsNullOrEmpty(stringFormat))
{
_effectiveStringFormat = Helper.GetEffectiveStringFormat(stringFormat);
}
}
}
internal void DetermineEffectiveTargetNullValue()
{
Type targetType = TargetProperty.PropertyType;
// determine the effective target type and the declared TargetNullValue
// by looking up the tree of binding expressions
object targetNullValue = ParentBindingBase.TargetNullValue;
BindingExpressionBase be = this.ParentBindingExpressionBase;
while (be != null)
{
if (be is MultiBindingExpression)
{
// MultiBindings should receive object values
targetType = typeof(Object);
break;
}
else if (targetNullValue == DependencyProperty.UnsetValue && be is PriorityBindingExpression)
{
// use a PriorityBinding's TargetNullValue, unless we already
// have a more specific one
targetNullValue = be.ParentBindingBase.TargetNullValue;
}
be = be.ParentBindingExpressionBase;
}
// if user declared a TargetNullValue, make sure it has the right type.
if (targetNullValue != DependencyProperty.UnsetValue)
{
targetNullValue = ConvertTargetNullValue(targetNullValue, TargetProperty, this);
if (targetNullValue == DefaultValueObject)
{
// if not, ignore it (having logged a trace message)
targetNullValue = DependencyProperty.UnsetValue;
}
}
// for back-compat, don't turn on TargetNullValue unless user explicitly
// asks for it. This means users have to add TargetNullValue to get
// pretty basic scenarios to work (e.g. binding a TextBox to a database
// string field that supports DBNull). It's painful, but can't be
// helped.
#if TargetNullValueBC //BreakingChange
// if no declared (or poorly declared) value, make one up
if (targetNullValue == DependencyProperty.UnsetValue)
{
targetNullValue = NullValueForType(targetType);
}
#endif
_effectiveTargetNullValue = targetNullValue;
}
// To prevent memory leaks, we store WeakReferences to certain objects
// in various places: _dataItem, _sources, worker fields. The logic
// for this is centralized in these two static methods. (See bug 940041)
internal static object CreateReference(object item)
{
// One source of leaks is the reference cycle formed when a BindingExpression's
// source item contains a reference chain to the target element:
// target -> BindingExpression -> source item -> target
//
// Making the second link into a WeakReference incurs some cost,
// so it should be avoided if we know the third link never exists.
// We definitely can't avoid this when the item is a DependencyObject,
// since it's reasonably common for the third link to occur (e.g.
// a ContextMenu contains a link to its Popup, which has a property
// bound back to the ContextMenu).
//
// For safety, we choose to use WeakRef all the time, unless the item is null.
// Exception (bug 1124954): Keep a strong reference to
// BindingListCollectionView - this keeps the underlying DataView
// alive, when nobody else will.
// Exception (bug 1970505): Don't allocate a WeakRef for the common
// case of the NullDataItem
if (item != null &&
!(item is BindingListCollectionView) &&
!(item == BindingExpression.NullDataItem))
{
item = new WeakReference(item);
}
#if USE_ITEM_REFERENCE
item = new ItemReference(item);
#endif
return item;
}
// like CreateReference, but use an existing WeakReference
internal static object CreateReference(WeakReference item)
{
object result = item;
#if USE_ITEM_REFERENCE
result = new ItemReference(item);
#endif
return result;
}
// like CreateReference, except re-target the old WeakReference (if any)
internal static object ReplaceReference(object oldReference, object item)
{
if (item != null &&
!(item is BindingListCollectionView) &&
!(item == BindingExpression.NullDataItem))
{
#if USE_ITEM_REFERENCE
// if this cast fails, it's because you have done a direct assignment of an
// item to some field instead of assigning the result of CreateReference.
oldReference = ((ItemReference)oldReference).Item;
#endif
WeakReference wr = oldReference as WeakReference;
if (wr != null)
{
wr.Target = item;
item = wr;
}
else
{
item = new WeakReference(item);
}
}
#if USE_ITEM_REFERENCE
item = new ItemReference(item);
#endif
return item;
}
internal static object GetReference(object reference)
{
if (reference == null)
return null;
#if USE_ITEM_REFERENCE
// if this cast fails, it's because you have done a direct assignment of an
// item to some field instead of assigning the result of CreateReference.
reference = ((ItemReference)reference).Item;
#endif
WeakReference wr = reference as WeakReference;
if (wr != null)
return wr.Target;
else
return reference;
}
internal static void InitializeTracing(BindingExpressionBase expr, DependencyObject d, DependencyProperty dp)
{
BindingBase parent = expr.ParentBindingBase;
}
//------------------------------------------------------
//
// Private Methods
//
//-----------------------------------------------------
#if USE_ITEM_REFERENCE
private class ItemReference
{
internal ItemReference(object item)
{
_item = item;
}
internal object Item { get { return _item; } }
object _item;
}
#endif
// change WeakDependencySources to (strong) DependencySources, and notify
// the property engine about the new sources
void ChangeSources(DependencyObject target, DependencyProperty dp, WeakDependencySource[] newSources)
{
DependencySource[] sources;
if (newSources != null)
{
// convert weak reference to strong
sources = new DependencySource[newSources.Length];
int n = 0;
for (int i = 0; i < newSources.Length; ++i)
{
DependencyObject sourceDO = newSources[i].DependencyObject;
if (sourceDO != null)
{
// only include sources that are still alive
sources[n++] = new DependencySource(sourceDO, newSources[i].DependencyProperty);
}
}
// if any of the sources were no longer alive, trim the array
if (n < newSources.Length)
{
DependencySource[] temp;
if (n > 0)
{
temp = new DependencySource[n];
Array.Copy(sources, 0, temp, 0, n);
}
else
{
temp = null;
}
sources = temp;
}
}
else
{
sources = null;
}
// notify property engine
ChangeSources(target, dp, sources);
}
// this method is here just to avoid the compiler error
// error CS0649: Warning as Error: Field 'System.Windows.Data.BindingExpression._traceLog' is never assigned to, and will always have its default value null
void InitializeTraceLog()
{
_traceLog = new TraceLog(20);
}
//-----------------------------------------------------
//
// Private Fields
//
//-----------------------------------------------------
BindingBase _binding;
WeakReference _targetElement;
DependencyProperty _targetProperty;
DataBindEngine _engine;
PrivateFlags _flags;
BindingExpressionBase _parentBindingExpression;
object _value = DefaultValueObject;
BindingStatus _status;
TraceLog _traceLog;
ValidationError _validationError;
WeakDependencySource[] _sources;
string _effectiveStringFormat;
object _effectiveTargetNullValue;
WeakReference _bindingGroup;
object _culture = DefaultValueObject;
/// Sentinel meaning "field has its default value"
internal static readonly object DefaultValueObject = new object();
}
}
// 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 BindingExpressionBase object,
// base class for BindingExpression, PriorityBindingExpression,
// and MultiBindingExpression.
//
// See spec at http://avalon/connecteddata/Specs/Data%20Binding.mht
//
//---------------------------------------------------------------------------
// build with this symbol defined to catch errors about not using
// BindingExpression.GetReference correctly
//#define USE_ITEM_REFERENCE
using System;
using System.Collections.ObjectModel; // Collection
using System.ComponentModel; // TypeConverter
using System.Diagnostics; // StackTrace
using System.Globalization; // CultureInfo
using System.Windows; // FrameworkElement
using System.Windows.Controls; // Validation
using System.Windows.Markup; // XmlLanguage
using System.Windows.Threading; // Dispatcher
using MS.Internal; // Invariant.Assert
using MS.Internal.Controls; // ValidationErrorCollection
using MS.Internal.Data; // DataBindEngine
using MS.Internal.KnownBoxes; // BooleanBoxes
using MS.Internal.Utility; // TraceLog
namespace System.Windows.Data
{
///
/// Base class for Binding Expressions.
///
public abstract class BindingExpressionBase : Expression, IWeakEventListener
{
// Flags indicating run-time properties of a BindingExpression
[Flags]
internal enum BindingFlags
{
// names used by Binding
OneWay = PrivateFlags.iSourceToTarget,
TwoWay = PrivateFlags.iSourceToTarget | PrivateFlags.iTargetToSource,
OneWayToSource = PrivateFlags.iTargetToSource,
OneTime = 0,
PropDefault = PrivateFlags.iPropDefault,
NotifyOnTargetUpdated = PrivateFlags.iNotifyOnTargetUpdated,
NotifyOnSourceUpdated = PrivateFlags.iNotifyOnSourceUpdated,
NotifyOnValidationError = PrivateFlags.iNotifyOnValidationError,
UpdateOnPropertyChanged = 0,
UpdateOnLostFocus = PrivateFlags.iUpdateOnLostFocus,
UpdateExplicitly = PrivateFlags.iUpdateExplicitly,
UpdateDefault = PrivateFlags.iUpdateDefault,
PathGeneratedInternally = PrivateFlags.iPathGeneratedInternally,
ValidatesOnExceptions = PrivateFlags.iValidatesOnExceptions,
ValidatesOnDataErrors = PrivateFlags.iValidatesOnDataErrors,
Default = PropDefault | UpdateDefault,
/// Error value, returned by FlagsFrom to indicate faulty input
IllegalInput = PrivateFlags.iIllegalInput,
PropagationMask = OneWay | TwoWay | OneWayToSource | OneTime | PropDefault,
UpdateMask = UpdateOnPropertyChanged | UpdateOnLostFocus | UpdateExplicitly | UpdateDefault,
}
[Flags]
private enum PrivateFlags
{
// internal use
iSourceToTarget = 0x00000001,
iTargetToSource = 0x00000002,
iPropDefault = 0x00000004,
iNotifyOnTargetUpdated = 0x00000008,
iDefaultValueConverter = 0x00000010,
iInTransfer = 0x00000020,
iInUpdate = 0x00000040,
iTransferPending = 0x00000080,
iNeedDataTransfer = 0x00000100,
iTransferDeferred = 0x00000200, // used by MultiBindingExpression
iUpdateOnLostFocus = 0x00000400,
iUpdateExplicitly = 0x00000800,
iUpdateDefault = iUpdateExplicitly | iUpdateOnLostFocus,
iNeedUpdate = 0x00001000,
iPathGeneratedInternally = 0x00002000,
iUsingMentor = 0x00004000,
iResolveNamesInTemplate = 0x00008000,
iDetaching = 0x00010000,
iNeedsCollectionView = 0x00020000,
iInPriorityBindingExpression= 0x00040000,
iInMultiBindingExpression = 0x00080000,
iUsingFallbackValue = 0x00100000,
iNotifyOnValidationError = 0x00200000,
iAttaching = 0x00400000,
iNotifyOnSourceUpdated = 0x00800000,
iValidatesOnExceptions = 0x01000000,
iValidatesOnDataErrors = 0x02000000,
iIllegalInput = 0x04000000,
iNeedsValidation = 0x08000000,
iPropagationMask = iSourceToTarget | iTargetToSource | iPropDefault,
iUpdateMask = iUpdateOnLostFocus | iUpdateExplicitly,
}
//-----------------------------------------------------
//
// Constructors
//
//-----------------------------------------------------
/// Constructor
internal BindingExpressionBase(BindingBase binding, BindingExpressionBase parent) : base(ExpressionMode.SupportsUnboundSources)
{
if (binding == null)
throw new ArgumentNullException("binding");
_binding = binding;
_parentBindingExpression = parent;
_flags = (PrivateFlags)binding.Flags;
if (parent != null)
{
ResolveNamesInTemplate = parent.ResolveNamesInTemplate;
Type type = parent.GetType();
if (type == typeof(MultiBindingExpression))
ChangeFlag(PrivateFlags.iInMultiBindingExpression, true);
else if (type == typeof(PriorityBindingExpression))
ChangeFlag(PrivateFlags.iInPriorityBindingExpression, true);
}
// initialize tracing information
PresentationTraceLevel traceLevel = PresentationTraceSources.GetTraceLevel(binding);
if (traceLevel > 0)
{
// copy TraceLevel from parent BindingBase - it can be changed later
PresentationTraceSources.SetTraceLevel(this, traceLevel);
}
if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.CreateExpression))
{
if (parent == null)
{
TraceData.Trace(TraceEventType.Warning,
TraceData.CreatedExpression(
TraceData.Identify(this),
TraceData.Identify(binding)));
}
else
{
TraceData.Trace(TraceEventType.Warning,
TraceData.CreatedExpressionInParent(
TraceData.Identify(this),
TraceData.Identify(binding),
TraceData.Identify(parent)));
}
}
if (LookupValidationRule(typeof(ExceptionValidationRule)) != null)
{
ChangeFlag(PrivateFlags.iValidatesOnExceptions, true);
}
if (LookupValidationRule(typeof(DataErrorValidationRule)) != null)
{
ChangeFlag(PrivateFlags.iValidatesOnDataErrors, true);
}
}
//------------------------------------------------------
//
// Public Properties
//
//-----------------------------------------------------
/// Binding from which this expression was created
public BindingBase ParentBindingBase { get { return _binding; } }
/// Status of the BindingExpression
public BindingStatus Status { get { return _status; } }
///
/// The ValidationError that caused this
/// BindingExpression to be invalid.
///
public virtual ValidationError ValidationError
{
get
{
return _validationError;
}
}
///
/// HasError returns true if any of the ValidationRules
/// in the ParentBinding failed its validation rule.
///
public virtual bool HasError
{
get
{
return _validationError != null;
}
}
//------------------------------------------------------
//
// Public Methods
//
//------------------------------------------------------
/// Force a data transfer from source to target
public virtual void UpdateTarget()
{
}
/// Send the current value back to the source
/// Does nothing when binding's Mode is not TwoWay or OneWayToSource
public virtual void UpdateSource()
{
}
#region Expression overrides
///
/// Notification that the Expression has been set as a property's value
///
///
/// Subclasses should not override OnAttach(), but must override Attach()
///
/// DependencyObject being set
/// Property being set
internal sealed override void OnAttach(DependencyObject d, DependencyProperty dp)
{
if (d == null)
throw new ArgumentNullException("d");
if (dp == null)
throw new ArgumentNullException("dp");
Attach(d, dp);
}
///
/// Notification that the Expression has been removed as a property's value
///
///
/// Subclasses should not override OnDetach(), but must override Detach()
///
/// DependencyObject being cleared
/// Property being cleared
internal sealed override void OnDetach(DependencyObject d, DependencyProperty dp)
{
Detach();
}
///
/// List of sources of the Expression
///
/// Sources list
internal override DependencySource[] GetSources()
{
int j, k;
int n = (_sources != null) ? _sources.Length : 0;
if (n == 0)
return null;
DependencySource[] result = new DependencySource[n];
// convert the weak references into strong ones
for (j=0, k=0; k
/// Handle events from the centralized event table
///
bool IWeakEventListener.ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
{
return ReceiveWeakEvent(managerType, sender, e);
}
#region BindingExpressions with no target DP
//-----------------------------------------------------
//
// API for BindingExpressions with no target DP (task 20769)
// This is internal for now, until we review whether we wish to
// make it public
//
//------------------------------------------------------
/// Create an untargeted BindingExpression
internal static BindingExpressionBase CreateUntargetedBindingExpression(DependencyObject d, BindingBase binding)
{
return binding.CreateBindingExpression(d, NoTargetProperty);
}
/// Attach the BindingExpression to its target element
///
/// This method must be called once during the initialization.
///
/// The target element
internal void Attach(DependencyObject d)
{
Attach(d, NoTargetProperty);
}
/// This event is raised when the BindingExpression's value changes
internal event EventHandler ValueChanged;
/* The following APIs are also needed for untargeted bindings, but they already exist
for other reasons.
/// The current value of the BindingExpression
internal object Value { get; set; }
/// Activate the BindingExpression, using the given item as its root item.
internal void Activate(object item) {}
/// Deactivate the BindingExpression.
internal void Deactivate() {}
/// Detach the BindingExpression from its target element
///
/// This method must be called once when the BindingExpression is no longer needed.
///
internal void Detach() {}
*/
#endregion BindingExpressions with no target DP
//-----------------------------------------------------
//
// Protected Properties
//
//-----------------------------------------------------
/// True if this binding expression is attaching
internal bool IsAttaching
{
get { return TestFlag(PrivateFlags.iAttaching); }
set { ChangeFlag(PrivateFlags.iAttaching, value); }
}
/// True if this binding expression is detaching
internal bool IsDetaching
{
get { return TestFlag(PrivateFlags.iDetaching); }
set { ChangeFlag(PrivateFlags.iDetaching, value); }
}
/// True if this binding expression updates the target
internal bool IsDynamic
{
get
{
return ( TestFlag(PrivateFlags.iSourceToTarget)
&& (!IsInMultiBindingExpression || ParentMultiBindingExpression.IsDynamic));
}
}
/// True if this binding expression updates the source
internal bool IsReflective
{
get
{
return ( TestFlag(PrivateFlags.iTargetToSource)
&& (!IsInMultiBindingExpression || ParentMultiBindingExpression.IsReflective));
}
set { ChangeFlag(PrivateFlags.iTargetToSource, value); }
}
/// True if this binding expression uses a default ValueConverter
internal bool UseDefaultValueConverter
{
get { return TestFlag(PrivateFlags.iDefaultValueConverter); }
set { ChangeFlag(PrivateFlags.iDefaultValueConverter, value); }
}
/// True if this binding expression is OneWayToSource
internal bool IsOneWayToSource
{
get { return (_flags & PrivateFlags.iPropagationMask) == PrivateFlags.iTargetToSource; }
}
/// True if this binding expression updates on PropertyChanged
internal bool IsUpdateOnPropertyChanged
{
get { return (_flags & PrivateFlags.iUpdateMask) == 0; }
}
/// True if this binding expression updates on LostFocus
internal bool IsUpdateOnLostFocus
{
get { return TestFlag(PrivateFlags.iUpdateOnLostFocus); }
}
/// True if this binding expression has a pending target update
internal bool IsTransferPending
{
get { return TestFlag(PrivateFlags.iTransferPending); }
set { ChangeFlag(PrivateFlags.iTransferPending, value); }
}
/// True if this binding expression is deferring a target update
internal bool TransferIsDeferred
{
get { return TestFlag(PrivateFlags.iTransferDeferred); }
set { ChangeFlag(PrivateFlags.iTransferDeferred, value); }
}
/// True if this binding expression is updating the target
internal bool IsInTransfer
{
get { return TestFlag(PrivateFlags.iInTransfer); }
set { ChangeFlag(PrivateFlags.iInTransfer, value); }
}
/// True if this binding expression is updating the source
internal bool IsInUpdate
{
get { return TestFlag(PrivateFlags.iInUpdate); }
set { ChangeFlag(PrivateFlags.iInUpdate, value); }
}
/// True if this binding expression is using the fallback value
internal bool UsingFallbackValue
{
get { return TestFlag(PrivateFlags.iUsingFallbackValue); }
set { ChangeFlag(PrivateFlags.iUsingFallbackValue, value); }
}
/// True if this binding expression uses the mentor of the target element
internal bool UsingMentor
{
get { return TestFlag(PrivateFlags.iUsingMentor); }
set { ChangeFlag(PrivateFlags.iUsingMentor, value); }
}
/// True if this binding expression should resolve ElementName within the template of the target element
internal bool ResolveNamesInTemplate
{
get { return TestFlag(PrivateFlags.iResolveNamesInTemplate); }
set { ChangeFlag(PrivateFlags.iResolveNamesInTemplate, value); }
}
/// True if this binding expression has a pending target update
internal bool NeedsDataTransfer
{
get { return TestFlag(PrivateFlags.iNeedDataTransfer); }
set { ChangeFlag(PrivateFlags.iNeedDataTransfer, value); }
}
/// True if this binding expression has a pending source update
internal bool NeedsUpdate
{
get { return TestFlag(PrivateFlags.iNeedUpdate); }
set
{
ChangeFlag(PrivateFlags.iNeedUpdate, value);
if (value)
{
NeedsValidation = true;
}
}
}
/// True if this binding expression needs validation
internal bool NeedsValidation
{
get { return TestFlag(PrivateFlags.iNeedsValidation); }
set { ChangeFlag(PrivateFlags.iNeedsValidation, value); }
}
/// True if this binding expression should raise the TargetUpdated event
internal bool NotifyOnTargetUpdated
{
get { return TestFlag(PrivateFlags.iNotifyOnTargetUpdated); }
set { ChangeFlag(PrivateFlags.iNotifyOnTargetUpdated, value); }
}
/// True if this binding expression should raise the SourceUpdated event
internal bool NotifyOnSourceUpdated
{
get { return TestFlag(PrivateFlags.iNotifyOnSourceUpdated); }
set { ChangeFlag(PrivateFlags.iNotifyOnSourceUpdated, value); }
}
/// True if this binding expression should raise the ValidationError event
internal bool NotifyOnValidationError
{
get { return TestFlag(PrivateFlags.iNotifyOnValidationError); }
set { ChangeFlag(PrivateFlags.iNotifyOnValidationError, value); }
}
/// True if this binding expression belongs to a PriorityBinding
internal bool IsInPriorityBindingExpression
{
get { return TestFlag(PrivateFlags.iInPriorityBindingExpression); }
}
/// True if this binding expression belongs to a MultiBinding
internal bool IsInMultiBindingExpression
{
get { return TestFlag(PrivateFlags.iInMultiBindingExpression); }
}
/// True if this binding expression belongs to a PriorityBinding or MultiBinding
internal bool IsInBindingExpressionCollection
{
get { return TestFlag(PrivateFlags.iInPriorityBindingExpression | PrivateFlags.iInMultiBindingExpression); }
}
/// True if this binding expression validates on exceptions
internal bool ValidatesOnExceptions
{
get { return TestFlag(PrivateFlags.iValidatesOnExceptions); }
}
/// True if this binding expression validates on data errors
internal bool ValidatesOnDataErrors
{
get { return TestFlag(PrivateFlags.iValidatesOnDataErrors); }
}
/// The parent MultiBindingExpression (if any)
internal MultiBindingExpression ParentMultiBindingExpression
{
get { return _parentBindingExpression as MultiBindingExpression; }
}
/// The parent PriorityBindingExpression (if any)
internal PriorityBindingExpression ParentPriorityBindingExpression
{
get { return _parentBindingExpression as PriorityBindingExpression; }
}
/// The parent PriorityBindingExpression or MultiBindingExpression (if any)
internal BindingExpressionBase ParentBindingExpressionBase
{
get { return _parentBindingExpression; }
}
/// The FallbackValue (from the parent Binding), possibly converted
/// to a type suitable for the target property.
internal object FallbackValue
{
// perf note: we recompute the value every time it's needed. This is
// a good decision if we seldom need the value. Alternatively we could
// cache it. Wait until we know what the perf impact really is.
get { return ConvertFallbackValue(ParentBindingBase.FallbackValue, TargetProperty, this); }
}
/// The default value of the target property
internal object DefaultValue
{
// perf note: we recompute the value every time it's needed. This is
// a good decision if we seldom need the value. Alternatively we could
// cache it. Wait until we know what the perf impact really is.
get
{
DependencyObject target = TargetElement;
if (target != null)
{
return TargetProperty.GetDefaultValue(target.DependencyObjectType);
}
return DependencyProperty.UnsetValue;
}
}
/// The effective string format, taking into account the target
/// property, parent bindings, convenience syntax, etc.
internal string EffectiveStringFormat
{
get { return _effectiveStringFormat; }
}
/// The effective TargetNullValue, taking into account the target
/// property, parent bindings, etc.
internal object EffectiveTargetNullValue
{
get { return _effectiveTargetNullValue; }
}
/// return the root of the tree of {Multi/Priority}BindingExpressions
internal BindingExpressionBase RootBindingExpression
{
get
{
BindingExpressionBase child = this;
BindingExpressionBase parent = this.ParentBindingExpressionBase;
while (parent != null)
{
child = parent;
parent = child.ParentBindingExpressionBase;
}
return child;
}
}
/// return the binding group to which this expression belongs (or null)
internal BindingGroup BindingGroup
{
get
{
BindingExpressionBase root = RootBindingExpression;
WeakReference wr = root._bindingGroup;
return (wr == null) ? null : (BindingGroup)wr.Target;
}
}
internal virtual bool IsParentBindingUpdateTriggerDefault
{
get { return false; }
}
//-----------------------------------------------------
//
// Protected Methods
//
//------------------------------------------------------
///
/// Attach the binding expression to the given target object and property.
/// Derived classes should call base.AttachOverride before doing their work.
///
internal virtual void AttachOverride(DependencyObject target, DependencyProperty dp)
{
_targetElement = new WeakReference(target);
_targetProperty = dp;
// get the engine
_engine = DataBindEngine.CurrentDataBindEngine;
DetermineEffectiveStringFormat();
DetermineEffectiveTargetNullValue();
if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.Attach))
{
TraceData.Trace(TraceEventType.Warning,
TraceData.AttachExpression(
TraceData.Identify(this),
target.GetType().FullName, dp.Name, AvTrace.GetHashCodeHelper(target)));
}
}
///
/// Detach the binding expression from its target object and property.
/// Derived classes should call base.DetachOverride after doing their work.
///
internal virtual void DetachOverride()
{
_engine = null;
_targetElement = null;
_targetProperty = null;
SetStatus(BindingStatus.Detached);
if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.Attach))
{
TraceData.Trace(TraceEventType.Warning,
TraceData.DetachExpression(
TraceData.Identify(this)));
}
}
///
/// Invalidate the given child expression.
///
internal abstract void InvalidateChild(BindingExpressionBase bindingExpression);
///
/// Change the dependency sources for the given child expression.
///
internal abstract void ChangeSourcesForChild(BindingExpressionBase bindingExpression, WeakDependencySource[] newSources);
///
/// Replace the given child expression with a new one.
///
internal abstract void ReplaceChild(BindingExpressionBase bindingExpression);
// handle joining or leaving a binding group
internal void OnBindingGroupChanged(bool joining)
{
if (joining)
{
// joining a binding group:
// update Explicitly, unless declared otherwise
if (IsParentBindingUpdateTriggerDefault)
{
if (IsUpdateOnLostFocus)
{
LostFocusEventManager.RemoveListener(TargetElement, this);
}
SetUpdateSourceTrigger(UpdateSourceTrigger.Explicit);
}
}
else
{
// leaving a binding group:
// restore update trigger
if (IsParentBindingUpdateTriggerDefault)
{
FrameworkPropertyMetadata fwMetaData = TargetProperty.GetMetadata(TargetElement.DependencyObjectType) as FrameworkPropertyMetadata;
UpdateSourceTrigger ust = GetDefaultUpdateSourceTrigger(fwMetaData);
SetUpdateSourceTrigger(ust);
if (IsUpdateOnLostFocus)
{
LostFocusEventManager.AddListener(TargetElement, this);
}
}
}
}
// register the leaf bindings with the binding group
internal abstract void UpdateBindingGroup(BindingGroup bg);
// transfer a value from target to source
internal void UpdateValue()
{
UpdateValidationError(null);
object value = GetRawProposedValue();
if (!Validate(value, ValidationStep.RawProposedValue))
return;
value = ConvertProposedValue(value);
if (!Validate(value, ValidationStep.ConvertedProposedValue))
return;
value = UpdateSource(value);
if (!Validate(value, ValidationStep.UpdatedValue))
return;
value = CommitSource(value);
if (!Validate(value, ValidationStep.CommittedValue))
return;
EndSourceUpdate();
}
///
/// Get the raw proposed value
///
internal virtual object GetRawProposedValue()
{
object value = Value;
// TargetNullValue is the UI representation of a "null" value. Use null internally.
if (Object.Equals(value, EffectiveTargetNullValue))
{
value = null;
}
return value;
}
///
/// Get the converted proposed value
///
internal abstract object ConvertProposedValue(object rawValue);
///
/// Get the converted proposed value and inform the binding group
///
internal abstract void ObtainConvertedProposedValue(BindingGroup bindingGroup);
///
/// Update the source value
///
internal abstract object UpdateSource(object convertedValue);
///
/// Update the source value and inform the binding group
///
internal abstract void UpdateSource(BindingGroup bindingGroup);
///
/// Commit the source value
///
internal virtual object CommitSource(object value)
{
return value;
}
///
/// Store the value in the binding group
///
internal abstract void StoreValueInBindingGroup(object value, BindingGroup bindingGroup);
///
/// Run validation rules for the given step
///
internal virtual bool Validate(object value, ValidationStep validationStep)
{
if (value == Binding.DoNothing || value == DependencyProperty.UnsetValue)
return true;
// clear errors from this step - we're about to reevaluate those rules
ClearValidationErrors(validationStep);
Collection validationRules = ParentBindingBase.ValidationRulesInternal;
if (validationRules != null)
{
CultureInfo culture = GetCulture();
switch (validationStep)
{
case ValidationStep.UpdatedValue:
case ValidationStep.CommittedValue:
// rules at these steps get passed the rule owner
value = this;
break;
}
foreach (ValidationRule validationRule in validationRules)
{
if (validationRule.ValidationStep == validationStep)
{
ValidationResult validationResult = validationRule.Validate(value, culture);
if (!validationResult.IsValid)
{
if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.Update))
{
TraceData.Trace(TraceEventType.Warning,
TraceData.ValidationRuleFailed(
TraceData.Identify(this),
TraceData.Identify(validationRule)));
}
UpdateValidationError( new ValidationError(validationRule, this, validationResult.ErrorContent, null));
return false; // kenlai:
}
}
}
}
return true;
}
///
/// Run validation rules for the given step, and inform the binding group
///
internal abstract bool CheckValidationRules(BindingGroup bindingGroup, ValidationStep validationStep);
///
/// Compute the culture, either from the parent Binding, or from the target element.
///
internal CultureInfo GetCulture()
{
// lazy initialization, to let the target element acquire all its properties
if (_culture == DefaultValueObject)
{
// explicit culture set in Binding
_culture = ParentBindingBase.ConverterCultureInternal;
// if that doesn't work, use target element's xml:lang property
if (_culture == null)
{
DependencyObject target = TargetElement;
if (target != null)
{
if (IsInTransfer && (TargetProperty == FrameworkElement.LanguageProperty))
{
// A binding for the Language property needs the value
// of the Language property. This circularity is not
// supported (bug 1274874).
if (TraceData.IsEnabled)
{
TraceData.Trace(TraceEventType.Critical, TraceData.RequiresExplicitCulture, TargetProperty.Name, this);
}
throw new InvalidOperationException(SR.Get(SRID.RequiresExplicitCulture, TargetProperty.Name));
}
// cache CultureInfo since requerying an inheritable property on every Transfer/Update can be quite expensive
// CultureInfo DP rarely changes once a XAML document is loaded.
// To be 100% correct, changes to the CultureInfo attached DP should be tracked
// and cause a re-evaluation of this binding.
_culture = ((XmlLanguage) target.GetValue(FrameworkElement.LanguageProperty)).GetSpecificCulture();
}
}
}
return (CultureInfo)_culture;
}
/// Begin a source update
internal void BeginSourceUpdate()
{
ChangeFlag(PrivateFlags.iInUpdate, true);
}
/// End a source update
internal void EndSourceUpdate()
{
ChangeFlag(PrivateFlags.iInUpdate | PrivateFlags.iNeedUpdate, false);
}
/// change the value to the new value, and notify listeners
internal void ChangeValue(object newValue, bool notify)
{
object oldValue = (_value != DefaultValueObject) ? _value : DependencyProperty.UnsetValue;
_value = newValue;
if (notify && ValueChanged != null)
{
ValueChanged(this, new BindingValueChangedEventArgs(oldValue, newValue));
}
}
/// the target value has changed - the source needs to be updated
internal void Dirty()
{
if (!IsInTransfer)
{
NeedsUpdate = true;
if (IsUpdateOnPropertyChanged)
Update(true);
}
}
/// use the Fallback or Default value, called when a real value is not available
internal object UseFallbackValue()
{
object value = FallbackValue;
// if there's a problem with the fallback, use Default instead
if (value == DefaultValueObject)
{
value = DependencyProperty.UnsetValue;
}
if (value != DependencyProperty.UnsetValue)
{
UsingFallbackValue = true;
}
else
{
// if fallback isn't available, use Default (except when in a binding collection)
if (Status == BindingStatus.Active)
SetStatus(BindingStatus.UpdateTargetError);
if (!IsInBindingExpressionCollection)
{
value = DefaultValue;
if (TraceData.IsEnabled)
{
TraceData.Trace(TraceEventType.Information, TraceData.NoValueToTransfer, this);
}
}
}
return value;
}
// determine if the given value is "null" (in a general sense)
internal bool IsNullValue(object value)
{
if (value == null)
return true;
if (Convert.IsDBNull(value))
return true;
if (IsSqlNull(value))
return true;
return false;
}
// return true if the value is null in the SqlTypes sense
bool IsSqlNull(object value)
{
Type type = value.GetType();
return Engine.IsINullable(type) && IsSqlNullImpl(value);
}
// separate function to avoid loading System.Data unnecessarily
[System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
static bool IsSqlNullImpl(object value)
{
System.Data.SqlTypes.INullable nullable = value as System.Data.SqlTypes.INullable;
return (nullable != null && nullable.IsNull);
}
// determine a "null" value appropriate for the given type
internal object NullValueForType(Type type)
{
if (type == null)
return null;
object sqlNull;
if (IsSqlNullableType(type, out sqlNull))
return sqlNull;
if (!type.IsValueType)
return null;
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
return null;
return DependencyProperty.UnsetValue;
}
// return true if the type is nullable in the SqlTypes sense. If so, return the null value.
bool IsSqlNullableType(Type type, out object nullValue)
{
if (Engine.IsINullable(type))
{
return IsSqlNullableTypeImpl(type, out nullValue);
}
else
{
nullValue = null;
return false;
}
}
// separate function to avoid loading System.Data unnecessarily
[System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
static bool IsSqlNullableTypeImpl(Type type, out object nullValue)
{
// some SqlTypes are classes with a Null property. Others are structs with a Null field. Try both.
System.Reflection.FieldInfo nullField = type.GetField("Null", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.FlattenHierarchy);
if (nullField != null)
{
nullValue = nullField.GetValue(null);
return true;
}
System.Reflection.PropertyInfo nullProperty = type.GetProperty("Null", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.FlattenHierarchy);
if (nullProperty != null)
{
nullValue = nullProperty.GetValue(null, null);
return true;
}
nullValue = null;
return false;
}
internal ValidationRule LookupValidationRule(Type type)
{
ValidationRule result = ParentBindingBase.GetValidationRule(type);
if (result == null && _parentBindingExpression != null)
{
result = _parentBindingExpression.LookupValidationRule(type);
}
return result;
}
// discover the binding group (if any) that this binding should join,
// and join it. More precisely, cause the root binding to join.
internal void JoinBindingGroup(bool isReflective, DependencyObject contextElement)
{
BindingGroup bindingGroup = RootBindingExpression.FindBindingGroup(isReflective, contextElement);
if (bindingGroup != null)
{
JoinBindingGroup(bindingGroup, /*explicit*/false);
}
}
// remove the binding from its group
internal void LeaveBindingGroup()
{
BindingExpressionBase root = RootBindingExpression;
BindingGroup bg = root.BindingGroup;
if (bg != null)
{
bg.BindingExpressions.Remove(root);
root._bindingGroup = null;
}
}
// discover the binding group (if any) that this root binding should join.
BindingGroup FindBindingGroup(bool isReflective, DependencyObject contextElement)
{
Debug.Assert(RootBindingExpression == this, "Only call this with a root binding");
// if we've already joined (or failed to join) a group, just return the result
if (_bindingGroup != null)
{
return (BindingGroup)_bindingGroup.Target;
}
BindingGroup bg;
string groupName = ParentBindingBase.BindingGroupName;
// a null group name means "don't join any group".
if (groupName == null)
{
MarkAsNonGrouped();
return null;
}
// an empty group name means join by DataContext
if (String.IsNullOrEmpty(groupName))
{
// check further preconditions:
if (!isReflective || // must have target-to-source data flow
contextElement == null) // must use data context
{
// later child bindings might pass this test, so don't mark
// this root binding as non-grouped yet
return null;
}
// only the innermost binding group is eligible
bg = (BindingGroup)contextElement.GetValue(FrameworkElement.BindingGroupProperty);
if (bg == null)
{
MarkAsNonGrouped();
return null;
}
// the context element must share data context with the group
DependencyProperty dataContextDP = FrameworkElement.DataContextProperty;
DependencyObject groupContextElement = bg.InheritanceContext;
if (groupContextElement == null ||
!Object.Equals( contextElement.GetValue(dataContextDP),
groupContextElement.GetValue(dataContextDP)))
{
MarkAsNonGrouped();
return null;
}
// if the binding survives the gauntlet, return the group
return bg;
}
// a non-empty group name means join by name
else
{
// walk up the tree, looking for a matching binding group
DependencyProperty bindingGroupDP = FrameworkElement.BindingGroupProperty;
FrameworkObject fo = new FrameworkObject(TargetElement);
while (fo.DO != null)
{
BindingGroup bgCandidate = (BindingGroup)fo.DO.GetValue(bindingGroupDP);
if (bgCandidate == null)
{
MarkAsNonGrouped();
return null;
}
if (bgCandidate.Name == groupName)
{
// return the matching group
return bgCandidate;
}
fo = fo.FrameworkParent;
}
// no match - report an error
if (TraceData.IsEnabled)
{
TraceData.Trace(TraceEventType.Error,
TraceData.BindingGroupNameMatchFailed(groupName),
this);
}
MarkAsNonGrouped();
return null;
}
}
// add a binding to the given binding group
internal void JoinBindingGroup(BindingGroup bg, bool explicitJoin)
{
BindingExpressionBase root = null; // set to non-null by the next loop
for ( BindingExpressionBase bindingExpr = this;
bindingExpr != null;
bindingExpr = bindingExpr.ParentBindingExpressionBase)
{
root = bindingExpr;
// bindings in a group update Explicitly, unless declared otherwise
bindingExpr.OnBindingGroupChanged(/*joining*/true);
bg.AddToValueTable(bindingExpr);
}
// add the root binding to the group
if (root._bindingGroup == null)
{
// use WeakReference because the BindingGroup contains a strong reference
// to the visual tree (via InheritanceContext)
root._bindingGroup = new WeakReference(bg);
// when the group is implicitly discovered, always add the root binding to the group's collection.
// When the binding is added explicitly - via BindingGroup.BindingExpressions.Add() -
// check first to see if it has already been added
bool addToGroup = explicitJoin ? !bg.BindingExpressions.Contains(root) : true;
if (addToGroup)
{
bg.BindingExpressions.Add(root);
}
// in the explicit case, register its items and values with the binding group
// (the implicit case does this when the bindings activate)
if (explicitJoin)
{
root.UpdateBindingGroup(bg);
}
}
else
{
if (root.BindingGroup != bg)
throw new InvalidOperationException(SR.Get(SRID.BindingGroup_CannotChangeGroups));
}
}
// mark a binding as non-grouped, so that we avoid doing the discovery again
void MarkAsNonGrouped()
{
// Leaf bindings only get asked once, so there's no need to add a mark
if (!(this is BindingExpression))
{
_bindingGroup = new WeakReference(null);
}
}
///
/// Handle events from the centralized event table
///
internal virtual bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
{
return false; // unrecognized event
}
private bool TestFlag(PrivateFlags flag)
{
return (_flags & flag) != 0;
}
private void ChangeFlag(PrivateFlags flag, bool value)
{
if (value) _flags |= flag;
else _flags &= ~flag;
}
//-----------------------------------------------------
//
// Internal Properties
//
//------------------------------------------------------
internal DependencyProperty TargetProperty { get { return _targetProperty; } }
// A BindingExpression cannot hold a strong reference to the target element - this
// leads to memory leaks (bug 871139). The problem is that BindingExpression and its workers
// register for events from the data item, creating a reference from
// the data item to the BindingExpression. The data item typically has a long lifetime,
// so if the BindingExpression held a SR to the target, the target element would
// also stay alive forever.
// Instead, BindingExpression holds a WeakReference to the target. This means we
// have to check it before dereferencing (here), and cope when the
// reference fails (in callers to this property). Requests for the TargetElement
// are not trivial, so callers should request it once and cache the result
// in a local variable. They should not save it in a global or instance
// variable of course; that would defeat the purpose of the WR.
// This allows the target element to be GC'd when it's no longer in
// use by the tree or application. The next time the BindingExpression asks for
// its TargetElement, the WR will fail. At this point, the BindingExpression is no
// longer useful, so it can sever all connections to the outside world (i.e.
// stop listening for events). This allows the BindingExpression itself to be GC'd.
internal DependencyObject TargetElement
{
get
{
if (_targetElement != null)
{
DependencyObject result = _targetElement.Target as DependencyObject;
if (result != null)
return result;
// target has been GC'd, sever all connections
_targetElement = null; // prevents re-entry from Detach()
Detach();
}
return null;
}
}
internal WeakReference TargetElementReference
{
get { return _targetElement; }
}
internal DataBindEngine Engine
{
get { return _engine; }
}
internal Dispatcher Dispatcher
{
get { return (_engine != null) ? _engine.Dispatcher : null; }
}
internal object Value
{
get
{
if (_value == DefaultValueObject)
{
// don't notify listeners. This isn't a real value change.
ChangeValue(UseFallbackValue(), false /*notify*/);
}
return _value;
}
set
{
ChangeValue(value, true);
Dirty();
}
}
internal WeakDependencySource[] WeakSources
{
get { return _sources; }
}
///
/// NoTarget DependencyProperty, a placeholder used by BindingExpressions with no target property
///
internal static readonly DependencyProperty NoTargetProperty =
DependencyProperty.RegisterAttached("NoTarget", typeof(object), typeof(BindingExpressionBase),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None));
internal TraceLog TraceLog { get { return _traceLog; } }
//------------------------------------------------------
//
// Internal Methods
//
//-----------------------------------------------------
///
/// Attach the binding expression to the given target object and property.
///
internal void Attach(DependencyObject target, DependencyProperty dp)
{
// make sure we're on the right thread to access the target
if (target != null)
{
target.VerifyAccess();
}
IsAttaching = true;
AttachOverride(target, dp);
IsAttaching = false;
}
///
/// Detach the binding expression from its target object and property.
///
internal void Detach()
{
if (_status == BindingStatus.Detached || IsDetaching)
return;
IsDetaching = true;
DetachOverride();
IsDetaching = false;
}
internal void SetStatus(BindingStatus status)
{
if (_status == BindingStatus.Detached && status != _status)
{
throw new InvalidOperationException(SR.Get(SRID.BindingExpressionStatusChanged, _status, status));
}
_status = status;
if (_traceLog != null)
{
_traceLog.Add("Set status = {0} {1}", _status, new StackTrace());
}
}
// convert a user-supplied fallback value to a usable equivalent
// returns: UnsetValue if user did not supply a fallback value
// value if fallback value is legal
// DefaultValueObject otherwise
internal static object ConvertFallbackValue(object value, DependencyProperty dp, object sender)
{
Exception e;
object result = ConvertValue(value, dp, out e);
if (result == DefaultValueObject)
{
if (TraceData.IsEnabled)
{
TraceData.Trace(TraceEventType.Error,
TraceData.FallbackConversionFailed(
AvTrace.ToStringHelper(value),
AvTrace.TypeName(value),
dp.Name,
dp.PropertyType.Name),
sender, e);
}
}
return result;
}
// convert a user-supplied TargetNullValue to a usable equivalent
// returns: UnsetValue if user did not supply a fallback value
// value if fallback value is legal
// DefaultValueObject otherwise
internal static object ConvertTargetNullValue(object value, DependencyProperty dp, object sender)
{
Exception e;
object result = ConvertValue(value, dp, out e);
if (result == DefaultValueObject)
{
if (TraceData.IsEnabled)
{
TraceData.Trace(TraceEventType.Error,
TraceData.TargetNullValueConversionFailed(
AvTrace.ToStringHelper(value),
AvTrace.TypeName(value),
dp.Name,
dp.PropertyType.Name),
sender, e);
}
}
return result;
}
static object ConvertValue(object value, DependencyProperty dp, out Exception e)
{
object result;
e = null;
if (value == DependencyProperty.UnsetValue || dp.IsValidValue(value))
{
result = value;
}
else
{
result = null; // placeholder to keep compiler happy
// if value isn't the right type, use a type converter to get a better value
bool success = false;
TypeConverter converter = DefaultValueConverter.GetConverter(dp.PropertyType);
if (converter != null && converter.CanConvertFrom(value.GetType()))
{
// PreSharp uses message numbers that the C# compiler doesn't know about.
// Disable the C# complaints, per the PreSharp documentation.
#pragma warning disable 1634, 1691
// PreSharp complains about catching NullReference (and other) exceptions.
// It doesn't recognize that IsCriticalException() handles these correctly.
#pragma warning disable 56500
try
{
result = converter.ConvertFrom(null, CultureInfo.InvariantCulture, value);
success = dp.IsValidValue(result);
}
// Catch all exceptions. If we can't convert the fallback value, it doesn't
// matter why not; we should always use the default value instead.
// (See bug 1853628 for an example of a converter that throws
// an exception not mentioned in the documentation for ConvertFrom.)
catch (Exception ex)
{
e = ex;
}
catch // non CLS compliant exception
{
}
#pragma warning restore 56500
#pragma warning restore 1634, 1691
}
if (!success)
{
// if can't convert it, don't use it
result = DefaultValueObject;
}
}
return result;
}
// Certain trace reports should be marked as 'error' unless the binding
// is prepared to handle it in some way (e.g. FallbackValue), in which
// case 'warning'.
internal TraceEventType TraceLevel
{
get
{
// FallbackValue is present
if (ParentBindingBase.FallbackValue != DependencyProperty.UnsetValue)
return TraceEventType.Warning;
// Binding is a member of MultiBinding or PriorityBinding
if (IsInBindingExpressionCollection)
return TraceEventType.Warning;
// all other cases - error
return TraceEventType.Error;
}
}
internal virtual void Activate()
{
}
internal virtual void Deactivate()
{
}
internal virtual void Update(bool synchronous)
{
}
internal void UpdateValidationError(ValidationError validationError)
{
if (_validationError != null)
{
ValidationError oldValidationError = _validationError;
_validationError = null; // clear before raising events, so that HasError is correct
RemoveValidationError(oldValidationError);
}
_validationError = validationError;
if (_validationError != null)
{
AddValidationError(_validationError);
}
}
internal void AddValidationError(ValidationError validationError)
{
// add the error to the target element
Validation.AddValidationError(validationError, TargetElement, NotifyOnValidationError);
// add the error to the binding group's target element
BindingGroup bindingGroup = BindingGroup;
if (bindingGroup != null)
{
bindingGroup.AddValidationError(validationError);
}
}
internal void RemoveValidationError(ValidationError validationError)
{
// remove the error from the target element
Validation.RemoveValidationError(validationError, TargetElement, NotifyOnValidationError);
// remove the error from the binding group's target element
BindingGroup bindingGroup = BindingGroup;
if (bindingGroup != null)
{
bindingGroup.RemoveValidationError(validationError);
}
}
// remove all errors raised at the given step, in preparation for running
// the rules at that step
internal void ClearValidationErrors(ValidationStep validationStep)
{
if (_validationError == null || _validationError.RuleInError.ValidationStep != validationStep)
return;
RemoveValidationError(_validationError);
_validationError = null;
}
internal void ChangeSources(WeakDependencySource[] newSources)
{
if (IsInBindingExpressionCollection)
ParentBindingExpressionBase.ChangeSourcesForChild(this, newSources);
else
ChangeSources(TargetElement, TargetProperty, newSources);
// store the sources with weak refs, so they don't cause memory leaks (bug 980041)
_sources = newSources;
}
///
/// combine the sources of BindingExpressions, using new sources for
/// the BindingExpression at the given index
///
/// -1 to indicate no new sources
/// collection of child binding expressions
/// how many child expressions to include
/// use null when no new sources
///
internal static WeakDependencySource[] CombineSources(int index, Collection bindingExpressions, int count, WeakDependencySource[] newSources)
{
if (index == count)
{
// Be sure to include newSources if they are being appended
count++;
}
Collection tempList = new Collection();
for (int i = 0; i < count; ++i)
{
BindingExpressionBase bindExpr = bindingExpressions[i];
WeakDependencySource[] sources = (i==index) ? newSources :
(bindExpr != null) ? bindExpr.WeakSources :
null;
int m = (sources == null) ? 0 : sources.Length;
for (int j = 0; j < m; ++j)
{
WeakDependencySource candidate = sources[j];
// don't add duplicate source
for (int k = 0; k < tempList.Count; ++k)
{
WeakDependencySource prior = tempList[k];
if (candidate.DependencyObject == prior.DependencyObject &&
candidate.DependencyProperty == prior.DependencyProperty)
{
candidate = null;
break;
}
}
if (candidate != null)
tempList.Add(candidate);
}
}
WeakDependencySource[] result;
if (tempList.Count > 0)
{
result = new WeakDependencySource[tempList.Count];
tempList.CopyTo(result, 0);
tempList.Clear();
}
else
{
result = null;
}
return result;
}
internal void ResolvePropertyDefaultSettings(BindingMode mode, UpdateSourceTrigger updateTrigger, FrameworkPropertyMetadata fwMetaData)
{
// resolve "property-default" dataflow
if (mode == BindingMode.Default)
{
BindingFlags f = BindingFlags.OneWay;
if (fwMetaData != null && fwMetaData.BindsTwoWayByDefault)
{
f = BindingFlags.TwoWay;
}
ChangeFlag(PrivateFlags.iPropagationMask, false);
ChangeFlag((PrivateFlags)f, true);
if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.ResolveDefaults))
{
TraceData.Trace(TraceEventType.Warning,
TraceData.ResolveDefaultMode(
TraceData.Identify(this),
(f == BindingFlags.OneWay) ? BindingMode.OneWay : BindingMode.TwoWay));
}
}
Debug.Assert((_flags & PrivateFlags.iPropagationMask) != PrivateFlags.iPropDefault,
"BindingExpression should not have Default propagation");
// resolve "property-default" update trigger
if (updateTrigger == UpdateSourceTrigger.Default)
{
UpdateSourceTrigger ust = GetDefaultUpdateSourceTrigger(fwMetaData);
SetUpdateSourceTrigger(ust);
if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.ResolveDefaults))
{
TraceData.Trace(TraceEventType.Warning,
TraceData.ResolveDefaultUpdate(
TraceData.Identify(this),
ust));
}
}
Invariant.Assert((_flags & PrivateFlags.iUpdateMask) != PrivateFlags.iUpdateDefault,
"BindingExpression should not have Default update trigger");
}
// return the effective update trigger, used when binding doesn't set one explicitly
internal UpdateSourceTrigger GetDefaultUpdateSourceTrigger(FrameworkPropertyMetadata fwMetaData)
{
UpdateSourceTrigger ust =
IsInMultiBindingExpression ? UpdateSourceTrigger.Explicit :
(fwMetaData != null) ? fwMetaData.DefaultUpdateSourceTrigger :
UpdateSourceTrigger.PropertyChanged;
return ust;
}
internal void SetUpdateSourceTrigger(UpdateSourceTrigger ust)
{
ChangeFlag(PrivateFlags.iUpdateMask, false);
ChangeFlag((PrivateFlags)BindingBase.FlagsFrom(ust), true);
}
internal void DetermineEffectiveStringFormat()
{
Type targetType = TargetProperty.PropertyType;
if (targetType != typeof(String))
{
// if the target type isn't String, we don't need a string format
return; // _effectiveStringFormat is already null
}
// determine the effective target type and the declared string format
// by looking up the tree of binding expressions
string stringFormat = ParentBindingBase.StringFormat;
BindingExpressionBase be = this.ParentBindingExpressionBase;
while (be != null)
{
if (be is MultiBindingExpression)
{
// MultiBindings should receive object values, not string
targetType = typeof(Object);
break;
}
else if (stringFormat == null && be is PriorityBindingExpression)
{
// use a PriorityBinding's string format, unless we already
// have a more specific one
stringFormat = be.ParentBindingBase.StringFormat;
}
be = be.ParentBindingExpressionBase;
}
// if we need a string format, cache it
if (targetType == typeof(String))
{
#if NotToday
// special case: when these conditions all apply
// a) target element belongs to a DataTemplate
// b) template was found by implicit (type-based) lookup
// c) container (ContentPresenter) has a string format
// d) binding has no effective string format
// then use the CP's string format. This enables scenarios like
//
//
//
// where you'd like to control the format at the point of use,
// rather than at the point of template declaration. This is
// especially useful when the template is declared in a global place
// like app resources or a theme file. In particular it makes the
// GroupStyle.HeaderStringFormat property work; the template for
// GroupItem is defined in the theme, but needs to pick up formatting
// from the app's markup.
if (stringFormat == null) // (d)
{
FrameworkObject fo = new FrameworkObject(TargetElement);
ContentPresenter cp = fo.TemplatedParent as ContentPresenter;
if (cp != null && // (a)
cp.ContentStringFormat != null) // (c)
{
DataTemplate dt = cp.TemplateInternal as DataTemplate;
if (dt != null && // (a)
dt.DataType != null) // (b)
{
stringFormat = cp.ContentStringFormat;
}
}
}
#endif
if (!String.IsNullOrEmpty(stringFormat))
{
_effectiveStringFormat = Helper.GetEffectiveStringFormat(stringFormat);
}
}
}
internal void DetermineEffectiveTargetNullValue()
{
Type targetType = TargetProperty.PropertyType;
// determine the effective target type and the declared TargetNullValue
// by looking up the tree of binding expressions
object targetNullValue = ParentBindingBase.TargetNullValue;
BindingExpressionBase be = this.ParentBindingExpressionBase;
while (be != null)
{
if (be is MultiBindingExpression)
{
// MultiBindings should receive object values
targetType = typeof(Object);
break;
}
else if (targetNullValue == DependencyProperty.UnsetValue && be is PriorityBindingExpression)
{
// use a PriorityBinding's TargetNullValue, unless we already
// have a more specific one
targetNullValue = be.ParentBindingBase.TargetNullValue;
}
be = be.ParentBindingExpressionBase;
}
// if user declared a TargetNullValue, make sure it has the right type.
if (targetNullValue != DependencyProperty.UnsetValue)
{
targetNullValue = ConvertTargetNullValue(targetNullValue, TargetProperty, this);
if (targetNullValue == DefaultValueObject)
{
// if not, ignore it (having logged a trace message)
targetNullValue = DependencyProperty.UnsetValue;
}
}
// for back-compat, don't turn on TargetNullValue unless user explicitly
// asks for it. This means users have to add TargetNullValue to get
// pretty basic scenarios to work (e.g. binding a TextBox to a database
// string field that supports DBNull). It's painful, but can't be
// helped.
#if TargetNullValueBC //BreakingChange
// if no declared (or poorly declared) value, make one up
if (targetNullValue == DependencyProperty.UnsetValue)
{
targetNullValue = NullValueForType(targetType);
}
#endif
_effectiveTargetNullValue = targetNullValue;
}
// To prevent memory leaks, we store WeakReferences to certain objects
// in various places: _dataItem, _sources, worker fields. The logic
// for this is centralized in these two static methods. (See bug 940041)
internal static object CreateReference(object item)
{
// One source of leaks is the reference cycle formed when a BindingExpression's
// source item contains a reference chain to the target element:
// target -> BindingExpression -> source item -> target
//
// Making the second link into a WeakReference incurs some cost,
// so it should be avoided if we know the third link never exists.
// We definitely can't avoid this when the item is a DependencyObject,
// since it's reasonably common for the third link to occur (e.g.
// a ContextMenu contains a link to its Popup, which has a property
// bound back to the ContextMenu).
//
// For safety, we choose to use WeakRef all the time, unless the item is null.
// Exception (bug 1124954): Keep a strong reference to
// BindingListCollectionView - this keeps the underlying DataView
// alive, when nobody else will.
// Exception (bug 1970505): Don't allocate a WeakRef for the common
// case of the NullDataItem
if (item != null &&
!(item is BindingListCollectionView) &&
!(item == BindingExpression.NullDataItem))
{
item = new WeakReference(item);
}
#if USE_ITEM_REFERENCE
item = new ItemReference(item);
#endif
return item;
}
// like CreateReference, but use an existing WeakReference
internal static object CreateReference(WeakReference item)
{
object result = item;
#if USE_ITEM_REFERENCE
result = new ItemReference(item);
#endif
return result;
}
// like CreateReference, except re-target the old WeakReference (if any)
internal static object ReplaceReference(object oldReference, object item)
{
if (item != null &&
!(item is BindingListCollectionView) &&
!(item == BindingExpression.NullDataItem))
{
#if USE_ITEM_REFERENCE
// if this cast fails, it's because you have done a direct assignment of an
// item to some field instead of assigning the result of CreateReference.
oldReference = ((ItemReference)oldReference).Item;
#endif
WeakReference wr = oldReference as WeakReference;
if (wr != null)
{
wr.Target = item;
item = wr;
}
else
{
item = new WeakReference(item);
}
}
#if USE_ITEM_REFERENCE
item = new ItemReference(item);
#endif
return item;
}
internal static object GetReference(object reference)
{
if (reference == null)
return null;
#if USE_ITEM_REFERENCE
// if this cast fails, it's because you have done a direct assignment of an
// item to some field instead of assigning the result of CreateReference.
reference = ((ItemReference)reference).Item;
#endif
WeakReference wr = reference as WeakReference;
if (wr != null)
return wr.Target;
else
return reference;
}
internal static void InitializeTracing(BindingExpressionBase expr, DependencyObject d, DependencyProperty dp)
{
BindingBase parent = expr.ParentBindingBase;
}
//------------------------------------------------------
//
// Private Methods
//
//-----------------------------------------------------
#if USE_ITEM_REFERENCE
private class ItemReference
{
internal ItemReference(object item)
{
_item = item;
}
internal object Item { get { return _item; } }
object _item;
}
#endif
// change WeakDependencySources to (strong) DependencySources, and notify
// the property engine about the new sources
void ChangeSources(DependencyObject target, DependencyProperty dp, WeakDependencySource[] newSources)
{
DependencySource[] sources;
if (newSources != null)
{
// convert weak reference to strong
sources = new DependencySource[newSources.Length];
int n = 0;
for (int i = 0; i < newSources.Length; ++i)
{
DependencyObject sourceDO = newSources[i].DependencyObject;
if (sourceDO != null)
{
// only include sources that are still alive
sources[n++] = new DependencySource(sourceDO, newSources[i].DependencyProperty);
}
}
// if any of the sources were no longer alive, trim the array
if (n < newSources.Length)
{
DependencySource[] temp;
if (n > 0)
{
temp = new DependencySource[n];
Array.Copy(sources, 0, temp, 0, n);
}
else
{
temp = null;
}
sources = temp;
}
}
else
{
sources = null;
}
// notify property engine
ChangeSources(target, dp, sources);
}
// this method is here just to avoid the compiler error
// error CS0649: Warning as Error: Field 'System.Windows.Data.BindingExpression._traceLog' is never assigned to, and will always have its default value null
void InitializeTraceLog()
{
_traceLog = new TraceLog(20);
}
//-----------------------------------------------------
//
// Private Fields
//
//-----------------------------------------------------
BindingBase _binding;
WeakReference _targetElement;
DependencyProperty _targetProperty;
DataBindEngine _engine;
PrivateFlags _flags;
BindingExpressionBase _parentBindingExpression;
object _value = DefaultValueObject;
BindingStatus _status;
TraceLog _traceLog;
ValidationError _validationError;
WeakDependencySource[] _sources;
string _effectiveStringFormat;
object _effectiveTargetNullValue;
WeakReference _bindingGroup;
object _culture = DefaultValueObject;
/// Sentinel meaning "field has its default value"
internal static readonly object DefaultValueObject = new object();
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- DrawingCollection.cs
- Normalization.cs
- StringPropertyBuilder.cs
- WebPartConnectionsConnectVerb.cs
- MSHTMLHost.cs
- Comparer.cs
- XmlDataFileEditor.cs
- MemberPathMap.cs
- ButtonBaseAdapter.cs
- DataGridViewTextBoxColumn.cs
- TextFormatterContext.cs
- PopupControlService.cs
- QueryConverter.cs
- AmbientProperties.cs
- RequestCacheEntry.cs
- COM2PropertyBuilderUITypeEditor.cs
- SparseMemoryStream.cs
- GenericsInstances.cs
- CoreSwitches.cs
- HttpModuleAction.cs
- ZipFileInfo.cs
- SettingsProperty.cs
- TableChangeProcessor.cs
- SafeArrayRankMismatchException.cs
- XmlBoundElement.cs
- WebPartTransformer.cs
- SmtpReplyReaderFactory.cs
- Base64Decoder.cs
- SmtpLoginAuthenticationModule.cs
- CopyNodeSetAction.cs
- NetSectionGroup.cs
- WindowProviderWrapper.cs
- WorkflowRuntimeServiceElement.cs
- ObjectDataSourceDisposingEventArgs.cs
- DataBoundControl.cs
- PersonalizationEntry.cs
- BitmapMetadataEnumerator.cs
- KnownBoxes.cs
- CodeDOMUtility.cs
- SmtpNtlmAuthenticationModule.cs
- ToolStripRendererSwitcher.cs
- JoinGraph.cs
- BasicExpandProvider.cs
- AssemblyFilter.cs
- ListSortDescriptionCollection.cs
- CryptoConfig.cs
- LoginStatusDesigner.cs
- ObjectDataSourceFilteringEventArgs.cs
- SafeNativeMethods.cs
- XmlSchemaAttribute.cs
- ImageField.cs
- formatter.cs
- Triangle.cs
- ObfuscateAssemblyAttribute.cs
- BrushValueSerializer.cs
- WebSysDescriptionAttribute.cs
- ToolStripGrip.cs
- TypeRestriction.cs
- InstanceNormalEvent.cs
- XPathNodeInfoAtom.cs
- ComponentTray.cs
- MethodImplAttribute.cs
- VerticalAlignConverter.cs
- ConnectionStringsExpressionBuilder.cs
- Page.cs
- ChangesetResponse.cs
- SchemaImporter.cs
- HierarchicalDataTemplate.cs
- NameObjectCollectionBase.cs
- PagerSettings.cs
- MergePropertyDescriptor.cs
- ProxyAttribute.cs
- Context.cs
- WebPartHeaderCloseVerb.cs
- ByteAnimationBase.cs
- NetCodeGroup.cs
- ClearTypeHintValidation.cs
- _HTTPDateParse.cs
- DrawingContextWalker.cs
- ServiceModelExtensionElement.cs
- BoundPropertyEntry.cs
- SingleTagSectionHandler.cs
- FaultHandlingFilter.cs
- DataBindingExpressionBuilder.cs
- IISUnsafeMethods.cs
- StaticSiteMapProvider.cs
- InputProcessorProfiles.cs
- UserInitiatedNavigationPermission.cs
- DataGridViewElement.cs
- DataControlFieldHeaderCell.cs
- XmlnsCompatibleWithAttribute.cs
- StrongTypingException.cs
- ProtocolsSection.cs
- IncrementalReadDecoders.cs
- TextParaLineResult.cs
- SQLUtility.cs
- ConfigXmlAttribute.cs
- SoapExtensionImporter.cs
- InternalConfigEventArgs.cs
- WindowsBrush.cs