WPF學習筆記07-簡析依賴屬性

来源:https://www.cnblogs.com/iceamos/archive/2023/03/15/17218242.html
-Advertisement-
Play Games

接下來我們對依賴屬性進行一個簡單的剖析,從以下幾個方面入手吧。 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);
}

    /// &lt;summary&gt;
    ///     Register a Dependency Property
    /// &lt;/summary&gt;
    /// &lt;param name="name"&gt;Name of property&lt;/param&gt;
    /// &lt;param name="propertyType"&gt;Type of the property&lt;/param&gt;
    /// &lt;param name="ownerType"&gt;Type that is registering the property&lt;/param&gt;
    /// &lt;param name="typeMetadata"&gt;Metadata to use if current type doesn't specify type-specific metadata&lt;/param&gt;
    /// &lt;returns&gt;Dependency Property&lt;/returns&gt;
    public static DependencyProperty Register(string name, Type propertyType, Type ownerType, PropertyMetadata typeMetadata)
    {
        // Forwarding
        return Register(name, propertyType, ownerType, typeMetadata, null);
    }

    /// &lt;summary&gt;
    ///     Register a Dependency Property
    /// &lt;/summary&gt;
    /// &lt;param name="name"&gt;Name of property&lt;/param&gt;
    /// &lt;param name="propertyType"&gt;Type of the property&lt;/param&gt;
    /// &lt;param name="ownerType"&gt;Type that is registering the property&lt;/param&gt;
    /// &lt;param name="typeMetadata"&gt;Metadata to use if current type doesn't specify type-specific metadata&lt;/param&gt;
    /// &lt;param name="validateValueCallback"&gt;Provides additional value validation outside automatic type validation&lt;/param&gt;
    /// &lt;returns&gt;Dependency Property&lt;/returns&gt;
    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 &amp;&amp; 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;
    }

    /// &lt;summary&gt;
    ///  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.
    /// &lt;/summary&gt;
    public static DependencyPropertyKey RegisterReadOnly(
        string name,
        Type propertyType,
        Type ownerType,
        PropertyMetadata typeMetadata )
    {
        return RegisterReadOnly( name, propertyType, ownerType, typeMetadata, null );
    }

    /// &lt;summary&gt;
    ///  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.
    /// &lt;/summary&gt;
    public static DependencyPropertyKey RegisterReadOnly(
        string name,
        Type propertyType,
        Type ownerType,
        PropertyMetadata typeMetadata,
        ValidateValueCallback validateValueCallback )
    {
        RegisterParameterValidation(name, propertyType, ownerType);

        PropertyMetadata defaultMetadata = null;

        if (typeMetadata != null &amp;&amp; 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;
    }

    /// &lt;summary&gt;
    ///  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.
    /// &lt;/summary&gt;
    public static DependencyPropertyKey RegisterAttachedReadOnly(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata)
    {
        return RegisterAttachedReadOnly( name, propertyType, ownerType, defaultMetadata, null );
    }

    /// &lt;summary&gt;
    ///  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.
    /// &lt;/summary&gt;
    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;
    }

    /// &lt;summary&gt;
    ///     Register an attached Dependency Property
    /// &lt;/summary&gt;
    /// &lt;param name="name"&gt;Name of property&lt;/param&gt;
    /// &lt;param name="propertyType"&gt;Type of the property&lt;/param&gt;
    /// &lt;param name="ownerType"&gt;Type that is registering the property&lt;/param&gt;
    /// &lt;returns&gt;Dependency Property&lt;/returns&gt;
    public static DependencyProperty RegisterAttached(string name, Type propertyType, Type ownerType)
    {
        // Forwarding
        return RegisterAttached(name, propertyType, ownerType, null, null );
    }

    /// &lt;summary&gt;
    ///     Register an attached Dependency Property
    /// &lt;/summary&gt;
    /// &lt;param name="name"&gt;Name of property&lt;/param&gt;
    /// &lt;param name="propertyType"&gt;Type of the property&lt;/param&gt;
    /// &lt;param name="ownerType"&gt;Type that is registering the property&lt;/param&gt;
    /// &lt;param name="defaultMetadata"&gt;Metadata to use if current type doesn't specify type-specific metadata&lt;/param&gt;
    /// &lt;returns&gt;Dependency Property&lt;/returns&gt;
    public static DependencyProperty RegisterAttached(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata)
    {
        // Forwarding
        return RegisterAttached(name, propertyType, ownerType, defaultMetadata, null );
    }

    /// &lt;summary&gt;
    ///     Register an attached Dependency Property
    /// &lt;/summary&gt;
    /// &lt;param name="name"&gt;Name of property&lt;/param&gt;
    /// &lt;param name="propertyType"&gt;Type of the property&lt;/param&gt;
    /// &lt;param name="ownerType"&gt;Type that is registering the property&lt;/param&gt;
    /// &lt;param name="defaultMetadata"&gt;Metadata to use if current type doesn't specify type-specific metadata&lt;/param&gt;
    /// &lt;param name="validateValueCallback"&gt;Provides additional value validation outside automatic type validation&lt;/param&gt;
    /// &lt;returns&gt;Dependency Property&lt;/returns&gt;
    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 &amp;&amp;
            !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 &amp;&amp; dispatcherObject.Dispatcher != null)
            {
                // Try to make the DispatcherObject free-threaded if it's an
                // ISealable.

                ISealable valueAsISealable = dispatcherObject as ISealable;

                if (valueAsISealable != null &amp;&amp; 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 &amp;&amp;
            !validateValueCallback(defaultValue))
        {
            throw new ArgumentException(SR.Format(SR.DefaultValueInvalid, propertyName));
        }
    }


    /// &lt;summary&gt;
    ///     Parameter validation for OverrideMetadata, includes code to force
    /// all base classes of "forType" to register their metadata so we know
    /// what we are overriding.
    /// &lt;/summary&gt;
    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);
        }
    }


    /// &lt;summary&gt;
    ///     Supply metadata for given type &amp; run static constructors if needed.
    /// &lt;/summary&gt;
    /// &lt;remarks&gt;
    ///     The supplied metadata will be merged with the type's base
    ///     metadata
    /// &lt;/remarks&gt;
    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);
    }

    /// &lt;summary&gt;
    ///     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.
    /// &lt;/summary&gt;
    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);
    }

    /// &lt;summary&gt;
    ///     After parameters have been validated for OverrideMetadata, this
    /// method is called to actually update the data structures.
    /// &lt;/summary&gt;
    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() &amp;&amp; (typeMetadata.DefaultValue != DefaultMetadata.DefaultValue))
        {
            _packedData |= Flags.IsDefaultValueChanged;
        }

        if (typeMetadata.UsingDefaultValueFactory)
        {
            _packedData |= Flags.IsPotentiallyUsingDefaultValueFactory;
        }
    }


    [FriendAccessAllowed]   // Built into Base, also used by Core &amp; Framework.
    internal object GetDefaultValue(DependencyObjectType dependencyObjectType)
    {
        if (!IsDefaultValueChanged)
        {
            return DefaultMetadata.DefaultValue;
        }

        return GetMetadata(dependencyObjectType).DefaultValue;
    }

    [FriendAccessAllowed]   // Built into Base, also used by Core &amp; Framework.
    internal object GetDefaultValue(Type forType)
    {
        if (!IsDefaultValueChanged)
        {
            return DefaultMetadata.DefaultValue;
        }

        return GetMetadata(DependencyObjectType.FromSystemTypeInternal(forType)).DefaultValue;
    }

    /// &lt;summary&gt;
    ///     Retrieve metadata for a provided type
    /// &lt;/summary&gt;
    /// &lt;param name="forType"&gt;Type to get metadata&lt;/param&gt;
    /// &lt;returns&gt;Property metadata&lt;/returns&gt;
    public PropertyMetadata GetMetadata(Type forType)
    {
        if (forType != null)
        {
            return GetMetadata(DependencyObjectType.FromSystemType(forType));
        }
        throw new ArgumentNullException("forType");
    }

    /// &lt;summary&gt;
    ///     Retrieve metadata for a provided DependencyObject
    /// &lt;/summary&gt;
    /// &lt;param name="dependencyObject"&gt;DependencyObject to get metadata&lt;/param&gt;
    /// &lt;returns&gt;Property metadata&lt;/returns&gt;
    public PropertyMetadata GetMetadata(DependencyObject dependencyObject)
    {
        if (dependencyObject != null)
        {
            return GetMetadata(dependencyObject.DependencyObjectType);
        }
        throw new ArgumentNullException("dependencyObject");
    }

    /// &lt;summary&gt;
    /// Reteive metadata for a DependencyObject type described by the
    /// given DependencyObjectType
    /// &lt;/summary&gt;
    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 &lt; 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 &gt; 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 &lt; Id) &amp;&amp; (index &gt;= 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 &gt; Id)
                        {
                            dependencyObjectType = dependencyObjectType.BaseType;
                        }

                        if (Id == dependencyObjectType.Id)
                        {
                            // Return the override
                            return (PropertyMetadata)value;
                        }
                    }
                    while (index &gt;= 0);
                }
            }
        }
        return _defaultMetadata;
    }


    /// &lt;summary&gt;
    ///     Associate another owner type with this property
    /// &lt;/summary&gt;
    /// &lt;remarks&gt;
    ///     The owner type is used when resolving a property by name (&lt;see cref="FromName"/&gt;)
    /// &lt;/remarks&gt;
    /// &lt;param name="ownerType"&gt;Additional owner type&lt;/param&gt;
    /// &lt;returns&gt;This property&lt;/returns&gt;
    public DependencyProperty AddOwner(Type ownerType)
    {
        // Forwarding
        return AddOwner(ownerType, null);
    }

    /// &lt;summary&gt;
    ///     Associate another owner type with this property
    /// &lt;/summary&gt;
    /// &lt;remarks&gt;
    ///     The owner type is used when resolving a property by name (&lt;see cref="FromName"/&gt;)
    /// &lt;/remarks&gt;
    /// &lt;param name="ownerType"&gt;Additional owner type&lt;/param&gt;
    /// &lt;param name="typeMetadata"&gt;Optional type metadata to override on owner's behalf&lt;/param&gt;
    /// &lt;returns&gt;This property&lt;/returns&gt;
    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;
    }


    /// &lt;summary&gt;
    ///     Name of the property
    /// &lt;/summary&gt;
    public string Name
    {
        get { return _name; }
    }

    /// &lt;summary&gt;
    ///     Type of the property
    /// &lt;/summary&gt;
    public Type PropertyType
    {
        get { return _propertyType; }
    }

    /// &lt;summary&gt;
    ///     Owning type of the property
    /// &lt;/summary&gt;
    public Type OwnerType
    {
        get { return _ownerType; }
    }

    /// &lt;summary&gt;
    ///     Default metadata for the property
    /// &lt;/summary&gt;
    public PropertyMetadata DefaultMetadata
    {
        get { return _defaultMetadata; }
    }

    /// &lt;summary&gt;
    ///     Value validation callback
    /// &lt;/summary&gt;
    public ValidateValueCallback ValidateValueCallback
    {
        get { return _validateValueCallback; }
    }

    /// &lt;summary&gt;
    ///     Zero-based globally unique index of the property
    /// &lt;/summary&gt;
    public int GlobalIndex
    {
        get { return (int) (_packedData &amp; Flags.GlobalIndexMask); }
    }

    internal bool IsObjectType
    {
        get { return (_packedData &amp; Flags.IsObjectType) != 0; }
    }

    internal bool IsValueType
    {
        get { return (_packedData &amp; Flags.IsValueType) != 0; }
    }

    internal bool IsFreezableType
    {
        get { return (_packedData &amp; Flags.IsFreezableType) != 0; }
    }

    internal bool IsStringType
    {
        get { return (_packedData &amp; Flags.IsStringType) != 0; }
    }

    internal bool IsPotentiallyInherited
    {
        get { return (_packedData &amp; Flags.IsPotentiallyInherited) != 0; }
    }

    internal bool IsDefaultValueChanged
    {
        get { return (_packedData &amp; Flags.IsDefaultValueChanged) != 0; }
    }

    internal bool IsPotentiallyUsingDefaultValueFactory
    {
        get { return (_packedData &amp; Flags.IsPotentiallyUsingDefaultValueFactory) != 0; }
    }

    /// &lt;summary&gt;
    ///     Serves as a hash function for a particular type, suitable for use in
    ///     hashing algorithms and data structures like a hash table
    /// &lt;/summary&gt;
    /// &lt;returns&gt;The DependencyProperty's GlobalIndex&lt;/returns&gt;
    public override int GetHashCode()
    {
        return GlobalIndex;
    }

    /// &lt;summary&gt;
    ///     Used to determine if given value is appropriate for the type of the property
    /// &lt;/summary&gt;
    /// &lt;param name="value"&gt;Value to check&lt;/param&gt;
    /// &lt;returns&gt;true if value matches property type&lt;/returns&gt;
    public bool IsValidType(object value)
    {
        return IsValidType(value, PropertyType);
    }


    /// &lt;summary&gt;
    ///     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
    /// &lt;/summary&gt;
    /// &lt;param name="value"&gt;Value to check&lt;/param&gt;
    /// &lt;returns&gt;true if value is appropriate&lt;/returns&gt;
    public bool IsValidValue(object value)
    {
        if (!IsValidType(value, PropertyType))
        {
            return false;
        }

        if (ValidateValueCallback != null)
        {
            // CALLBACK
            return ValidateValueCallback(value);
        }

        return true;
    }

    /// &lt;summary&gt;
    ///     Set/Value value disabling
    /// &lt;/summary&gt;
    public bool ReadOnly
    {
        get
        {
            return (_readOnlyKey != null);
        }
    }

    /// &lt;summary&gt;
    ///     Returns the DependencyPropertyKey associated with this DP.
    /// &lt;/summary&gt;
    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);
        }
    }

    /// &lt;summary&gt;
    ///     Internal version of IsValidValue that bypasses IsValidType check;
    ///     Called from SetValueInternal
    /// &lt;/summary&gt;
    /// &lt;param name="value"&gt;Value to check&lt;/param&gt;
    /// &lt;returns&gt;true if value is appropriate&lt;/returns&gt;
    internal bool IsValidValueInternal(object value)
    {
        if (ValidateValueCallback != null)
        {
            // CALLBACK
            return ValidateValueCallback(value);
        }

        return true;
    }

    /// &lt;summary&gt;
    ///     Find a property from name
    /// &lt;/summary&gt;
    /// &lt;remarks&gt;
    ///     Search includes base classes of the provided type as well
    /// &lt;/remarks&gt;
    /// &lt;param name="name"&gt;Name of the property&lt;/param&gt;
    /// &lt;param name="ownerType"&gt;Owner type of the property&lt;/param&gt;
    /// &lt;returns&gt;Dependency property&lt;/returns&gt;
    [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) &amp;&amp; (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;
    }


    /// &lt;summary&gt;
    ///    String representation
    /// &lt;/summary&gt;
    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 &amp;&amp;
                !(propertyType.IsGenericType &amp;&amp; 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) &amp;&amp; (o is FromNameKey))
            {
                return Equals((FromNameKey)o);
            }
            else
            {
                return false;
            }
        }

        public bool Equals(FromNameKey key)
        {
            return (_name.Equals(key._name) &amp;&amp; (_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 &gt;= (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++;
    }

    /// &lt;summary&gt;
    /// 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.
    /// &lt;/summary&gt;
    internal CoerceValueCallback DesignerCoerceValueCallback
    {
        get {  return _designerCoerceValueCallback; }
        set
        {
            if (ReadOnly)
            {
                throw new InvalidOperationException(SR.Format(SR.ReadOnlyDesignerCoersionNotAllowed, Name));
            }

            _designerCoerceValueCallback = value;
        }
    }

    /// &lt;summary&gt; Standard unset value &lt;/summary&gt;
    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&lt;DependencyProperty&gt; RegisteredPropertyList = new ItemStructList&lt;DependencyProperty&gt;(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&lt;&gt;);

    /// &lt;summary&gt;
    ///     Returns the number of all registered properties.
    /// &lt;/summary&gt;
    internal static int RegisteredPropertyCount {
        get {
            return RegisteredPropertyList.Count;
        }
    }

    /// &lt;summary&gt;
    ///     Returns an enumeration of properties that are
    ///     currently registered.
    ///     Synchronized (write locks, lock-free reads): Covered by DependencyProperty.Synchronized
    /// &lt;/summary&gt;
    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.html

5 - 依賴屬性怎麼解決記憶體占比問題呢?

首先呢,我們先確認一下對於依賴屬性而言,到底有沒有節約記憶體。我們寫個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構造函數參數進行傳遞。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Qt 學習筆記全系列傳送門: Qt 學習筆記 - 第一章 - 快速開始、信號與槽 Qt 學習筆記 - 第二章 - 添加圖片、佈局、界面切換 Qt 學習筆記 - 第三章 - Qt的三駕馬車之一 - 串口編程 + 程式打包成Windows軟體 Qt 學習筆記 - 第四章 - Qt的三駕馬車之二 - 網路 ...
  • 讀寫Excel基本代碼 直接複製不一定能用 實體類 @ExcelIgnore 在導出操作中不會被導出 @ExcelProperty 在導入過程中 可以根據導入模板自動匹配欄位, 在導出過程中可用於設置導出的標題名字 @Getter @Setter public class Material{ @Ex ...
  • TypeScript 備忘清單 IT寶庫TypeScript開發速查清單包含最重要基礎、泛型、方法、class 等 TypeScript 強類型編程語言語法的快速參考備忘單。初學者的完整快速參考入門,為開發人員分享快速參考備忘單。 開發速查表大綱 入門 Interface 介紹 內置類型基元 常見的 ...
  • Qt 學習筆記全系列傳送門: Qt 學習筆記 - 第一章 - 快速開始、信號與槽 Qt 學習筆記 - 第二章 - 添加圖片、佈局、界面切換 Qt 學習筆記 - 第三章 - Qt的三駕馬車之一 - 串口編程 + 程式打包成Windows軟體 【本章】Qt 學習筆記 - 第四章 - Qt的三駕馬車之二 ...
  • 背景 日常開發中,經常需要對一些響應不是很快的關鍵業務介面增加防重功能,即短時間內收到的多個相同的請求,只處理一個,其餘不處理,避免產生臟數據。這和冪等性(idempotency)稍微有點區別,冪等性要求的是對重覆請求有相同的效果和結果,通常需要在介面內部執行業務操作前檢查狀態;而防重可以認為是一個 ...
  • 預覽 技術實現 看過我上篇在 WPF 中實現 OpenGL 與 D3D 渲染的同學應該知道,我是依靠 WGL 中 WGL_NV_DX_interop 擴展與 D3D Surface 關聯併在使用該 Surface 實現渲染。 所以我們這次實現也是如此,但與 WPF 不同的是 WinUI 支持 D3D ...
  • 需求 swagger頁面按標簽Tags分組顯示。 沒有打標簽Tags的介面,預設歸到"未分組"。 分組內按介面路徑排序 說明 為什麼沒有使用GroupName對介面進行分組? 暫時不需要,以及不想點擊swagger頁面右上角那個下拉框。 當然Tags和GroupName不衝突,不影響通過GroupN ...
  • 背景: 我們項目一開始的所有提示都是中文,後來要做國際化。發現項目中的帶雙引號的中文居然有 2.3 w 多條!!!簡直讓人欲哭無淚... 如果使用人工改的話,首先不說正確率了。光是效率都是難難難。所以發揮了自己的才能寫了一個自動化工具。 思路: 首選讀取項目文件夾下的所有文件路徑 篩選路徑文件尾碼. ...
一周排行
    -Advertisement-
    Play Games
  • 概述:在C#中,++i和i++都是自增運算符,其中++i先增加值再返回,而i++先返回值再增加。應用場景根據需求選擇,首碼適合先增後用,尾碼適合先用後增。詳細示例提供清晰的代碼演示這兩者的操作時機和實際應用。 在C#中,++i 和 i++ 都是自增運算符,但它們在操作上有細微的差異,主要體現在操作的 ...
  • 上次發佈了:Taurus.MVC 性能壓力測試(ap 壓測 和 linux 下wrk 壓測):.NET Core 版本,今天計劃準備壓測一下 .NET 版本,來測試並記錄一下 Taurus.MVC 框架在 .NET 版本的性能,以便後續持續優化改進。 為了方便對比,本文章的電腦環境和測試思路,儘量和... ...
  • .NET WebAPI作為一種構建RESTful服務的強大工具,為開發者提供了便捷的方式來定義、處理HTTP請求並返迴響應。在設計API介面時,正確地接收和解析客戶端發送的數據至關重要。.NET WebAPI提供了一系列特性,如[FromRoute]、[FromQuery]和[FromBody],用 ...
  • 原因:我之所以想做這個項目,是因為在之前查找關於C#/WPF相關資料時,我發現講解圖像濾鏡的資源非常稀缺。此外,我註意到許多現有的開源庫主要基於CPU進行圖像渲染。這種方式在處理大量圖像時,會導致CPU的渲染負擔過重。因此,我將在下文中介紹如何通過GPU渲染來有效實現圖像的各種濾鏡效果。 生成的效果 ...
  • 引言 上一章我們介紹了在xUnit單元測試中用xUnit.DependencyInject來使用依賴註入,上一章我們的Sample.Repository倉儲層有一個批量註入的介面沒有做單元測試,今天用這個示例來演示一下如何用Bogus創建模擬數據 ,和 EFCore 的種子數據生成 Bogus 的優 ...
  • 一、前言 在自己的項目中,涉及到實時心率曲線的繪製,項目上的曲線繪製,一般很難找到能直接用的第三方庫,而且有些還是定製化的功能,所以還是自己繪製比較方便。很多人一聽到自己畫就害怕,感覺很難,今天就分享一個完整的實時心率數據繪製心率曲線圖的例子;之前的博客也分享給DrawingVisual繪製曲線的方 ...
  • 如果你在自定義的 Main 方法中直接使用 App 類並啟動應用程式,但發現 App.xaml 中定義的資源沒有被正確載入,那麼問題可能在於如何正確配置 App.xaml 與你的 App 類的交互。 確保 App.xaml 文件中的 x:Class 屬性正確指向你的 App 類。這樣,當你創建 Ap ...
  • 一:背景 1. 講故事 上個月有個朋友在微信上找到我,說他們的軟體在客戶那邊隔幾天就要崩潰一次,一直都沒有找到原因,讓我幫忙看下怎麼回事,確實工控類的軟體環境複雜難搞,朋友手上有一個崩潰的dump,剛好丟給我來分析一下。 二:WinDbg分析 1. 程式為什麼會崩潰 windbg 有一個厲害之處在於 ...
  • 前言 .NET生態中有許多依賴註入容器。在大多數情況下,微軟提供的內置容器在易用性和性能方面都非常優秀。外加ASP.NET Core預設使用內置容器,使用很方便。 但是筆者在使用中一直有一個頭疼的問題:服務工廠無法提供請求的服務類型相關的信息。這在一般情況下並沒有影響,但是內置容器支持註冊開放泛型服 ...
  • 一、前言 在項目開發過程中,DataGrid是經常使用到的一個數據展示控制項,而通常表格的最後一列是作為操作列存在,比如會有編輯、刪除等功能按鈕。但WPF的原始DataGrid中,預設只支持固定左側列,這跟大家習慣性操作列放最後不符,今天就來介紹一種簡單的方式實現固定右側列。(這裡的實現方式參考的大佬 ...