|
接下来我们对依赖属性进行一个简单的剖析,从以下几个方面入手吧。
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.
- <p>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;</p>
- <h1 id="pragma-warning-disable-1634-1691---suppressing-presharp-warnings">pragma warning disable 1634, 1691 // suppressing PreSharp warnings</h1>
- <p>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);
- }</p>
- [code] /// <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;
- }
- }
- }
- }
复制代码 }
}
[/code]
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;
- <p>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; }
- }
- }</p>
复制代码
此时我们将普通属性换位依赖属性我们再看看- 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;
- <p>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);
- }
- }</p>
- [code] 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; }
- //}
复制代码 }
[/code]
然后我们再循环一千万次
那么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;
- };
- <p>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;
- }
- });</p>
复制代码 在写代码是都会考虑可能发生的错误。在定义属性时,也需要考虑错误设置属性的可能性。对于传统.NET属性,可以在属性的设置器中进行属性值的验证,不满足条件的值可以抛出异常。但对于依赖属性来说,这种方法不合适,因为依赖属性通过SetValue方法来直接设置其值的。然而WPF有其代替的方式,WPF中提供了两种方法来用于验证依赖属性的值。
1、ValidateValueCallback:该回调函数可以接受或拒绝新值。该值可作为DependencyProperty.Register方法的一个参数。
2、CoerceValueCallback:该回调函数可将新值强制修改为可被接受的值。例如某个依赖属性工作年龄的值范围是25到55,在该回调函数中,可以对设置的值进行强制修改,对于不满足条件的值,强制修改为满足条件的值。如当设置为负值时,可强制修改为0。该回调函数PropertyMetadata构造函数参数进行传递。
来源:https://www.cnblogs.com/iceamos/archive/2023/03/15/17218242.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作! |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
x
|