// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. // // Description: Defines BindingBase object, common base class for Binding, // PriorityBinding, MultiBinding. // // See spec at Data Binding.mht // using System; using System.Collections.ObjectModel; // Collection using System.ComponentModel; // [DefaultValue] using System.Diagnostics; // Debug.Assert using System.Globalization; // CultureInfo using System.Windows.Markup; // MarkupExtension using System.Windows.Controls; // ValidationRule using MS.Internal; // Helper namespace System.Windows.Data { /// This enum describes how the data flows through a given Binding /// public enum BindingMode { /// Data flows from source to target and vice-versa TwoWay, /// Data flows from source to target, source changes cause data flow OneWay, /// Data flows from source to target once, source changes are ignored OneTime, /// Data flows from target to source, target changes cause data flow OneWayToSource, /// Data flow is obtained from target property default Default } /// This enum describes when updates (target-to-source data flow) /// happen in a given Binding. /// public enum UpdateSourceTrigger { /// Obtain trigger from target property default Default, /// Update whenever the target property changes PropertyChanged, /// Update only when target element loses focus, or when Binding deactivates LostFocus, /// Update only by explicit call to BindingExpression.UpdateSource() Explicit } /// /// Base class for Binding, PriorityBinding, and MultiBinding. /// [MarkupExtensionReturnType(typeof(object))] [Localizability(LocalizationCategory.None, Modifiability = Modifiability.Unmodifiable, Readability = Readability.Unreadable)] // Not localizable by-default public abstract class BindingBase: MarkupExtension { #region Constructors //------------------------------------------------------ // // Constructors // //------------------------------------------------------ static BindingBase() { Debug.Assert((int)Feature.LastFeatureId <= 32, "UncommonValueTable supports only 32 Ids"); } /// Initialize a new instance of BindingBase. /// /// This constructor can only be called by one of the built-in /// derived classes. /// internal BindingBase() { } #endregion Constructors #region Public Properties //------------------------------------------------------ // // Public Properties // //------------------------------------------------------ /// Value to use when source cannot provide a value /// /// Initialized to DependencyProperty.UnsetValue; if FallbackValue is not set, BindingExpression /// will return target property's default when Binding cannot get a real value. /// public object FallbackValue { get { return GetValue(Feature.FallbackValue, DependencyProperty.UnsetValue); } set { CheckSealed(); SetValue(Feature.FallbackValue, value); } } /// /// This method is used by TypeDescriptor to determine if this property should /// be serialized. /// [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public bool ShouldSerializeFallbackValue() { return HasValue(Feature.FallbackValue); } /// Format string used to convert the data to type String. /// /// /// This property is used when the target of the binding has type /// String and no Converter is declared. It is ignored in all other /// cases. /// [System.ComponentModel.DefaultValue(null)] public string StringFormat { get { return (string)GetValue(Feature.StringFormat, null); } set { CheckSealed(); SetValue(Feature.StringFormat, value, null); } } /// Value used to represent "null" in the target property. /// public object TargetNullValue { get { return GetValue(Feature.TargetNullValue, DependencyProperty.UnsetValue); } set { CheckSealed(); SetValue(Feature.TargetNullValue, value); } } /// /// This method is used by TypeDescriptor to determine if this property should /// be serialized. /// [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public bool ShouldSerializeTargetNullValue() { return HasValue(Feature.TargetNullValue); } /// Name of the this binding should join. /// [DefaultValue("")] public string BindingGroupName { get { return (string)GetValue(Feature.BindingGroupName, String.Empty); } set { CheckSealed(); SetValue(Feature.BindingGroupName, value, String.Empty); } } /// /// The time (in milliseconds) to wait after the most recent property /// change before performing source update. /// /// /// This property affects only TwoWay bindings with UpdateSourceTrigger=PropertyChanged. /// [DefaultValue(0)] public int Delay { get { return (int)GetValue(Feature.Delay, 0); } set { CheckSealed(); SetValue(Feature.Delay, value, 0); } } #endregion Public Properties #region Public Methods //------------------------------------------------------ // // MarkupExtension overrides // //------------------------------------------------------ /// /// Return the value to set on the property for the target for this /// binding. /// public sealed override object ProvideValue(IServiceProvider serviceProvider) { // Binding a property value only works on DependencyObject and DependencyProperties. // For all other cases, just return this Binding object as the value. if (serviceProvider == null) { return this; } // Bindings are not allowed On CLR props except for Setter,Trigger,Condition (bugs 1183373,1572537) DependencyObject targetDependencyObject; DependencyProperty targetDependencyProperty; Helper.CheckCanReceiveMarkupExtension(this, serviceProvider, out targetDependencyObject, out targetDependencyProperty); if (targetDependencyObject == null || targetDependencyProperty == null) { return this; } // delegate real work to subclass return CreateBindingExpression(targetDependencyObject, targetDependencyProperty); } //------------------------------------------------------ // // Public Methods // //------------------------------------------------------ #endregion Public Methods #region Protected Enums //------------------------------------------------------ // // Protected Enums // //------------------------------------------------------ /// Flags indicating special properties of a Binding. [Flags] internal enum BindingFlags : uint { /// Data flows from source to target (only) OneWay = BindingExpressionBase.BindingFlags.OneWay, /// Data flows in both directions - source to target and vice-versa TwoWay = BindingExpressionBase.BindingFlags.TwoWay, /// Data flows from target to source (only) OneWayToSource = BindingExpressionBase.BindingFlags.OneWayToSource, /// Target is initialized from the source (only) OneTime = BindingExpressionBase.BindingFlags.OneTime, /// Data flow obtained from target property default PropDefault = BindingExpressionBase.BindingFlags.PropDefault, /// Raise TargetUpdated event whenever a value flows from source to target NotifyOnTargetUpdated = BindingExpressionBase.BindingFlags.NotifyOnTargetUpdated, /// Raise SourceUpdated event whenever a value flows from target to source NotifyOnSourceUpdated = BindingExpressionBase.BindingFlags.NotifyOnSourceUpdated, /// Raise ValidationError event whenever there is a ValidationError on Update NotifyOnValidationError = BindingExpressionBase.BindingFlags.NotifyOnValidationError, /// Obtain trigger from target property default UpdateDefault = BindingExpressionBase.BindingFlags.UpdateDefault, /// Update the source value whenever the target value changes UpdateOnPropertyChanged = BindingExpressionBase.BindingFlags.UpdateOnPropertyChanged, /// Update the source value whenever the target element loses focus UpdateOnLostFocus = BindingExpressionBase.BindingFlags.UpdateOnLostFocus, /// Update the source value only when explicitly told to do so UpdateExplicitly = BindingExpressionBase.BindingFlags.UpdateExplicitly, /// /// Used to determine whether the Path was internally Generated (such as the implicit /// /InnerText from an XPath). If it is, then it doesn't need to be serialized. /// PathGeneratedInternally = BindingExpressionBase.BindingFlags.PathGeneratedInternally, ValidatesOnExceptions = BindingExpressionBase.BindingFlags.ValidatesOnExceptions, ValidatesOnDataErrors = BindingExpressionBase.BindingFlags.ValidatesOnDataErrors, ValidatesOnNotifyDataErrors = BindingExpressionBase.BindingFlags.ValidatesOnNotifyDataErrors, /// Flags describing data transfer PropagationMask = OneWay | TwoWay | OneWayToSource | OneTime | PropDefault, /// Flags describing update trigger UpdateMask = UpdateDefault | UpdateOnPropertyChanged | UpdateOnLostFocus | UpdateExplicitly, /// Default value Default = BindingExpressionBase.BindingFlags.Default | ValidatesOnNotifyDataErrors, /// Error value, returned by FlagsFrom to indicate faulty input IllegalInput = BindingExpressionBase.BindingFlags.IllegalInput, } #endregion Protected Enums #region Protected Methods //------------------------------------------------------ // // Protected Methods // //------------------------------------------------------ /// /// Create an appropriate expression for this Binding, to be attached /// to the given DependencyProperty on the given DependencyObject. /// internal abstract BindingExpressionBase CreateBindingExpressionOverride(DependencyObject targetObject, DependencyProperty targetProperty, BindingExpressionBase owner); /// Return true if any of the given flags are set. internal bool TestFlag(BindingFlags flag) { return (_flags & flag) != 0; } /// Set the given flags. internal void SetFlag(BindingFlags flag) { _flags |= flag; } /// Clear the given flags. internal void ClearFlag(BindingFlags flag) { _flags &= ~flag; } /// Change the given flags to have the given value. internal void ChangeFlag(BindingFlags flag, bool value) { if (value) _flags |= flag; else _flags &= ~flag; } /// Get the flags within the given mas. internal BindingFlags GetFlagsWithinMask(BindingFlags mask) { return (_flags & mask); } /// Change the flags within the given mask to have the given value. internal void ChangeFlagsWithinMask(BindingFlags mask, BindingFlags flags) { _flags = (_flags & ~mask) | (flags & mask); } /// Convert the given BindingMode to BindingFlags. internal static BindingFlags FlagsFrom(BindingMode bindingMode) { switch (bindingMode) { case BindingMode.OneWay: return BindingFlags.OneWay; case BindingMode.TwoWay: return BindingFlags.TwoWay; case BindingMode.OneWayToSource: return BindingFlags.OneWayToSource; case BindingMode.OneTime: return BindingFlags.OneTime; case BindingMode.Default: return BindingFlags.PropDefault; } return BindingFlags.IllegalInput; } /// Convert the given UpdateSourceTrigger to BindingFlags. internal static BindingFlags FlagsFrom(UpdateSourceTrigger updateSourceTrigger) { switch (updateSourceTrigger) { case UpdateSourceTrigger.Default: return BindingFlags.UpdateDefault; case UpdateSourceTrigger.PropertyChanged: return BindingFlags.UpdateOnPropertyChanged; case UpdateSourceTrigger.LostFocus: return BindingFlags.UpdateOnLostFocus; case UpdateSourceTrigger.Explicit: return BindingFlags.UpdateExplicitly; } return BindingFlags.IllegalInput; } #endregion Protected Methods #region Internal Properties //------------------------------------------------------ // // Internal Properties // //------------------------------------------------------ internal BindingFlags Flags { get { return _flags; } } internal virtual CultureInfo ConverterCultureInternal { get { return null; } } internal virtual Collection ValidationRulesInternal { get { return null; } } internal virtual bool ValidatesOnNotifyDataErrorsInternal { get { return false; } } #endregion Internal Properties #region Internal Methods //------------------------------------------------------ // // Internal Methods // //------------------------------------------------------ /// /// Create an appropriate expression for this Binding, to be attached /// to the given DependencyProperty on the given DependencyObject. /// internal BindingExpressionBase CreateBindingExpression(DependencyObject targetObject, DependencyProperty targetProperty) { _isSealed = true; return CreateBindingExpressionOverride(targetObject, targetProperty, null); } /// /// Create an appropriate expression for this Binding, to be attached /// to the given DependencyProperty on the given DependencyObject. /// internal BindingExpressionBase CreateBindingExpression(DependencyObject targetObject, DependencyProperty targetProperty, BindingExpressionBase owner) { _isSealed = true; return CreateBindingExpressionOverride(targetObject, targetProperty, owner); } // Throw if the binding is sealed. internal void CheckSealed() { if (_isSealed) throw new InvalidOperationException(SR.Get(SRID.ChangeSealedBinding)); } // Return one of the special ValidationRules internal ValidationRule GetValidationRule(Type type) { if (TestFlag(BindingFlags.ValidatesOnExceptions) && type == typeof(System.Windows.Controls.ExceptionValidationRule)) return System.Windows.Controls.ExceptionValidationRule.Instance; if (TestFlag(BindingFlags.ValidatesOnDataErrors) && type == typeof(System.Windows.Controls.DataErrorValidationRule)) return System.Windows.Controls.DataErrorValidationRule.Instance; if (TestFlag(BindingFlags.ValidatesOnNotifyDataErrors) && type == typeof(System.Windows.Controls.NotifyDataErrorValidationRule)) return System.Windows.Controls.NotifyDataErrorValidationRule.Instance; return LookupValidationRule(type); } internal virtual ValidationRule LookupValidationRule(Type type) { return null; } internal static ValidationRule LookupValidationRule(Type type, Collection collection) { if (collection == null) return null; for (int i=0; i