// Using generics simplifies the type check A LOT, so this is preferred.
        /// <summary>
        /// Sets the value of an attached property on this control.
        /// </summary>
        /// <param name="property">The attached property to set.</param>
        /// <param name="value">The new value.</param>
        /// <typeparam name="T">
        /// The type of the value being assigned.
        /// </typeparam>
        /// <exception cref="InvalidOperationException">
        /// Thrown if the type of <paramref name="value"/> is not assignable to the property's type.
        /// </exception>
        /// <exception cref="ArgumentException">
        /// Thrown if the property has a validation function, and the validation function failed.
        /// </exception>
        public void SetValue <T>(AttachedProperty property, T value)
        {
            if (_attachedProperties == null)
            {
                _attachedProperties = new Dictionary <AttachedProperty, object?>();
            }

            if (!property.PropertyType.IsAssignableFrom(typeof(T)))
            {
                throw new InvalidOperationException("Property is of the wrong type.");
            }

            if (typeof(T) == typeof(object))
            {
                SetValue(property, (object?)value);
                return;
            }

            if (property.Validate != null && !property.Validate(value))
            {
                throw new ArgumentException("Value is not valid for this property.", nameof(value));
            }

            if (!_attachedProperties.TryGetValue(property, out var oldValue))
            {
                oldValue = property.DefaultValue;
            }

            var changed = new AttachedPropertyChangedEventArgs(value, oldValue);

            _attachedProperties[property] = value;

            property.Changed?.Invoke(this, changed);
        }
        public void SetValue <T>(AttachedProperty <T> property, T value)
        {
            _attachedProperties ??= new Dictionary <AttachedProperty, object?>();

            if (property.Validate != null && !property.Validate(value))
            {
                throw new ArgumentException("Value is not valid for this property.", nameof(value));
            }

            T oldValue;

            if (!_attachedProperties.TryGetValue(property, out var oldValueBoxed))
            {
                oldValue = property.DefaultValue;
            }
            else
            {
                oldValue = (T)oldValueBoxed !;
            }

            var changed = new AttachedPropertyChangedEventArgs <T>(value, oldValue);

            _attachedProperties[property] = value;

            property.Changed?.Invoke(this, changed);
        }
        /// <summary>
        /// Sets the value of an attached property on this control.
        /// </summary>
        /// <remarks>
        /// If possible, it is recommended to use <see cref="SetValue{T}"/> instead.
        /// </remarks>
        /// <param name="property">The attached property to set.</param>
        /// <param name="value">The new value.</param>
        /// <exception cref="ArgumentNullException">
        /// Thrown if the property type is a non-nullable value type, but <paramref name="value"/> is null.
        /// </exception>
        /// <exception cref="ArgumentException">
        /// Thrown if the type of <paramref name="value"/> is not assignable to the property's type.
        /// </exception>
        /// <exception cref="ArgumentException">
        /// Thrown if the property has a validation function, and the validation function failed.
        /// </exception>
        public void SetValue(AttachedProperty property, object?value)
        {
            if (_attachedProperties == null)
            {
                _attachedProperties = new Dictionary <AttachedProperty, object?>();
            }

            // Verify that the value can be assigned to the property.
            if (value == null)
            {
                if (property.PropertyType.IsValueType &&
                    // Nullable<T> actually boxes as null. Keep that in mind.
                    Nullable.GetUnderlyingType(property.PropertyType) == null)
                {
                    throw new ArgumentNullException(nameof(value),
                                                    "Property is a value type, but null was passed as value.");
                }
            }
            else
            {
                if (!property.PropertyType.IsInstanceOfType(value))
                {
                    throw new ArgumentException("Value is of wrong type for property.", nameof(value));
                }
            }

            if (property.Validate != null && !property.Validate(value))
            {
                throw new ArgumentException("Value is not valid for this property.", nameof(value));
            }

            if (!_attachedProperties.TryGetValue(property, out var oldValue))
            {
                oldValue = property.DefaultValue;
            }

            var changed = new AttachedPropertyChangedEventArgs(value, oldValue);

            _attachedProperties[property] = value;

            property.Changed?.Invoke(this, changed);
        }