File: System\Windows\Markup\XamlStyleSerializer.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.
 
//
//
// Description:
//   Class that serializes and deserializes Styles.
//
 
using System;
using System.ComponentModel;
 
using System.ComponentModel.Design.Serialization;
using System.Diagnostics;
using System.Collections;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Text;
using System.Xml;
using MS.Utility;
 
#if !PBTCOMPILER
using System.Windows.Data;
#endif
 
#if PBTCOMPILER
namespace MS.Internal.Markup
#else
namespace System.Windows.Markup
#endif
{
    /// <summary>
    ///     Class that knows how to serialize and deserialize Style objects
    /// </summary>
    internal class XamlStyleSerializer : XamlSerializer
    {
#if PBTCOMPILER 

        #region Construction

        /// <summary>
        ///     Constructor for XamlStyleSerializer
        /// </summary>
        public XamlStyleSerializer() : base()
        {
        }
 
        internal XamlStyleSerializer(ParserHooks parserHooks) : base()
        {
            _parserHooks = parserHooks;
        }
 
        private ParserHooks   _parserHooks = null;
 
        #endregion Construction

        /// <summary>
        ///   Convert from Xaml read by a token reader into baml being written
        ///   out by a record writer.  The context gives mapping information.
        /// </summary>
        internal override void ConvertXamlToBaml (
            XamlReaderHelper             tokenReader,
            ParserContext          context,
            XamlNode               xamlNode,
            BamlRecordWriter       bamlWriter)
        {
            StyleXamlParser styleParser = new StyleXamlParser(tokenReader, context);
            styleParser.BamlRecordWriter = bamlWriter;
            styleParser.ParserHooks = _parserHooks;
 
 
            // Process the xamlNode that is passed in so that the <Style> element is written to baml
            styleParser.WriteElementStart((XamlElementStartNode)xamlNode);
 
            // Parse the entire Style section now, writing everything out directly to BAML.
            styleParser.Parse();
       }
#else
 
 
        /// <summary>
        ///   If the Style represented by a group of baml records is stored in a dictionary, this
        ///   method will extract the key used for this dictionary from the passed
        ///   collection of baml records.  For Style, this is the type of the first element record
        ///   in the record collection, skipping over the Style element itself.
        /// </summary>
        internal override object GetDictionaryKey(BamlRecord startRecord,  ParserContext parserContext)
        {
            Type       styleTargetType = Style.DefaultTargetType;
            bool       styleTargetTypeSet = false;
            object     targetType = null;
            int        numberOfElements = 0;
            BamlRecord record = startRecord;
            short      ownerTypeId = 0;
 
            while (record != null)
            {
                if (record.RecordType == BamlRecordType.ElementStart)
                {
                    BamlElementStartRecord elementStart = record as BamlElementStartRecord;
                    if (++numberOfElements == 1)
                    {
                        // save the type ID of the first element (i.e. <Style>)
                        ownerTypeId = elementStart.TypeId;
                    }
                    else if (numberOfElements == 2)
                    {
                        styleTargetType = parserContext.MapTable.GetTypeFromId(elementStart.TypeId);
                        styleTargetTypeSet = true;
                        break;
                    }
                }
                else if (record.RecordType == BamlRecordType.Property && numberOfElements == 1)
                {
                    // look for the TargetType property on the <Style> element
                    BamlPropertyRecord propertyRecord = record as BamlPropertyRecord;
                    if (parserContext.MapTable.DoesAttributeMatch(propertyRecord.AttributeId, ownerTypeId, TargetTypePropertyName))
                    {
                        targetType = parserContext.XamlTypeMapper.GetDictionaryKey(propertyRecord.Value, parserContext);
                    }
                }
                else if (record.RecordType == BamlRecordType.PropertyComplexStart ||
                         record.RecordType == BamlRecordType.PropertyIListStart)
                {
                    // We didn't find the second element before a complex property like
                    // Style.Triggers, so return the default style target type:  FrameworkElement.
                    break;
                }
                record = record.Next;
            }
 
            if (targetType == null)
            {
                if (!styleTargetTypeSet)
                {
                    ThrowException(nameof(SR.StyleNoDictionaryKey),
                                   parserContext.LineNumber,
                                   parserContext.LinePosition);
                }
                return styleTargetType;
            }
            else
                return targetType;
        }
 
                // Helper to insert line and position numbers into message, if they are present
        void ThrowException(
             string id,
             int  lineNumber,
             int  linePosition)
        {
            string message = SR.GetResourceString(id);
            XamlParseException parseException;
 
            // Throw the appropriate execption.  If we have line numbers, then we are
            // parsing a xaml file, so throw a xaml exception.  Otherwise were are
            // parsing a baml file.
            if (lineNumber > 0)
            {
                message += " ";
                message += SR.Format(SR.ParserLineAndOffset,
                                  lineNumber.ToString(CultureInfo.CurrentCulture),
                                  linePosition.ToString(CultureInfo.CurrentCulture));
                parseException = new XamlParseException(message, lineNumber, linePosition);
            }
            else
            {
                parseException = new XamlParseException(message);
            }
 
            throw parseException;
        }
 
 
#endif // !PBTCOMPILER
 
 
        #region Data
 
        // Constants used for emitting specific properties and attributes for a Style
        internal const string StyleTagName                                  = "Style";
        internal const string TargetTypePropertyName                        = "TargetType";
        internal const string BasedOnPropertyName                           = "BasedOn";
        internal const string VisualTriggersPropertyName                    = "Triggers";
        internal const string ResourcesPropertyName                         = "Resources";
        internal const string SettersPropertyName                           = "Setters";
        internal const string VisualTriggersFullPropertyName    = $"{StyleTagName}.{VisualTriggersPropertyName}";
        internal const string SettersFullPropertyName           = $"{StyleTagName}.{SettersPropertyName}";
        internal const string ResourcesFullPropertyName         = $"{StyleTagName}.{ResourcesPropertyName}";
        internal const string PropertyTriggerPropertyName                   = "Property";
        internal const string PropertyTriggerValuePropertyName              = "Value";
        internal const string PropertyTriggerSourceName                     = "SourceName";
        internal const string PropertyTriggerEnterActions                   = "EnterActions";
        internal const string PropertyTriggerExitActions                    = "ExitActions";
        internal const string DataTriggerBindingPropertyName                = "Binding";
        internal const string EventTriggerEventName                         = "RoutedEvent";
        internal const string EventTriggerSourceName                          = "SourceName";
        internal const string EventTriggerActions                           = "Actions";
        internal const string MultiPropertyTriggerConditionsPropertyName    = "Conditions";
        internal const string SetterTagName                                 = "Setter";
        internal const string SetterPropertyAttributeName                   = "Property";
        internal const string SetterValueAttributeName                      = "Value";
        internal const string SetterTargetAttributeName                     = "TargetName";
        internal const string SetterEventAttributeName                      = "Event";
        internal const string SetterHandlerAttributeName                    = "Handler";
#if HANDLEDEVENTSTOO
        internal const string SetterHandledEventsTooAttributeName           = "HandledEventsToo";
#endif
        #endregion Data
 
    }
}