WPF 依赖属性源码 洞察微软如何实现DependencyProperty
依赖属性DependencyProperty是wpf最重要的一个类,理解该类如何实现对学习wpf帮助很大!
终于找到了该类的源码!仔细阅读源码,看看微软如何玩的花招!
File: Base\System\Windows\DependencyProperty.cs
Project: wpf\src\WindowsBase.csproj (WindowsBase)
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Globalization;
using System.ComponentModel;
using System.Windows.Markup;// For ValueSerializerAttribute
using System.Windows.Threading; // For DispatcherObject
using System.Security.Permissions; // For LinkDemand
using MS.Utility;
using MS.Internal.WindowsBase;
using System.Reflection; // for IsInstanceOfType
using MS.Internal; #pragma warning disable 1634, 1691 // suppressing PreSharp warnings namespace System.Windows
{
/// <summary>
/// An attached dependency-based property
/// </summary>
[TypeConverter("System.Windows.Markup.DependencyPropertyConverter, PresentationFramework, Version=" + BuildInfo.WCP_VERSION + ", Culture=neutral, PublicKeyToken=" + BuildInfo.WCP_PUBLIC_KEY_TOKEN + ", Custom=null")]
[ValueSerializer(typeof(DependencyPropertyValueSerializer))]
public sealed class DependencyProperty
{
/// <summary>
/// Register a Dependency Property
/// </summary>
/// <param name="name">Name of property</param>
/// <param name="propertyType">Type of the property</param>
/// <param name="ownerType">Type that is registering the property</param>
/// <returns>Dependency Property</returns>
public static DependencyProperty Register(string name, Type propertyType, Type ownerType)
{
// Forwarding
return Register(name, propertyType, ownerType, null, null);
} /// <summary>
/// Register a Dependency Property
/// </summary>
/// <param name="name">Name of property</param>
/// <param name="propertyType">Type of the property</param>
/// <param name="ownerType">Type that is registering the property</param>
/// <param name="typeMetadata">Metadata to use if current type doesn't specify type-specific metadata</param>
/// <returns>Dependency Property</returns>
public static DependencyProperty Register(string name, Type propertyType, Type ownerType, PropertyMetadata typeMetadata)
{
// Forwarding
return Register(name, propertyType, ownerType, typeMetadata, null);
} /// <summary>
/// Register a Dependency Property
/// </summary>
/// <param name="name">Name of property</param>
/// <param name="propertyType">Type of the property</param>
/// <param name="ownerType">Type that is registering the property</param>
/// <param name="typeMetadata">Metadata to use if current type doesn't specify type-specific metadata</param>
/// <param name="validateValueCallback">Provides additional value validation outside automatic type validation</param>
/// <returns>Dependency Property</returns>
public static DependencyProperty Register(string name, Type propertyType, Type ownerType, PropertyMetadata typeMetadata, ValidateValueCallback validateValueCallback)
{
RegisterParameterValidation(name, propertyType, ownerType); // Register an attached property
PropertyMetadata defaultMetadata = null;
if (typeMetadata != null && typeMetadata.DefaultValueWasSet())
{
defaultMetadata = new PropertyMetadata(typeMetadata.DefaultValue);
} DependencyProperty property = RegisterCommon(name, propertyType, ownerType, defaultMetadata, validateValueCallback); if (typeMetadata != null)
{
// Apply type-specific metadata to owner type only
property.OverrideMetadata(ownerType, typeMetadata);
} return property;
} /// <summary>
/// Simple registration, metadata, validation, and a read-only property
/// key. Calling this version restricts the property such that it can
/// only be set via the corresponding overload of DependencyObject.SetValue.
/// </summary>
public static DependencyPropertyKey RegisterReadOnly(
string name,
Type propertyType,
Type ownerType,
PropertyMetadata typeMetadata )
{
return RegisterReadOnly( name, propertyType, ownerType, typeMetadata, null );
} /// <summary>
/// Simple registration, metadata, validation, and a read-only property
/// key. Calling this version restricts the property such that it can
/// only be set via the corresponding overload of DependencyObject.SetValue.
/// </summary>
public static DependencyPropertyKey RegisterReadOnly(
string name,
Type propertyType,
Type ownerType,
PropertyMetadata typeMetadata,
ValidateValueCallback validateValueCallback )
{
RegisterParameterValidation(name, propertyType, ownerType); PropertyMetadata defaultMetadata = null; if (typeMetadata != null && typeMetadata.DefaultValueWasSet())
{
defaultMetadata = new PropertyMetadata(typeMetadata.DefaultValue);
}
else
{
defaultMetadata = AutoGeneratePropertyMetadata(propertyType,validateValueCallback,name,ownerType);
} // We create a DependencyPropertyKey at this point with a null property
// and set that in the _readOnlyKey field. This is so the property is
// marked as requiring a key immediately. If something fails in the
// initialization path, the property is still marked as needing a key.
// This is better than the alternative of creating and setting the key
// later, because if that code fails the read-only property would not
// be marked read-only. The intent of this mildly convoluted code
// is so we fail securely.
DependencyPropertyKey authorizationKey = new DependencyPropertyKey(null); // No property yet, use null as placeholder. DependencyProperty property = RegisterCommon(name, propertyType, ownerType, defaultMetadata, validateValueCallback); property._readOnlyKey = authorizationKey; authorizationKey.SetDependencyProperty(property); if (typeMetadata == null )
{
// No metadata specified, generate one so we can specify the authorized key.
typeMetadata = AutoGeneratePropertyMetadata(propertyType,validateValueCallback,name,ownerType);
} // Authorize registering type for read-only access, create key.
#pragma warning suppress 6506 // typeMetadata is never null, since we generate default metadata if none is provided. // Apply type-specific metadata to owner type only
property.OverrideMetadata(ownerType, typeMetadata, authorizationKey); return authorizationKey;
} /// <summary>
/// Simple registration, metadata, validation, and a read-only property
/// key. Calling this version restricts the property such that it can
/// only be set via the corresponding overload of DependencyObject.SetValue.
/// </summary>
public static DependencyPropertyKey RegisterAttachedReadOnly(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata)
{
return RegisterAttachedReadOnly( name, propertyType, ownerType, defaultMetadata, null );
} /// <summary>
/// Simple registration, metadata, validation, and a read-only property
/// key. Calling this version restricts the property such that it can
/// only be set via the corresponding overload of DependencyObject.SetValue.
/// </summary>
public static DependencyPropertyKey RegisterAttachedReadOnly(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata, ValidateValueCallback validateValueCallback)
{
RegisterParameterValidation(name, propertyType, ownerType); // Establish default metadata for all types, if none is provided
if (defaultMetadata == null)
{
defaultMetadata = AutoGeneratePropertyMetadata( propertyType, validateValueCallback, name, ownerType );
} // We create a DependencyPropertyKey at this point with a null property
// and set that in the _readOnlyKey field. This is so the property is
// marked as requiring a key immediately. If something fails in the
// initialization path, the property is still marked as needing a key.
// This is better than the alternative of creating and setting the key
// later, because if that code fails the read-only property would not
// be marked read-only. The intent of this mildly convoluted code
// is so we fail securely.
DependencyPropertyKey authorizedKey = new DependencyPropertyKey(null); DependencyProperty property = RegisterCommon( name, propertyType, ownerType, defaultMetadata, validateValueCallback); property._readOnlyKey = authorizedKey; authorizedKey.SetDependencyProperty(property); return authorizedKey;
} /// <summary>
/// Register an attached Dependency Property
/// </summary>
/// <param name="name">Name of property</param>
/// <param name="propertyType">Type of the property</param>
/// <param name="ownerType">Type that is registering the property</param>
/// <returns>Dependency Property</returns>
public static DependencyProperty RegisterAttached(string name, Type propertyType, Type ownerType)
{
// Forwarding
return RegisterAttached(name, propertyType, ownerType, null, null );
} /// <summary>
/// Register an attached Dependency Property
/// </summary>
/// <param name="name">Name of property</param>
/// <param name="propertyType">Type of the property</param>
/// <param name="ownerType">Type that is registering the property</param>
/// <param name="defaultMetadata">Metadata to use if current type doesn't specify type-specific metadata</param>
/// <returns>Dependency Property</returns>
public static DependencyProperty RegisterAttached(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata)
{
// Forwarding
return RegisterAttached(name, propertyType, ownerType, defaultMetadata, null );
} /// <summary>
/// Register an attached Dependency Property
/// </summary>
/// <param name="name">Name of property</param>
/// <param name="propertyType">Type of the property</param>
/// <param name="ownerType">Type that is registering the property</param>
/// <param name="defaultMetadata">Metadata to use if current type doesn't specify type-specific metadata</param>
/// <param name="validateValueCallback">Provides additional value validation outside automatic type validation</param>
/// <returns>Dependency Property</returns>
public static DependencyProperty RegisterAttached(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata, ValidateValueCallback validateValueCallback)
{
RegisterParameterValidation(name, propertyType, ownerType); return RegisterCommon( name, propertyType, ownerType, defaultMetadata, validateValueCallback );
} private static void RegisterParameterValidation(string name, Type propertyType, Type ownerType)
{
if (name == null)
{
throw new ArgumentNullException("name");
} if (name.Length == )
{
throw new ArgumentException(SR.Get(SRID.StringEmpty), "name");
} if (ownerType == null)
{
throw new ArgumentNullException("ownerType");
} if (propertyType == null)
{
throw new ArgumentNullException("propertyType");
}
} private static DependencyProperty RegisterCommon(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata, ValidateValueCallback validateValueCallback)
{
FromNameKey key = new FromNameKey(name, ownerType);
lock (Synchronized)
{
if (PropertyFromName.Contains(key))
{
throw new ArgumentException(SR.Get(SRID.PropertyAlreadyRegistered, name, ownerType.Name));
}
} // Establish default metadata for all types, if none is provided
if (defaultMetadata == null)
{
defaultMetadata = AutoGeneratePropertyMetadata( propertyType, validateValueCallback, name, ownerType );
}
else // Metadata object is provided.
{
// If the defaultValue wasn't specified auto generate one
if (!defaultMetadata.DefaultValueWasSet())
{
defaultMetadata.DefaultValue = AutoGenerateDefaultValue(propertyType);
} ValidateMetadataDefaultValue( defaultMetadata, propertyType, name, validateValueCallback );
} // Create property
DependencyProperty dp = new DependencyProperty(name, propertyType, ownerType, defaultMetadata, validateValueCallback); // Seal (null means being used for default metadata, calls OnApply)
defaultMetadata.Seal(dp, null); if (defaultMetadata.IsInherited)
{
dp._packedData |= Flags.IsPotentiallyInherited;
} if (defaultMetadata.UsingDefaultValueFactory)
{
dp._packedData |= Flags.IsPotentiallyUsingDefaultValueFactory;
} // Map owner type to this property
// Build key
lock (Synchronized)
{
PropertyFromName[key] = dp;
} if( TraceDependencyProperty.IsEnabled )
{
TraceDependencyProperty.TraceActivityItem(
TraceDependencyProperty.Register,
dp,
dp.OwnerType );
} return dp;
} private static object AutoGenerateDefaultValue(
Type propertyType)
{
// Default per-type metadata not provided, create
object defaultValue = null; // Auto-assigned default value
if (propertyType.IsValueType)
{
// Value-types have default-constructed type default values
defaultValue = Activator.CreateInstance(propertyType);
} return defaultValue;
} private static PropertyMetadata AutoGeneratePropertyMetadata(
Type propertyType,
ValidateValueCallback validateValueCallback,
string name,
Type ownerType)
{
// Default per-type metadata not provided, create
object defaultValue = AutoGenerateDefaultValue(propertyType); // If a validator is passed in, see if the default value makes sense.
if ( validateValueCallback != null &&
!validateValueCallback(defaultValue))
{
// Didn't work - require the caller to specify one.
throw new ArgumentException(SR.Get(SRID.DefaultValueAutoAssignFailed, name, ownerType.Name));
} return new PropertyMetadata(defaultValue);
} // Validate the default value in the given metadata
private static void ValidateMetadataDefaultValue(
PropertyMetadata defaultMetadata,
Type propertyType,
string propertyName,
ValidateValueCallback validateValueCallback )
{
// If we are registered to use the DefaultValue factory we can
// not validate the DefaultValue at registration time, so we
// early exit.
if (defaultMetadata.UsingDefaultValueFactory)
{
return;
} ValidateDefaultValueCommon(defaultMetadata.DefaultValue, propertyType,
propertyName, validateValueCallback, /*checkThreadAffinity = */ true);
} // Validate the given default value, used by PropertyMetadata.GetDefaultValue()
// when the DefaultValue factory is used.
// These default values are allowed to have thread-affinity.
internal void ValidateFactoryDefaultValue(object defaultValue)
{
ValidateDefaultValueCommon(defaultValue, PropertyType, Name, ValidateValueCallback, false);
} private static void ValidateDefaultValueCommon(
object defaultValue,
Type propertyType,
string propertyName,
ValidateValueCallback validateValueCallback,
bool checkThreadAffinity)
{
// Ensure default value is the correct type
if (!IsValidType(defaultValue, propertyType))
{
throw new ArgumentException(SR.Get(SRID.DefaultValuePropertyTypeMismatch, propertyName));
} // An Expression used as default value won't behave as expected since
// it doesn't get evaluated. We explicitly fail it here.
if (defaultValue is Expression )
{
throw new ArgumentException(SR.Get(SRID.DefaultValueMayNotBeExpression));
} if (checkThreadAffinity)
{
// If the default value is a DispatcherObject with thread affinity
// we cannot accept it as a default value. If it implements ISealable
// we attempt to seal it; if not we throw an exception. Types not
// deriving from DispatcherObject are allowed - it is up to the user to
// make any custom types free-threaded. DispatcherObject dispatcherObject = defaultValue as DispatcherObject; if (dispatcherObject != null && dispatcherObject.Dispatcher != null)
{
// Try to make the DispatcherObject free-threaded if it's an
// ISealable. ISealable valueAsISealable = dispatcherObject as ISealable; if (valueAsISealable != null && valueAsISealable.CanSeal)
{
Invariant.Assert (!valueAsISealable.IsSealed,
"A Sealed ISealable must not have dispatcher affinity"); valueAsISealable.Seal(); Invariant.Assert(dispatcherObject.Dispatcher == null,
"ISealable.Seal() failed after ISealable.CanSeal returned true");
}
else
{
throw new ArgumentException(SR.Get(SRID.DefaultValueMustBeFreeThreaded, propertyName));
}
}
} // After checking for correct type, check default value against
// validator (when one is given)
if ( validateValueCallback != null &&
!validateValueCallback(defaultValue))
{
throw new ArgumentException(SR.Get(SRID.DefaultValueInvalid, propertyName));
}
} /// <summary>
/// Parameter validation for OverrideMetadata, includes code to force
/// all base classes of "forType" to register their metadata so we know
/// what we are overriding.
/// </summary>
private void SetupOverrideMetadata(
Type forType,
PropertyMetadata typeMetadata,
out DependencyObjectType dType,
out PropertyMetadata baseMetadata )
{
if (forType == null)
{
throw new ArgumentNullException("forType");
} if (typeMetadata == null)
{
throw new ArgumentNullException("typeMetadata");
} if (typeMetadata.Sealed)
{
throw new ArgumentException(SR.Get(SRID.TypeMetadataAlreadyInUse));
} if (!typeof(DependencyObject).IsAssignableFrom(forType))
{
throw new ArgumentException(SR.Get(SRID.TypeMustBeDependencyObjectDerived, forType.Name));
} // Ensure default value is a correct value (if it was supplied,
// otherwise, the default value will be taken from the base metadata
// which was already validated)
if (typeMetadata.IsDefaultValueModified)
{
// Will throw ArgumentException if fails.
ValidateMetadataDefaultValue( typeMetadata, PropertyType, Name, ValidateValueCallback );
} // Force all base classes to register their metadata
dType = DependencyObjectType.FromSystemType(forType); // Get metadata for the base type
baseMetadata = GetMetadata(dType.BaseType); // Make sure overriding metadata is the same type or derived type of
// the base metadata
if (!baseMetadata.GetType().IsAssignableFrom(typeMetadata.GetType()))
{
throw new ArgumentException(SR.Get(SRID.OverridingMetadataDoesNotMatchBaseMetadataType));
}
} /// <summary>
/// Supply metadata for given type & run static constructors if needed.
/// </summary>
/// <remarks>
/// The supplied metadata will be merged with the type's base
/// metadata
/// </remarks>
public void OverrideMetadata(Type forType, PropertyMetadata typeMetadata)
{
DependencyObjectType dType;
PropertyMetadata baseMetadata; SetupOverrideMetadata(forType, typeMetadata, out dType, out baseMetadata); if (ReadOnly)
{
// Readonly and no DependencyPropertyKey - not allowed.
throw new InvalidOperationException(SR.Get(SRID.ReadOnlyOverrideNotAllowed, Name));
} ProcessOverrideMetadata(forType, typeMetadata, dType, baseMetadata);
} /// <summary>
/// Supply metadata for a given type, overriding a property that is
/// read-only. If property is not read only, tells user to use the Plain
/// Jane OverrideMetadata instead.
/// </summary>
public void OverrideMetadata(Type forType, PropertyMetadata typeMetadata, DependencyPropertyKey key)
{
DependencyObjectType dType;
PropertyMetadata baseMetadata; SetupOverrideMetadata(forType, typeMetadata, out dType, out baseMetadata); if (key == null)
{
throw new ArgumentNullException("key");
} if (ReadOnly)
{
// If the property is read-only, the key must match this property
// and the key must match that in the base metadata. if (key.DependencyProperty != this)
{
throw new ArgumentException(SR.Get(SRID.ReadOnlyOverrideKeyNotAuthorized, Name));
} VerifyReadOnlyKey(key);
}
else
{
throw new InvalidOperationException(SR.Get(SRID.PropertyNotReadOnly));
} // Either the property doesn't require a key, or the key match was
// successful. Proceed with the metadata override.
ProcessOverrideMetadata(forType, typeMetadata, dType, baseMetadata);
} /// <summary>
/// After parameters have been validated for OverrideMetadata, this
/// method is called to actually update the data structures.
/// </summary>
private void ProcessOverrideMetadata(
Type forType,
PropertyMetadata typeMetadata,
DependencyObjectType dType,
PropertyMetadata baseMetadata)
{
// Store per-Type metadata for this property. Locks only on Write.
// Datastructure guaranteed to be valid for non-locking readers
lock (Synchronized)
{
if (DependencyProperty.UnsetValue == _metadataMap[dType.Id])
{
_metadataMap[dType.Id] = typeMetadata;
}
else
{
throw new ArgumentException(SR.Get(SRID.TypeMetadataAlreadyRegistered, forType.Name));
}
} // Merge base's metadata into this metadata
// CALLBACK
typeMetadata.InvokeMerge(baseMetadata, this); // Type metadata may no longer change (calls OnApply)
typeMetadata.Seal(this, forType); if (typeMetadata.IsInherited)
{
_packedData |= Flags.IsPotentiallyInherited;
} if (typeMetadata.DefaultValueWasSet() && (typeMetadata.DefaultValue != DefaultMetadata.DefaultValue))
{
_packedData |= Flags.IsDefaultValueChanged;
} if (typeMetadata.UsingDefaultValueFactory)
{
_packedData |= Flags.IsPotentiallyUsingDefaultValueFactory;
}
} [FriendAccessAllowed] // Built into Base, also used by Core & Framework.
internal object GetDefaultValue(DependencyObjectType dependencyObjectType)
{
if (!IsDefaultValueChanged)
{
return DefaultMetadata.DefaultValue;
} return GetMetadata(dependencyObjectType).DefaultValue;
} [FriendAccessAllowed] // Built into Base, also used by Core & Framework.
internal object GetDefaultValue(Type forType)
{
if (!IsDefaultValueChanged)
{
return DefaultMetadata.DefaultValue;
} return GetMetadata(DependencyObjectType.FromSystemTypeInternal(forType)).DefaultValue;
} /// <summary>
/// Retrieve metadata for a provided type
/// </summary>
/// <param name="forType">Type to get metadata</param>
/// <returns>Property metadata</returns>
public PropertyMetadata GetMetadata(Type forType)
{
if (forType != null)
{
return GetMetadata(DependencyObjectType.FromSystemType(forType));
}
throw new ArgumentNullException("forType");
} /// <summary>
/// Retrieve metadata for a provided DependencyObject
/// </summary>
/// <param name="dependencyObject">DependencyObject to get metadata</param>
/// <returns>Property metadata</returns>
public PropertyMetadata GetMetadata(DependencyObject dependencyObject)
{
if (dependencyObject != null)
{
return GetMetadata(dependencyObject.DependencyObjectType);
}
throw new ArgumentNullException("dependencyObject");
} /// <summary>
/// Reteive metadata for a DependencyObject type described by the
/// given DependencyObjectType
/// </summary>
//CASRemoval:[StrongNameIdentityPermission(SecurityAction.LinkDemand, PublicKey = BuildInfo.WCP_PUBLIC_KEY_STRING)]
public PropertyMetadata GetMetadata(DependencyObjectType dependencyObjectType)
{
// All static constructors for this DType and all base types have already
// been run. If no overriden metadata was provided, then look up base types.
// If no metadata found on base types, then return default if (null != dependencyObjectType)
{
// Do we in fact have any overrides at all?
int index = _metadataMap.Count - ;
int Id;
object value; if (index < )
{
// No overrides or it's the base class
return _defaultMetadata;
}
else if (index == )
{
// Only 1 override
_metadataMap.GetKeyValuePair(index, out Id, out value); // If there is overriden metadata, then there is a base class with
// lower or equal Id of this class, or this class is already a base class
// of the overridden one. Therefore dependencyObjectType won't ever
// become null before we exit the while loop
while (dependencyObjectType.Id > Id)
{
dependencyObjectType = dependencyObjectType.BaseType;
} if (Id == dependencyObjectType.Id)
{
// Return the override
return (PropertyMetadata)value;
}
// Return default metadata
}
else
{
// We have more than 1 override for this class, so we will have to loop through
// both the overrides and the class Id
if ( != dependencyObjectType.Id)
{
do
{
// Get the Id of the most derived class with overridden metadata
_metadataMap.GetKeyValuePair(index, out Id, out value);
--index; // If the Id of this class is less than the override, then look for an override
// with an equal or lower Id until we run out of overrides
while ((dependencyObjectType.Id < Id) && (index >= ))
{
_metadataMap.GetKeyValuePair(index, out Id, out value);
--index;
} // If there is overriden metadata, then there is a base class with
// lower or equal Id of this class, or this class is already a base class
// of the overridden one. Therefore dependencyObjectType won't ever
// become null before we exit the while loop
while (dependencyObjectType.Id > Id)
{
dependencyObjectType = dependencyObjectType.BaseType;
} if (Id == dependencyObjectType.Id)
{
// Return the override
return (PropertyMetadata)value;
}
}
while (index >= );
}
}
}
return _defaultMetadata;
} /// <summary>
/// Associate another owner type with this property
/// </summary>
/// <remarks>
/// The owner type is used when resolving a property by name (<see cref="FromName"/>)
/// </remarks>
/// <param name="ownerType">Additional owner type</param>
/// <returns>This property</returns>
public DependencyProperty AddOwner(Type ownerType)
{
// Forwarding
return AddOwner(ownerType, null);
} /// <summary>
/// Associate another owner type with this property
/// </summary>
/// <remarks>
/// The owner type is used when resolving a property by name (<see cref="FromName"/>)
/// </remarks>
/// <param name="ownerType">Additional owner type</param>
/// <param name="typeMetadata">Optional type metadata to override on owner's behalf</param>
/// <returns>This property</returns>
public DependencyProperty AddOwner(Type ownerType, PropertyMetadata typeMetadata)
{
if (ownerType == null)
{
throw new ArgumentNullException("ownerType");
} // Map owner type to this property
// Build key
FromNameKey key = new FromNameKey(Name, ownerType); lock (Synchronized)
{
if (PropertyFromName.Contains(key))
{
throw new ArgumentException(SR.Get(SRID.PropertyAlreadyRegistered, Name, ownerType.Name));
}
} if (typeMetadata != null)
{
OverrideMetadata(ownerType, typeMetadata);
} lock (Synchronized)
{
PropertyFromName[key] = this;
} return this;
} /// <summary>
/// Name of the property
/// </summary>
public string Name
{
get { return _name; }
} /// <summary>
/// Type of the property
/// </summary>
public Type PropertyType
{
get { return _propertyType; }
} /// <summary>
/// Owning type of the property
/// </summary>
public Type OwnerType
{
get { return _ownerType; }
} /// <summary>
/// Default metadata for the property
/// </summary>
public PropertyMetadata DefaultMetadata
{
get { return _defaultMetadata; }
} /// <summary>
/// Value validation callback
/// </summary>
public ValidateValueCallback ValidateValueCallback
{
get { return _validateValueCallback; }
} /// <summary>
/// Zero-based globally unique index of the property
/// </summary>
public int GlobalIndex
{
get { return (int) (_packedData & Flags.GlobalIndexMask); }
} internal bool IsObjectType
{
get { return (_packedData & Flags.IsObjectType) != ; }
} internal bool IsValueType
{
get { return (_packedData & Flags.IsValueType) != ; }
} internal bool IsFreezableType
{
get { return (_packedData & Flags.IsFreezableType) != ; }
} internal bool IsStringType
{
get { return (_packedData & Flags.IsStringType) != ; }
} internal bool IsPotentiallyInherited
{
get { return (_packedData & Flags.IsPotentiallyInherited) != ; }
} internal bool IsDefaultValueChanged
{
get { return (_packedData & Flags.IsDefaultValueChanged) != ; }
} internal bool IsPotentiallyUsingDefaultValueFactory
{
get { return (_packedData & Flags.IsPotentiallyUsingDefaultValueFactory) != ; }
} /// <summary>
/// Serves as a hash function for a particular type, suitable for use in
/// hashing algorithms and data structures like a hash table
/// </summary>
/// <returns>The DependencyProperty's GlobalIndex</returns>
public override int GetHashCode()
{
return GlobalIndex;
} /// <summary>
/// Used to determine if given value is appropriate for the type of the property
/// </summary>
/// <param name="value">Value to check</param>
/// <returns>true if value matches property type</returns>
public bool IsValidType(object value)
{
return IsValidType(value, PropertyType);
} /// <summary>
/// Used to determine if given value is appropriate for the type of the property
/// and the range of values (as specified via the ValidateValueCallback) within that type
/// </summary>
/// <param name="value">Value to check</param>
/// <returns>true if value is appropriate</returns>
public bool IsValidValue(object value)
{
if (!IsValidType(value, PropertyType))
{
return false;
} if (ValidateValueCallback != null)
{
// CALLBACK
return ValidateValueCallback(value);
} return true;
} /// <summary>
/// Set/Value value disabling
/// </summary>
public bool ReadOnly
{
get
{
return (_readOnlyKey != null);
}
} /// <summary>
/// Returns the DependencyPropertyKey associated with this DP.
/// </summary>
internal DependencyPropertyKey DependencyPropertyKey
{
get
{
return _readOnlyKey;
}
} internal void VerifyReadOnlyKey( DependencyPropertyKey candidateKey )
{
Debug.Assert( ReadOnly, "Why are we trying to validate read-only key on a property that is not read-only?"); if (_readOnlyKey != candidateKey)
{
throw new ArgumentException(SR.Get(SRID.ReadOnlyKeyNotAuthorized));
}
} /// <summary>
/// Internal version of IsValidValue that bypasses IsValidType check;
/// Called from SetValueInternal
/// </summary>
/// <param name="value">Value to check</param>
/// <returns>true if value is appropriate</returns>
internal bool IsValidValueInternal(object value)
{
if (ValidateValueCallback != null)
{
// CALLBACK
return ValidateValueCallback(value);
} return true;
} /// <summary>
/// Find a property from name
/// </summary>
/// <remarks>
/// Search includes base classes of the provided type as well
/// </remarks>
/// <param name="name">Name of the property</param>
/// <param name="ownerType">Owner type of the property</param>
/// <returns>Dependency property</returns>
[FriendAccessAllowed] // Built into Base, also used by Framework.
internal static DependencyProperty FromName(string name, Type ownerType)
{
DependencyProperty dp = null; if (name != null)
{
if (ownerType != null)
{
FromNameKey key = new FromNameKey(name, ownerType); while ((dp == null) && (ownerType != null))
{
// Ensure static constructor of type has run
MS.Internal.WindowsBase.SecurityHelper.RunClassConstructor(ownerType); // Locate property
key.UpdateNameKey(ownerType); lock (Synchronized)
{
dp = (DependencyProperty)PropertyFromName[key];
} ownerType = ownerType.BaseType;
}
}
else
{
throw new ArgumentNullException("ownerType");
}
}
else
{
throw new ArgumentNullException("name");
}
return dp;
} /// <summary>
/// String representation
/// </summary>
public override string ToString()
{
return _name;
} internal static bool IsValidType(object value, Type propertyType)
{
if (value == null)
{
// Null values are invalid for value-types
if (propertyType.IsValueType &&
!(propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == NullableType))
{
return false;
}
}
else
{
// Non-null default value, ensure its the correct type
if (!propertyType.IsInstanceOfType(value))
{
return false;
}
} return true;
} private class FromNameKey
{
public FromNameKey(string name, Type ownerType)
{
_name = name;
_ownerType = ownerType; _hashCode = _name.GetHashCode() ^ _ownerType.GetHashCode();
} public void UpdateNameKey(Type ownerType)
{
_ownerType = ownerType; _hashCode = _name.GetHashCode() ^ _ownerType.GetHashCode();
} public override int GetHashCode()
{
return _hashCode;
} public override bool Equals(object o)
{
if ((o != null) && (o is FromNameKey))
{
return Equals((FromNameKey)o);
}
else
{
return false;
}
} public bool Equals(FromNameKey key)
{
return (_name.Equals(key._name) && (_ownerType == key._ownerType));
} private string _name;
private Type _ownerType; private int _hashCode;
} private DependencyProperty(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata, ValidateValueCallback validateValueCallback)
{
_name = name;
_propertyType = propertyType;
_ownerType = ownerType;
_defaultMetadata = defaultMetadata;
_validateValueCallback = validateValueCallback; Flags packedData;
lock (Synchronized)
{
packedData = (Flags) GetUniqueGlobalIndex(ownerType, name); RegisteredPropertyList.Add(this);
} if (propertyType.IsValueType)
{
packedData |= Flags.IsValueType;
} if (propertyType == typeof(object))
{
packedData |= Flags.IsObjectType;
} if (typeof(Freezable).IsAssignableFrom(propertyType))
{
packedData |= Flags.IsFreezableType;
} if (propertyType == typeof(string))
{
packedData |= Flags.IsStringType;
} _packedData = packedData;
} // Synchronized: Covered by DependencyProperty.Synchronized
internal static int GetUniqueGlobalIndex(Type ownerType, string name)
{
// Prevent GlobalIndex from overflow. DependencyProperties are meant to be static members and are to be registered
// only via static constructors. However there is no cheap way of ensuring this, without having to do a stack walk. Hence
// concievably people could register DependencyProperties via instance methods and therefore cause the GlobalIndex to
// overflow. This check will explicitly catch this error, instead of silently malfuntioning.
if (GlobalIndexCount >= (int)Flags.GlobalIndexMask)
{
if (ownerType != null)
{
throw new InvalidOperationException(SR.Get(SRID.TooManyDependencyProperties, ownerType.Name + "." + name));
}
else
{
throw new InvalidOperationException(SR.Get(SRID.TooManyDependencyProperties, "ConstantProperty"));
}
} // Covered by Synchronized by caller
return GlobalIndexCount++;
} /// <summary>
/// This is the callback designers use to participate in the computation of property
/// values at design time. Eg. Even if the author sets Visibility to Hidden, the designer
/// wants to coerce the value to Visible at design time so that the element doesn't
/// disappear from the design surface.
/// </summary>
internal CoerceValueCallback DesignerCoerceValueCallback
{
get { return _designerCoerceValueCallback; }
set
{
if (ReadOnly)
{
throw new InvalidOperationException(SR.Get(SRID.ReadOnlyDesignerCoersionNotAllowed, Name));
} _designerCoerceValueCallback = value;
}
} /// <summary> Standard unset value </summary>
public static readonly object UnsetValue = new NamedObject("DependencyProperty.UnsetValue"); private string _name;
private Type _propertyType;
private Type _ownerType;
private PropertyMetadata _defaultMetadata;
private ValidateValueCallback _validateValueCallback;
private DependencyPropertyKey _readOnlyKey; [Flags]
private enum Flags : int
{
GlobalIndexMask = 0x0000FFFF,
IsValueType = 0x00010000,
IsFreezableType = 0x00020000,
IsStringType = 0x00040000,
IsPotentiallyInherited = 0x00080000,
IsDefaultValueChanged = 0x00100000,
IsPotentiallyUsingDefaultValueFactory = 0x00200000,
IsObjectType = 0x00400000,
// 0xFF800000 free bits
} private Flags _packedData; // Synchronized (write locks, lock-free reads): Covered by DependencyProperty instance
// This is a map that contains the IDs of derived classes that have overriden metadata
/* property */ internal InsertionSortMap _metadataMap = new InsertionSortMap(); private CoerceValueCallback _designerCoerceValueCallback; // Synchronized (write locks, lock-free reads): Covered by DependencyProperty.Synchronized
/* property */ internal static ItemStructList<DependencyProperty> RegisteredPropertyList = new ItemStructList<DependencyProperty>(); // Synchronized: Covered by DependencyProperty.Synchronized
private static Hashtable PropertyFromName = new Hashtable(); // Synchronized: Covered by DependencyProperty.Synchronized
private static int GlobalIndexCount; // Global, cross-object synchronization
internal static object Synchronized = new object(); // Nullable Type
private static Type NullableType = typeof(Nullable<>); /// <summary>
/// Returns the number of all registered properties.
/// </summary>
internal static int RegisteredPropertyCount {
get {
return RegisteredPropertyList.Count;
}
} /// <summary>
/// Returns an enumeration of properties that are
/// currently registered.
/// Synchronized (write locks, lock-free reads): Covered by DependencyProperty.Synchronized
/// </summary>
internal static IEnumerable RegisteredProperties {
get {
foreach(DependencyProperty dp in RegisteredPropertyList.List) {
if (dp != null) {
yield return dp;
}
}
}
} }
}
WPF 依赖属性源码 洞察微软如何实现DependencyProperty的更多相关文章
- WPF依赖属性值源(BaseValueSource)
原文:WPF依赖属性值源(BaseValueSource) WPF依赖属性提供一个机制,可以获取依赖属性提供值的来源 其以BaseValueSource枚举表示 1.Default public ...
- WPF依赖属性(续)(2)依赖属性与附加属性的区别
原文:WPF依赖属性(续)(2)依赖属性与附加属性的区别 接上篇,感谢各位的评论,都是认为依赖属性的设计并不是为了节省内存,从大的方面而讲是如此.样式,数据绑定,动画样样都离不开它.这篇 ...
- WPF 依赖属性前言
WPF 依赖属性前言 在.net中,我们可以属性来获取或设置字段的值,不需要在编写额外的get和set方法,但这有一个前提,那就是需要在对象中拥有一个字段,才能在此字段的基础上获取或设置字段的值, ...
- springboot bean的循环依赖实现 源码分析
springboot bean的循环依赖实现 源码分析 本文基于springboot版本2.5.1 <parent> <groupId>org.springframework. ...
- WPF依赖属性详解
WPF依赖属性详解 WPF 依赖属性 英文译为 Dependency Properties,是WPF引入的一种新类型的属性,在WPF中有着极为广泛的应用,在WPF中对于WPF Dependency P ...
- WPF自学入门(五)WPF依赖属性
在.NET中有事件也有属性,WPF中加入了路由事件,也加入了依赖属性.最近在写项目时还不知道WPF依赖属性是干什么用的,在使用依赖项属性的时候我都以为是在用.NET中的属性,但是确实上不是的,通过阅读 ...
- WPF依赖属性(续)(3)依赖属性存储
原文:WPF依赖属性(续)(3)依赖属性存储 在之前的两篇,很多朋友参与了讨论,也说明各位对WPF/SL计数的热情,对DP系统各抒已见,当然也出现了一些分歧. 以下简称DP为依赖属性 ...
- WPF依赖属性(续)(1)
原文:WPF依赖属性(续)(1) 之前有写过几篇文章,详细地介绍了依赖属性的基本使用方法,如果你不想了解其内部实现机制的话,那么通过那两篇文章的介绍,足以应付平时的应用 ...
- 监听WPF依赖属性
原文:监听WPF依赖属性 当我们使用依赖属性的时候,有时需要监听它的变化,这在写自定义控件的时候十分有用, 下面介绍一种简单的方法. 如下使用DependencyPropertyDescripto ...
随机推荐
- 十五、Spring Boot 环境变量读取 和 属性对象的绑定
凡是被spring管理的类,实现接口 EnvironmentAware 重写方法 setEnvironment 可以在工程启动时,获取到系统环境变量和application配置文件中的变量. 如: @ ...
- gulp提高微信小程序开发效率
最近公司要求把一套公众号项目的页面迁移到小程序,也就意味着要重新敲一份代码,不能更繁琐了,为了节省时间,提高迁移效率,就决定自己动手用gulp搭一个简易的小程序框架,再记录一下搭建过程.希望有大神 ...
- java发送post请求 ,请求数据放到body里
java利用httpclient发送post请求 ,请求数据放到body里. /** * post请求 ,请求数据放到body里 * * @author lifq * * 2017年3月15日 下午3 ...
- spring -boot s-tarter 详解
Starter POMs是可以包含到应用中的一个方便的依赖关系描述符集合.你可以获取所有Spring及相关技术的一站式服务,而不需要翻阅示例代码,拷贝粘贴大量的依赖描述符.例如,如果你想使用Sprin ...
- IntelliJ IDEA创建java项目
IntelliJ IDEA创建java项目 进入到IntelliJ IDEA启动界面,点击Create New Project 2.这样就进入到了创建项目页面,这里可以创建好多项目,这里我们以java ...
- Linux下查看Go语言软件运行情况
在Linux下,使用"jps"可以查看用Java语言写的软件的运行情况,如果要查看GO语言写的软件的运行情况,可以使用"gops",但这不是系统自带的,需要进行 ...
- 《TCP-IP详解卷1:协议》【PDF】下载
<TCP-IP详解卷1:协议>[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230062539 内容简介<TCP/IP详解卷1:协 ...
- Mac OS 终端利器 iTerm2
之前一直使用 Mac OS 自带的终端,用起来虽然有些不太方便,但总体来说还是可以接受的,是有想换个终端的想法,然后今天偶然看到一个终端利器 iTerm2,发现真的很强大,也非常的好用,按照网上配置了 ...
- DEDECMS最新5.7版在Windows下的Memcache安装
一,织梦后台后台设置进入系统后台,在[系统基本参数]下面的"性能选项"卡当中,关于memcache进行如下配置: cfg_memcache_enable : 是否启用memcach ...
- 基于POI和DOM4将Excel(2007)文档写进Xml文件
刚进公司的training, 下面是要求: Requirements Write a java program to read system.xlsx Use POI API to parse all ...