接下來我們對依賴屬性進行一個簡單的剖析,從以下幾個方面入手吧。 1 - 為什麼是public static 首先說下為什麼是public 答:WPF有一種特殊屬性,叫附加屬性,需要直接訪問xxxxProperty的方法才能實現,所以xxxxProperty是public 的。 其次為什麼是靜態sta ...
接下來我們對依賴屬性進行一個簡單的剖析,從以下幾個方面入手吧。
1 - 為什麼是public static
首先說下為什麼是public
答:WPF有一種特殊屬性,叫附加屬性,需要直接訪問xxxxProperty的方法才能實現,所以xxxxProperty是public 的。
其次為什麼是靜態static
答:和依賴屬性的實現有關,也就是說,一個類,不管同一個依賴屬性有多少個實例,均對應同一個DependencyProperty 。比如你創建100個Control ,每個Control 都有一個FontSize屬性,但這100個FontSize均對應同一個FontSizeProperty實例
2 - DependencyProperty具體什麼時候用?
比如你創建用戶控制項,然後呢,你需要傳遞到其中一些值,你怎麼去處理,按照傳統的方式你闊以去試試看看能否完成!所以這時候就提現出來了依賴屬性的重要性
平常使用WPF已經封裝好的那些的話更多時候是你拿來直接用的。
3 - DependencyProperty實現原理是什麼?
實現原理的話要是解釋的話很複雜。不如直接貼上源代碼,自己分析去吧,哈哈
// 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.
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 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 == 0) { throw new ArgumentException(SR.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.Format(SR.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.Format(SR.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.Format(SR.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.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.Format(SR.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.Format(SR.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.TypeMetadataAlreadyInUse); } if (!typeof(DependencyObject).IsAssignableFrom(forType)) { throw new ArgumentException(SR.Format(SR.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.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.Format(SR.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.Format(SR.ReadOnlyOverrideKeyNotAuthorized, Name)); } VerifyReadOnlyKey(key); } else { throw new InvalidOperationException(SR.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.Format(SR.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> 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 - 1; int Id; object value; if (index < 0) { // No overrides or it's the base class return _defaultMetadata; } else if (index == 0) { // 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 (0 != 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 >= 0)) { _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 >= 0); } } } 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.Format(SR.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) != 0; } } internal bool IsValueType { get { return (_packedData & Flags.IsValueType) != 0; } } internal bool IsFreezableType { get { return (_packedData & Flags.IsFreezableType) != 0; } } internal bool IsStringType { get { return (_packedData & Flags.IsStringType) != 0; } } internal bool IsPotentiallyInherited { get { return (_packedData & Flags.IsPotentiallyInherited) != 0; } } internal bool IsDefaultValueChanged { get { return (_packedData & Flags.IsDefaultValueChanged) != 0; } } internal bool IsPotentiallyUsingDefaultValueFactory { get { return (_packedData & Flags.IsPotentiallyUsingDefaultValueFactory) != 0; } } /// <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.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.Format(SR.TooManyDependencyProperties, ownerType.Name + "." + name)); } else { throw new InvalidOperationException(SR.Format(SR.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.Format(SR.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>(768); // 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; } } } }
}
}
4 - 依賴屬性的好處怎麼提現?
WPF的設計理念是:數據驅動,UI與邏輯松耦合。我們從這個上邊可以去研究一下。
public class Person
{
private string _Name;
public string Name
{
get
{
return _Name;
}
set
{
_Name = value;
}
}
}
但是呢,CLR屬性有個特點,在多級繼承的情況下,每次繼承,父類的欄位都被繼承,孫孫輩對象占用記憶體空間不可避免的膨脹。
那麼說在基於這個缺點的情況下,依賴屬性呢解決了一部分問題
- 在多級繼承,大多數欄位並沒有被修改的情況下,如何少對象的體積。
- 數據驅動指導思想下,數據如何保存簡單一致,同步
// 1. 使類型繼承DependencyObject類
public class Person : DependencyObject
{
// 2. 聲明一個靜態只讀的DependencyProperty 欄位
public static readonly DependencyProperty nameProperty;
static Person()
{
// 3. 註冊定義的依賴屬性
nameProperty = DependencyProperty.Register("Name", typeof(string), typeof(Person),
new PropertyMetadata("Learning Hard",OnValueChanged));
}
// 4. 屬性包裝器,通過它來讀取和設置我們剛纔註冊的依賴屬性
public string Name
{
get { return (string)GetValue(nameProperty); }
set { SetValue(nameProperty, value); }
}
private static void OnValueChanged(DependencyObject dpobj, DependencyPropertyChangedEventArgs e)
{
// 當只發生改變時回調的方法
}
}
優勢在那裡呢?
1、解決多級繼承,且大多數欄位值不改變的情況下,減少記憶體占比
將一個DependencyProperty對象存儲在一個全局的Hashtable中;通過依賴對象(DependencyObject)的GetValue和SetValue存取數據;
2、以數據為中心,當數據源改變時,所以關聯的UI數據改變;
依賴屬性值可以通過Binding依賴於其它對象上,這就使得數據源一變動;依賴於此數據源的依賴屬性全部進行更新
這裡呢貼一個鏈接可以參考參考:
https://www.cnblogs.com/Zhouyongh/archive/2009/09/10/1564099.html5 - 依賴屬性怎麼解決記憶體占比問題呢?
首先呢,我們先確認一下對於依賴屬性而言,到底有沒有節約記憶體。我們寫個Demo就能明顯看出來
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes;
namespace FifthWPFDemo
{
/// <summary>
/// MainWindow.xaml 的交互邏輯
/// </summary>
public partial class MainWindow : Window
{
Person per;
public MainWindow()
{
InitializeComponent();
List<Person> list = new List<Person>();
for (int i = 0; i < 10000000; i++)
{
per = new Person();
list.Add(per);
}
}
}
public class Person
{
public double Name { get; set; }
public double Name1 { get; set; }
public double Name2 { get; set; }
public double Name3 { get; set; }
public double Name4 { get; set; }
public double Name5 { get; set; }
public double Name6 { get; set; }
public double Name7 { get; set; }
public double Name8 { get; set; }
public double Name9 { get; set; }
public double Name10 { get; set; }
}
}
此時我們將普通屬性換位依賴屬性我們再看看
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes;
namespace FifthWPFDemo
{
/// <summary>
/// MainWindow.xaml 的交互邏輯
/// </summary>
public partial class MainWindow : Window
{
Person per;
public MainWindow()
{
InitializeComponent();
List<Person> list = new List<Person>();
for (int i = 0; i < 10000000; i++)
{
per = new Person();
list.Add(per);
}
}
}
public class Person : DependencyObject
{
public double Name
{
get
{
return (double)GetValue(NameProperty);
}
set
{
SetValue(NameProperty, value);
}
}
public double Name1
{
get
{
return (double)GetValue(Name1Property);
}
set
{
SetValue(Name1Property, value);
}
}
public double Name2
{
get
{
return (double)GetValue(Name2Property);
}
set
{
SetValue(Name2Property, value);
}
}
public double Name3
{
get
{
return (double)GetValue(Name3Property);
}
set
{
SetValue(Name3Property, value);
}
}public double Name4 { get { return (double)GetValue(Name4Property); } set { SetValue(Name4Property, value); } } public double Name5 { get { return (double)GetValue(Name5Property); } set { SetValue(Name5Property, value); } } public double Name6 { get { return (double)GetValue(Name6Property); } set { SetValue(Name6Property, value); } } public double Name7 { get { return (double)GetValue(Name7Property); } set { SetValue(Name7Property, value); } } public double Name8 { get { return (double)GetValue(Name8Property); } set { SetValue(Name8Property, value); } } public double Name9 { get { return (double)GetValue(Name9Property); } set { SetValue(Name9Property, value); } } public double Name10 { get { return (double)GetValue(Name10Property); } set { SetValue(Name10Property, value); } } public static readonly DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(double), typeof(Person), new PropertyMetadata((double)55.55)); public static readonly DependencyProperty Name1Property = DependencyProperty.Register("Name1", typeof(double), typeof(Person), new PropertyMetadata((double)55.55)); public static readonly DependencyProperty Name2Property = DependencyProperty.Register("Name2", typeof(double), typeof(Person), new PropertyMetadata((double)55.55)); public static readonly DependencyProperty Name3Property = DependencyProperty.Register("Name3", typeof(double), typeof(Person), new PropertyMetadata((double)55.55)); public static readonly DependencyProperty Name4Property = DependencyProperty.Register("Name4", typeof(double), typeof(Person), new PropertyMetadata((double)55.55)); public static readonly DependencyProperty Name5Property = DependencyProperty.Register("Name5", typeof(double), typeof(Person), new PropertyMetadata((double)55.55)); public static readonly DependencyProperty Name6Property = DependencyProperty.Register("Name6", typeof(double), typeof(Person), new PropertyMetadata((double)55.55)); public static readonly DependencyProperty Name7Property = DependencyProperty.Register("Name7", typeof(double), typeof(Person), new PropertyMetadata((double)55.55)); public static readonly DependencyProperty Name8Property = DependencyProperty.Register("Name8", typeof(double), typeof(Person), new PropertyMetadata((double)55.55)); public static readonly DependencyProperty Name9Property = DependencyProperty.Register("Name9", typeof(double), typeof(Person), new PropertyMetadata((double)55.55)); public static readonly DependencyProperty Name10Property = DependencyProperty.Register("Name10", typeof(double), typeof(Person), new PropertyMetadata((double)55.55)); } //public class Person //{ // public double Name { get; set; } // public double Name1 { get; set; } // public double Name2 { get; set; } // public double Name3 { get; set; } // public double Name4 { get; set; } // public double Name5 { get; set; } // public double Name6 { get; set; } // public double Name7 { get; set; } // public double Name8 { get; set; } // public double Name9 { get; set; } // public double Name10 { get; set; } //}
}
然後我們再迴圈一千萬次
那麼WPF的屬性到底是如何節約記憶體的呢。因為CLR屬性是在實例聲明的時候就分配好了記憶體空間的。所以就算實例裡面沒有寫入值,或者仍然是預設值,仍然會分配好記憶體空間。但是WPF的依賴屬性不同。
所以依賴屬性正在節約記憶體就在於這兒的依賴屬性是一個static readonly 屬性。所以不需要在對象每次實例化的時候都分配相關屬性的記憶體空間,而是提供一個入口點。
6 - 依賴屬性的回寫和強制轉換
//註冊依賴屬性 PropertyMetadata propertyMetadata = new PropertyMetadata(); propertyMetadata.DefaultValue = Brushes.DeepSkyBlue; //屬性值改變回寫 propertyMetadata.PropertyChangedCallback = ((s, e) => { Debug.WriteLine(String.Format("PropertyChanged - 屬性:{0} 新值:{1} 舊值:{2}", e.Property.Name, e.NewValue, e.OldValue)); }); //強制轉換 propertyMetadata.CoerceValueCallback = (s, e) => { Debug.WriteLine(String.Format("CoerceValue - {0}", e)); return e; };
MyColorProperty =
DependencyProperty.Register("MyColor", typeof(Brush), typeof(MyButton), propertyMetadata, (o) => {
Brush brush = o as Brush;
if (brush== Brushes.Yellow||brush== Brushes.Blue)
{
return false;
}
else
{
return true;
}
});
在寫代碼是都會考慮可能發生的錯誤。在定義屬性時,也需要考慮錯誤設置屬性的可能性。對於傳統.NET屬性,可以在屬性的設置器中進行屬性值的驗證,不滿足條件的值可以拋出異常。但對於依賴屬性來說,這種方法不合適,因為依賴屬性通過SetValue方法來直接設置其值的。然而WPF有其代替的方式,WPF中提供了兩種方法來用於驗證依賴屬性的值。
1、ValidateValueCallback:該回調函數可以接受或拒絕新值。該值可作為DependencyProperty.Register方法的一個參數。
2、CoerceValueCallback:該回調函數可將新值強制修改為可被接受的值。例如某個依賴屬性工作年齡的值範圍是25到55,在該回調函數中,可以對設置的值進行強制修改,對於不滿足條件的值,強制修改為滿足條件的值。如當設置為負值時,可強制修改為0。該回調函數PropertyMetadata構造函數參數進行傳遞。