File: System\Windows\Markup\Baml2006\WpfKnownType.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\PresentationFramework\PresentationFramework.csproj (PresentationFramework)
// 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.ComponentModel;
using System.Reflection;
using System.Windows.Markup;
using System.Xaml;
using System.Xaml.Schema;
 
namespace System.Windows.Baml2006
{
    // XamlType for WPF Known BAML types.
    //
    class WpfKnownType : WpfXamlType, ICustomAttributeProvider
    {
        // We use the same bitField as in WpfXamlType.  If we change WpfXamlType, we need to update the enum here
        [Flags]
        private enum BoolTypeBits
        {
            Frozen                              = 0x0004,
            WhitespaceSignificantCollection     = 0x0008,
            UsableDurintInit                    = 0x0010,
            HasSpecialValueConverter            = 0x0020,
        }
 
        private static Attribute[] s_EmptyAttributes;
        short _bamlNumber;
        string _name;
        Type _underlyingType;       
        string _contentPropertyName;
        string _runtimeNamePropertyName;
        string _dictionaryKeyPropertyName;
        string _xmlLangPropertyName;
        string _uidPropertyName;
        Func<object> _defaultConstructor;
        Type _deferringLoader;
        Type _typeConverterType;
        XamlCollectionKind _collectionKind;
 
        bool Frozen
        {
            get { return WpfXamlType.GetFlag(ref _bitField, (byte)BoolTypeBits.Frozen); }
            set { WpfXamlType.SetFlag(ref _bitField, (byte)BoolTypeBits.Frozen, value); }
        }
 
        public bool WhitespaceSignificantCollection
        {
            get { return WpfXamlType.GetFlag(ref _bitField, (byte)BoolTypeBits.WhitespaceSignificantCollection); }
            set { CheckFrozen(); WpfXamlType.SetFlag(ref _bitField, (byte)BoolTypeBits.WhitespaceSignificantCollection, value); }
        }
 
        public bool IsUsableDuringInit
        {
            get { return WpfXamlType.GetFlag(ref _bitField, (byte)BoolTypeBits.UsableDurintInit); }
            set { CheckFrozen(); WpfXamlType.SetFlag(ref _bitField, (byte)BoolTypeBits.UsableDurintInit, value); }
        }
        public bool HasSpecialValueConverter
        {
            get { return WpfXamlType.GetFlag(ref _bitField, (byte)BoolTypeBits.HasSpecialValueConverter); }
            set { CheckFrozen(); WpfXamlType.SetFlag(ref _bitField, (byte)BoolTypeBits.HasSpecialValueConverter, value); }
        }
 
        public WpfKnownType(XamlSchemaContext schema,
            int bamlNumber,
            string name,
            Type underlyingType)
            : this(schema, bamlNumber, name, underlyingType, true, true)
        {
        }
 
        public WpfKnownType(XamlSchemaContext schema,
            int bamlNumber,
            string name,
            Type underlyingType, 
            bool isBamlType,
            bool useV3Rules)
            : base(underlyingType, schema, isBamlType, useV3Rules)
        {
            _bamlNumber = (short)bamlNumber;
            _name = name;
            _underlyingType = underlyingType;
        }
 
        public void Freeze()
        {
            Frozen = true;
        }
 
        private void CheckFrozen()
        {
            if (Frozen)
            {
                throw new InvalidOperationException("Can't Assign to Known Type attributes");
            }
        }
 
        public short BamlNumber
        {
            get { return _bamlNumber; }
        }
 
        protected override XamlMember LookupContentProperty()
        {
            return CallGetMember(_contentPropertyName);
        }
 
        public string ContentPropertyName
        {
            get { return _contentPropertyName; }
            set
            {
                CheckFrozen();
                _contentPropertyName = value;
            }
        }
 
        protected override XamlMember LookupAliasedProperty(XamlDirective directive)
        {
            if (directive == XamlLanguage.Name)
            {
                return CallGetMember(_runtimeNamePropertyName);
            }
            else if (directive == XamlLanguage.Key && _dictionaryKeyPropertyName != null)
            {
                return LookupMember(_dictionaryKeyPropertyName, true);
            }
            else if (directive == XamlLanguage.Lang)
            {
                return CallGetMember(_xmlLangPropertyName);
            }
            else if (directive == XamlLanguage.Uid)
            {
                return CallGetMember(_uidPropertyName);
            }
            else
            {
                return null;
            }
        }
 
        public string RuntimeNamePropertyName
        {
            get { return _runtimeNamePropertyName; }
            set
            {
                CheckFrozen();
                _runtimeNamePropertyName = value;
            }
        }
 
        public string XmlLangPropertyName
        {
            get { return _xmlLangPropertyName; }
            set
            {
                CheckFrozen();
                _xmlLangPropertyName = value;
            }
        }
 
        public string UidPropertyName
        {
            get { return _uidPropertyName; }
            set
            {
                CheckFrozen();
                _uidPropertyName = value;
            }
        }
 
        public string DictionaryKeyPropertyName
        {
            get { return _dictionaryKeyPropertyName; }
            set
            {
                CheckFrozen();
                _dictionaryKeyPropertyName = value;
            }
        }
 
        protected override XamlCollectionKind LookupCollectionKind()
        {
            return _collectionKind;
        }
 
        public XamlCollectionKind CollectionKind
        {
            get { return _collectionKind; }
            set
            {
                CheckFrozen();
                _collectionKind = value;
            }
        }
 
        protected override bool LookupIsWhitespaceSignificantCollection()
        {
            return WhitespaceSignificantCollection;
        }
 
        public Func<object> DefaultConstructor
        {
            get { return _defaultConstructor; }
            set
            {
                CheckFrozen();
                _defaultConstructor = value;
            }
        }
 
        protected override XamlValueConverter<TypeConverter> LookupTypeConverter()
        {
            WpfSharedBamlSchemaContext schema = System.Windows.Markup.XamlReader.BamlSharedSchemaContext;
 
            if (_typeConverterType != null)
            {
                return schema.GetTypeConverter(_typeConverterType);
            }
            
            if (HasSpecialValueConverter)
            {
                // This is for "object/string" syntax.
                // "Should" not cause reflection.
                return base.LookupTypeConverter();
            }
 
            return null;
        }
 
        public Type TypeConverterType
        {
            get { return _typeConverterType; }
            set
            {
                CheckFrozen();
                _typeConverterType = value;
            }
        }
 
        public Type DeferringLoaderType
        {
            get { return _deferringLoader; }
            set
            {
                CheckFrozen();
                _deferringLoader = value;
            }
        }
 
        protected override XamlValueConverter<XamlDeferringLoader> LookupDeferringLoader()
        {
            if (_deferringLoader != null)
            {
                WpfSharedBamlSchemaContext schema = System.Windows.Markup.XamlReader.BamlSharedSchemaContext;
                return schema.GetDeferringLoader(_deferringLoader);
            }
            return null;
        }
 
        protected override EventHandler<System.Windows.Markup.XamlSetMarkupExtensionEventArgs> LookupSetMarkupExtensionHandler()
        {
            if (typeof(Setter).IsAssignableFrom(_underlyingType))
            {
                return Setter.ReceiveMarkupExtension;
            }
            else if (typeof(DataTrigger).IsAssignableFrom(_underlyingType))
            {
                return DataTrigger.ReceiveMarkupExtension;
            }
            else if (typeof(Condition).IsAssignableFrom(_underlyingType))
            {
                return Condition.ReceiveMarkupExtension;
            }
            return null;
        }
 
        protected override EventHandler<System.Windows.Markup.XamlSetTypeConverterEventArgs> LookupSetTypeConverterHandler()
        {
            if (typeof(Setter).IsAssignableFrom(_underlyingType))
            {
                return Setter.ReceiveTypeConverter;
            }
            else if (typeof(Trigger).IsAssignableFrom(_underlyingType))
            {
                return Trigger.ReceiveTypeConverter;
            }
            else if (typeof(Condition).IsAssignableFrom(_underlyingType))
            {
                return Condition.ReceiveTypeConverter;
            }
            return null;
        }
 
        protected override bool LookupUsableDuringInitialization()
        {
            return IsUsableDuringInit;
        }
 
        protected override System.Xaml.Schema.XamlTypeInvoker LookupInvoker()
        {
            return new WpfKnownTypeInvoker(this);
        }
 
        private XamlMember CallGetMember(string name)
        {
            if (name != null)
            {
                return GetMember(name);
            }
            return null;
        }
 
        private Dictionary<int, Baml6ConstructorInfo> _constructors;
 
        public Dictionary<int, Baml6ConstructorInfo> Constructors
        {
            get
            {
                if (_constructors == null)
                {
                    _constructors = new Dictionary<int, Baml6ConstructorInfo>();
                }
                return _constructors;
            }
        }
 
        protected override IList<XamlType> LookupPositionalParameters(int paramCount)
        {
            if (this.IsMarkupExtension)
            {
                List<XamlType> xTypes = null;
                Baml6ConstructorInfo info = Constructors[paramCount];
                if (Constructors.TryGetValue(paramCount, out info))
                {
                    xTypes = new List<XamlType>();
                    foreach (Type type in info.Types)
                    {
                        xTypes.Add(SchemaContext.GetXamlType(type));
                    }
                }
 
                return xTypes;
            }
            else
            {
                return base.LookupPositionalParameters(paramCount);
            }
        }
 
        protected override ICustomAttributeProvider LookupCustomAttributeProvider()
        {
            return this;
        }
 
        #region ICustomAttributeProvider Members
 
        object[] ICustomAttributeProvider.GetCustomAttributes(bool inherit)
        {
            return UnderlyingType.GetCustomAttributes(inherit);
        }
 
        object[] ICustomAttributeProvider.GetCustomAttributes(Type attributeType, bool inherit)
        {
            Attribute result;
            if (TryGetCustomAttribute(attributeType, out result))
            {
                if (result != null)
                {
                    return new Attribute[] { result };
                }
                if (s_EmptyAttributes == null)
                {
                    s_EmptyAttributes = Array.Empty<Attribute>();
                }
                return s_EmptyAttributes;
            }
            return UnderlyingType.GetCustomAttributes(attributeType, inherit);
        }
 
        // Perf - Typically attributes shouldn't be looked up on known types, because we override 
        // the Lookup* methods. However, for content property, aliased properties, and XamlSet handlers, 
        // XamlType will look up inherited attributes for types that derive from the known types. 
        // So for just these attributes, we short-circuit reflection and return the known values.
        private bool TryGetCustomAttribute(Type attributeType, out Attribute result)
        {
            bool known = true;
            if (attributeType == typeof(ContentPropertyAttribute))
            {
                result = (_contentPropertyName == null) ? null : new ContentPropertyAttribute(_contentPropertyName);
            }
            else if (attributeType == typeof(RuntimeNamePropertyAttribute))
            {
                result = (_runtimeNamePropertyName == null) ? null : new RuntimeNamePropertyAttribute(_runtimeNamePropertyName);
            }
            else if (attributeType == typeof(DictionaryKeyPropertyAttribute))
            {
                result = (_dictionaryKeyPropertyName == null) ? null : new DictionaryKeyPropertyAttribute(_dictionaryKeyPropertyName);
            }
            else if (attributeType == typeof(XmlLangPropertyAttribute))
            {
                result = (_xmlLangPropertyName == null) ? null : new XmlLangPropertyAttribute(_xmlLangPropertyName);
            }
            else if (attributeType == typeof(UidPropertyAttribute))
            {
                result = (_uidPropertyName == null) ? null : new UidPropertyAttribute(_uidPropertyName);
            }
            else
            {
                result = null;
                known = false;
            }
            return known;
        }
 
        bool ICustomAttributeProvider.IsDefined(Type attributeType, bool inherit)
        {
            return UnderlyingType.IsDefined(attributeType, inherit);
        }
 
        #endregion
    }
}