File: System\Windows\Markup\StaticExtension.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\System.Xaml\System.Xaml.csproj (System.Xaml)
// 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.
 
#nullable disable
 
using System.ComponentModel;
using System.Reflection;
using System.Runtime.CompilerServices;
 
namespace System.Windows.Markup
{
    /// <summary>
    /// Class for Xaml markup extension for static field and property references.
    /// </summary>
    [TypeForwardedFrom("PresentationFramework, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")]
    [TypeConverter(typeof(StaticExtensionConverter))]
    [MarkupExtensionReturnType(typeof(object))]
    public class StaticExtension : MarkupExtension
    {
        private string _member;
        private Type _memberType;
 
        /// <summary>
        /// Constructor that takes no parameters
        /// </summary>
        public StaticExtension()
        {
        }
 
        /// <summary>
        /// Constructor that takes the member that this is a static reference to.
        /// This string is of the format Prefix:ClassName.FieldOrPropertyName.
        /// The Prefix is optional, and refers to the XML prefix in a Xaml file.
        /// </summary>
        public StaticExtension(string member)
        {
            _member = member ?? throw new ArgumentNullException(nameof(member));
        }
 
        /// <summary>
        /// Return an object that should be set on the targetObject's targetProperty
        /// for this markup extension. For a StaticExtension this is a static field
        /// or property value.
        /// </summary>
        /// <param name="serviceProvider">Object that can provide services for the markup extension.</param>
        /// <returns> The object to set on this property.</returns>
        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            if (_member is null)
            {
                throw new InvalidOperationException(SR.MarkupExtensionStaticMember);
            }
 
            Type type = MemberType;
            string fieldString;
            string typeNameForError = null;
            if (type is not null)
            {
                fieldString = _member;
                typeNameForError = type.FullName;
            }
            else
            {
                // Validate the _member
                int dotIndex = _member.IndexOf('.');
                if (dotIndex < 0)
                {
                    throw new ArgumentException(SR.Format(SR.MarkupExtensionBadStatic, _member));
                }
 
                // Pull out the type substring (this will include any XML prefix, e.g. "av:Button")
                string typeString = _member.Substring(0, dotIndex);
                if (string.IsNullOrEmpty(typeString))
                {
                    throw new ArgumentException(SR.Format(SR.MarkupExtensionBadStatic, _member));
                }
 
                // Get the IXamlTypeResolver from the service provider
 
                ArgumentNullException.ThrowIfNull(serviceProvider);
 
                IXamlTypeResolver xamlTypeResolver = serviceProvider.GetService(typeof(IXamlTypeResolver)) as IXamlTypeResolver;
                if (xamlTypeResolver is null)
                {
                    throw new ArgumentException(SR.Format(SR.MarkupExtensionNoContext, GetType().Name, nameof(IXamlTypeResolver)));
                }
 
                // Use the type resolver to get a Type instance.
                type = xamlTypeResolver.Resolve(typeString);
 
                // Get the member name substring.
                fieldString = _member.Substring(dotIndex + 1, _member.Length - dotIndex - 1);
                if (string.IsNullOrEmpty(typeString))
                {
                    throw new ArgumentException(SR.Format(SR.MarkupExtensionBadStatic, _member));
                }
            }
 
            // Use the built-in parser for enum types.
            if (type.IsEnum)
            {
                return Enum.Parse(type, fieldString);
            }
 
            // For other types, reflect.
            if (GetFieldOrPropertyValue(type, fieldString, out object value))
            {
                return value;
            }
 
            throw new ArgumentException(SR.Format(SR.MarkupExtensionBadStatic, typeNameForError is not null ? $"{typeNameForError}.{_member}" : _member));
        }
 
        /// <summary>
        /// Return false if a public static field or property with the same
        /// name cannot be found.
        /// <summary>
        private bool GetFieldOrPropertyValue(Type type, string name, out object value)
        {
            Type currentType = type;
            do
            {
                FieldInfo field = currentType.GetField(name, BindingFlags.Public | BindingFlags.Static);
                if (field is not null)
                {
                    value = field.GetValue(null);
                    return true;
                }
 
                currentType = currentType.BaseType;
            } while(currentType is not null);
 
            currentType = type;
            do
            {
                PropertyInfo prop = currentType.GetProperty(name, BindingFlags.Public | BindingFlags.Static);
                if (prop is not null)
                {
                    value = prop.GetValue(null,null);
                    return true;
                }
 
                currentType = currentType.BaseType;
            } while(currentType is not null);
 
            value = null;
            return false;
        }
 
        /// <summary>
        /// The static field or property represented by a string. This string is
        /// of the format Prefix:ClassName.FieldOrPropertyName. The Prefix is
        /// optional, and refers to the XML prefix in a Xaml file.
        /// </summary>
        [ConstructorArgument("member")]
        public string Member
        {
            get => _member;
            set => _member = value ?? throw new ArgumentNullException(nameof(value));
        }
 
        [DefaultValue(null)]
        public Type MemberType
        {
            get => _memberType;
            set => _memberType = value ?? throw new ArgumentNullException(nameof(value));
        }
    }
}