File: System\Windows\Style.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.
 
/***************************************************************************\
*
*
*  Style and templating.
*
*
\***************************************************************************/
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Windows.Threading;
using System.Windows.Markup;
using MS.Utility;
 
#pragma warning disable 1634, 1691  // suppressing PreSharp warnings
 
namespace System.Windows
{
    /// <summary>
    ///     Styling and Templating
    /// </summary>
    [Localizability(LocalizationCategory.Ignore)]
    [DictionaryKeyProperty("TargetType")]
    [ContentProperty("Setters")]
    public class Style : DispatcherObject, INameScope, IAddChild, ISealable, IHaveResources, IQueryAmbient
    {
        static Style()
        {
            // Register for the "alternative Expression storage" feature, since
            // we store Expressions in per-instance StyleData.
            StyleHelper.RegisterAlternateExpressionStorage();
        }
 
        /// <summary>
        ///     Style construction
        /// </summary>
        public Style()
        {
            GetUniqueGlobalIndex();
        }
 
        /// <summary>
        ///     Style construction
        /// </summary>
        /// <param name="targetType">Type in which Style will be applied</param>
        public Style(Type targetType)
        {
            TargetType = targetType;
 
            GetUniqueGlobalIndex();
        }
 
        /// <summary>
        ///     Style construction
        /// </summary>
        /// <param name="targetType">Type in which Style will be applied</param>
        /// <param name="basedOn">Style to base this Style on</param>
        public Style(Type targetType, Style basedOn)
        {
            TargetType = targetType;
            BasedOn = basedOn;
 
            GetUniqueGlobalIndex();
        }
 
        #region INameScope
        /// <summary>
        /// Registers the name - Context combination
        /// </summary>
        /// <param name="name">Name to register</param>
        /// <param name="scopedElement">Element where name is defined</param>
        public void RegisterName(string name, object scopedElement)
        {
            // Verify Context Access
            VerifyAccess();
 
            _nameScope.RegisterName(name, scopedElement);
        }
 
        /// <summary>
        /// Unregisters the name - element combination
        /// </summary>
        /// <param name="name">Name of the element</param>
        public void UnregisterName(string name)
        {
            // Verify Context Access
            VerifyAccess();
 
            _nameScope.UnregisterName(name);
        }
 
        /// <summary>
        /// Find the element given name
        /// </summary>
        /// <param name="name">Name of the element</param>
        object INameScope.FindName(string name)
        {
            // Verify Context Access
            VerifyAccess();
 
            return _nameScope.FindName(name);
        }
 
        private NameScope _nameScope = new NameScope();
        #endregion IIdScope
 
        /// <summary>
        /// Each Style gets its own unique index used for Style.GetHashCode
        /// </summary>
        private void GetUniqueGlobalIndex()
        {
            lock (Synchronized)
            {
                // Setup unqiue global index
                StyleInstanceCount++;
                GlobalIndex = StyleInstanceCount;
            }
        }
 
        /// <summary>
        ///     Style mutability state
        /// </summary>
        /// <remarks>
        ///     A style is sealed when another style is basing on it, or,
        ///     when it's applied
        /// </remarks>
        public bool IsSealed
        {
            get
            {
                // Verify Context Access
                VerifyAccess();
 
                return _sealed;
            }
        }
 
 
        /// <summary>
        ///     Type that this style is intended
        /// </summary>
        /// <remarks>
        ///     By default, the target type is FrameworkElement
        /// </remarks>
        [Ambient]
        [Localizability(LocalizationCategory.NeverLocalize)]
        public Type TargetType
        {
            get
            {
                // Verify Context Access
                VerifyAccess();
 
                return _targetType;
            }
 
            set
            {
                // Verify Context Access
                VerifyAccess();
 
                if (_sealed)
                {
                    throw new InvalidOperationException(SR.Format(SR.CannotChangeAfterSealed, "Style"));
                }
 
                ArgumentNullException.ThrowIfNull(value);
 
                if (!typeof(FrameworkElement).IsAssignableFrom(value) &&
                    !typeof(FrameworkContentElement).IsAssignableFrom(value) &&
                    !(DefaultTargetType == value))
                {
                    #pragma warning suppress 6506 // value is obviously not null
                    throw new ArgumentException(SR.Format(SR.MustBeFrameworkDerived, value.Name));
                }
 
                _targetType = value;
 
                SetModified(TargetTypeID);
            }
        }
 
        /// <summary>
        ///     Style to base on
        /// </summary>
        [DefaultValue(null)]
        [Ambient]
        public Style BasedOn
        {
            get
            {
                // Verify Context Access
                VerifyAccess();
 
                return _basedOn;
            }
            set
            {
                // Verify Context Access
                VerifyAccess();
 
                if (_sealed)
                {
                    throw new InvalidOperationException(SR.Format(SR.CannotChangeAfterSealed, "Style"));
                }
 
                if( value == this )
                {
                    // Basing on self is not allowed.  This is a degenerate case
                    //  of circular reference chain, the full check for circular
                    //  reference is done in Seal().
                    throw new ArgumentException(SR.StyleCannotBeBasedOnSelf);
                }
 
                _basedOn = value;
 
                SetModified(BasedOnID);
            }
        }
 
 
        /// <summary>
        ///     Visual triggers
        /// </summary>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public TriggerCollection Triggers
        {
            get
            {
                // Verify Context Access
                VerifyAccess();
 
                if (_visualTriggers == null)
                {
                    _visualTriggers = new TriggerCollection();
 
                    // If the style has been sealed prior to this the newly
                    // created TriggerCollection also needs to be sealed
                    if (_sealed)
                    {
                        _visualTriggers.Seal();
                    }
                }
                return _visualTriggers;
            }
        }
 
        /// <summary>
        ///     The collection of property setters for the target type
        /// </summary>
 
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public SetterBaseCollection Setters
        {
            get
            {
                // Verify Context Access
                VerifyAccess();
 
                if( _setters == null )
                {
                    _setters = new SetterBaseCollection();
 
                    // If the style has been sealed prior to this the newly
                    // created SetterBaseCollection also needs to be sealed
                    if (_sealed)
                    {
                        _setters.Seal();
                    }
                }
                return _setters;
            }
        }
 
        /// <summary>
        ///     The collection of resources that can be
        ///     consumed by the container and its sub-tree.
        /// </summary>
        [Ambient]
        public ResourceDictionary Resources
        {
            get
            {
                // Verify Context Access
                VerifyAccess();
 
                if( _resources == null )
                {
                    _resources = new ResourceDictionary();
 
                    // A Style ResourceDictionary can be accessed across threads
                    _resources.CanBeAccessedAcrossThreads = true;
 
                    // If the style has been sealed prior to this the newly
                    // created ResourceDictionary also needs to be sealed
                    if (_sealed)
                    {
                        _resources.IsReadOnly = true;
                    }
                }
                return _resources;
            }
            set
            {
                // Verify Context Access
                VerifyAccess();
 
                if( _sealed )
                {
                    throw new InvalidOperationException(SR.Format(SR.CannotChangeAfterSealed, "Style"));
                }
 
                _resources = value;
 
                if (_resources != null)
                {
                    // A Style ResourceDictionary can be accessed across threads
                    _resources.CanBeAccessedAcrossThreads = true;
                }
            }
        }
 
        ResourceDictionary IHaveResources.Resources
        {
            get { return Resources; }
            set { Resources = value; }
        }
 
        /// <summary>
        ///     Tries to find a Resource for the given resourceKey in the current
        ///     style's ResourceDictionary or the basedOn style's ResourceDictionary
        ///     in that order.
        /// </summary>
        internal object FindResource(object resourceKey, bool allowDeferredResourceReference, bool mustReturnDeferredResourceReference)
        {
            if ((_resources != null) && _resources.Contains(resourceKey))
            {
                bool canCache;
                return _resources.FetchResource(resourceKey, allowDeferredResourceReference, mustReturnDeferredResourceReference, out canCache);
            }
            if (_basedOn != null)
            {
                return _basedOn.FindResource(resourceKey, allowDeferredResourceReference, mustReturnDeferredResourceReference);
            }
            return DependencyProperty.UnsetValue;
        }
 
        internal ResourceDictionary FindResourceDictionary(object resourceKey)
        {
            Debug.Assert(resourceKey != null, "Argument cannot be null");
            if (_resources != null && _resources.Contains(resourceKey))
            {
                return _resources;
            }
            if (_basedOn != null)
            {
                return _basedOn.FindResourceDictionary(resourceKey);
            }
            return null;
        }
 
        bool IQueryAmbient.IsAmbientPropertyAvailable(string propertyName)
        {
            // We want to make sure that StaticResource resolution checks the .Resources
            // Ie.  The Ambient search should look at Resources if it is set.
            // Even if it wasn't set from XAML (eg. the Ctor (or derived Ctor) added stuff)
            switch (propertyName)
            {
                case "Resources":
                    if (_resources == null)
                    {
                        return false;
                    }
                    break;
                case "BasedOn":
                    if (_basedOn == null)
                    {
                        return false;
                    }
                    break;
            }
            return true;
        }
 
        ///<summary>
        /// This method is called to Add a Setter object as a child of the Style.
        /// This method is used primarily by the parser to set style properties and events.
        ///</summary>
        ///<param name="value">
        /// The object to add as a child; it must be a SetterBase subclass.
        ///</param>
        void IAddChild.AddChild (Object value)
        {
            // Verify Context Access
            VerifyAccess();
 
            ArgumentNullException.ThrowIfNull(value);
 
            SetterBase sb = value as SetterBase;
 
            if (sb == null)
            {
                throw new ArgumentException(SR.Format(SR.UnexpectedParameterType, value.GetType(), typeof(SetterBase)), "value");
            }
 
            Setters.Add(sb);
        }
 
        ///<summary>
        /// This method is called by the parser when text appears under the tag in markup.
        /// As default Styles do not support text, calling this method has no effect.
        ///</summary>
        ///<param name="text">
        /// Text to add as a child.
        ///</param>
        void IAddChild.AddText (string text)
        {
            // Verify Context Access
            VerifyAccess();
 
            XamlSerializerUtil.ThrowIfNonWhiteSpaceInAddText(text, this);
        }
 
        /// <summary>
        ///     Given a set of values for the PropertyValue struct, put that in
        /// to the PropertyValueList, overwriting any existing entry.
        /// </summary>
        private void UpdatePropertyValueList(
            DependencyProperty dp,
            PropertyValueType valueType,
            object value)
        {
            // Check for existing value on dp
            int existingIndex = -1;
            for( int i = 0; i < PropertyValues.Count; i++ )
            {
                if( PropertyValues[i].Property == dp )
                {
                    existingIndex = i;
                    break;
                }
            }
 
            if( existingIndex >= 0 )
            {
                // Overwrite existing value for dp
                PropertyValue propertyValue = PropertyValues[existingIndex];
                propertyValue.ValueType = valueType;
                propertyValue.ValueInternal = value;
                // Put back modified struct
                PropertyValues[existingIndex] = propertyValue;
            }
            else
            {
                // Store original data
                PropertyValue propertyValue = new PropertyValue();
                propertyValue.ValueType = valueType;
                propertyValue.ChildName = StyleHelper.SelfName;
                propertyValue.Property = dp;
                propertyValue.ValueInternal = value;
 
                PropertyValues.Add(propertyValue);
            }
        }
 
        internal void CheckTargetType(object element)
        {
            // In the most common case TargetType is Default
            // and we can avoid a call to IsAssignableFrom() who's performance is unknown.
            if(DefaultTargetType == TargetType)
                return;
 
            Type elementType = element.GetType();
            if(!TargetType.IsAssignableFrom(elementType))
            {
                throw new InvalidOperationException(SR.Format(SR.StyleTargetTypeMismatchWithElement,
                                                    this.TargetType.Name,
                                                    elementType.Name));
            }
        }
 
        /// <summary>
        /// This Style and all factories/triggers are now immutable
        /// </summary>
        public void Seal()
        {
            // Verify Context Access
            VerifyAccess();
 
            // 99% case - Style is already sealed.
            if (_sealed)
            {
                return;
            }
 
            // Most parameter checking is done as "upstream" as possible, but some
            //  can't be checked until Style is sealed.
            if (_targetType == null)
            {
                throw new InvalidOperationException(SR.Format(SR.NullPropertyIllegal, "TargetType"));
            }
 
            if (_basedOn != null)
            {
                if(DefaultTargetType != _basedOn.TargetType &&
                    !_basedOn.TargetType.IsAssignableFrom(_targetType))
                {
                    throw new InvalidOperationException(SR.Format(SR.MustBaseOnStyleOfABaseType, _targetType.Name));
                }
            }
 
            // Seal setters
            if (_setters != null)
            {
                _setters.Seal();
            }
 
            // Seal triggers
            if (_visualTriggers != null)
            {
                _visualTriggers.Seal();
            }
 
            // Will throw InvalidOperationException if we find a loop of
            //  BasedOn references.  (A.BasedOn = B, B.BasedOn = C, C.BasedOn = A)
            CheckForCircularBasedOnReferences();
 
            // Seal BasedOn Style chain
            if (_basedOn != null)
            {
                _basedOn.Seal();
            }
 
            // Seal the ResourceDictionary
            if (_resources != null)
            {
                _resources.IsReadOnly = true;
            }
 
            //
            // Build shared tables
            //
 
            // Process all Setters set on the selfStyle. This stores all the property
            // setters on the current styles into PropertyValues list, so it can be used
            // by ProcessSelfStyle in the next step. The EventSetters for the current
            // and all the basedOn styles are merged into the EventHandlersStore on the
            // current style.
            ProcessSetters(this);
 
            // Add an entry in the EventDependents list for
            // the TargetType's EventHandlersStore. Notice
            // that the childIndex is 0.
            StyleHelper.AddEventDependent(0, this.EventHandlersStore, ref EventDependents);
 
            // Process all PropertyValues (all are "Self") in the Style
            // chain (base added first)
            ProcessSelfStyles(this);
 
            // Process all TriggerBase PropertyValues ("Self" triggers
            // and child triggers) in the Style chain last (highest priority)
            ProcessVisualTriggers(this);
 
            // Sort the ResourceDependents, to help avoid duplicate invalidations
            StyleHelper.SortResourceDependents(ref ResourceDependents);
 
            // All done, seal self and call it a day.
            _sealed = true;
 
            // Remove thread affinity so it can be accessed across threads
            DetachFromDispatcher();
        }
 
        /// <summary>
        ///     This method checks to see if the BasedOn hierarchy contains
        /// a loop in the chain of references.
        /// </summary>
        /// <remarks>
        /// Classic "when did we enter the cycle" problem where we don't know
        ///  what to start remembering and what to check against.  Brute-
        ///  force approach here is to remember everything with a stack
        ///  and do a linear comparison through everything.  Since the Style
        ///  BasedOn hierarchy is not expected to be large, this should be OK.
        /// </remarks>
        private void CheckForCircularBasedOnReferences()
        {
            Stack basedOnHierarchy = new Stack(10);  // 10 because that's the default value (see MSDN) and the perf team wants us to specify something.
            Style latestBasedOn = this;
 
            while( latestBasedOn != null )
            {
                if( basedOnHierarchy.Contains( latestBasedOn ) )
                {
                    // Uh-oh.  We've seen this Style before.  This means
                    //  the BasedOn hierarchy contains a loop.
                    throw new InvalidOperationException(SR.Format(
                        SR.StyleBasedOnHasLoop));
 
                    // Debugging note: If we stop here, the basedOnHierarchy
                    //  object is still alive and we can browse through it to
                    //  see what we've explored.  (This does not apply if
                    //  somebody catches this exception and re-throws.)
                }
 
                // Haven't seen it, push on stack and go to next level.
                basedOnHierarchy.Push( latestBasedOn );
                latestBasedOn = latestBasedOn.BasedOn;
            }
 
            return;
        }
 
        // Iterates through the setters collection and adds the EventSetter information into
        // an EventHandlersStore for easy and fast retrieval during event routing. Also adds
        // an entry in the EventDependents list for EventhandlersStore holding the TargetType's
        // events.
        private void ProcessSetters(Style style)
        {
            // Walk down to bottom of based-on chain
            if (style == null)
            {
                return;
            }
 
            style.Setters.Seal(); // Does not mark individual setters as sealed, that's up to the loop below.
 
 
            // On-demand create the PropertyValues list, so that we can specify the right size.
 
            if(PropertyValues.Count == 0)
            {
                PropertyValues = new FrugalStructList<System.Windows.PropertyValue>(style.Setters.Count);
            }
 
            // Add EventSetters to local EventHandlersStore
            for (int i = 0; i < style.Setters.Count; i++)
            {
                SetterBase setterBase = style.Setters[i];
                Debug.Assert(setterBase != null, "Setter collection must contain non-null instances of SetterBase");
 
                // Setters are folded into the PropertyValues table only for the current style. The
                // processing of BasedOn Style properties will occur in subsequent call to ProcessSelfStyle
                Setter setter = setterBase as Setter;
                if (setter != null)
                {
                    // Style Setters are not allowed to have a child target name - since there are no child nodes in a Style.
                    if( setter.TargetName != null )
                    {
                        throw new InvalidOperationException(SR.Format(SR.SetterOnStyleNotAllowedToHaveTarget, setter.TargetName));
                    }
 
                    if (style == this)
                    {
                        DynamicResourceExtension dynamicResource = setter.ValueInternal as DynamicResourceExtension;
                        if (dynamicResource == null)
                        {
                            UpdatePropertyValueList( setter.Property, PropertyValueType.Set, setter.ValueInternal );
                        }
                        else
                        {
                            UpdatePropertyValueList( setter.Property, PropertyValueType.Resource, dynamicResource.ResourceKey );
                        }
                    }
                }
                else
                {
 
                    Debug.Assert(setterBase is EventSetter,
                                 "Unsupported SetterBase subclass in style triggers ({0})", setterBase.GetType().ToString());
 
                    // Add this to the _eventHandlersStore
 
                    EventSetter eventSetter = (EventSetter)setterBase;
                    if (_eventHandlersStore == null)
                    {
                        _eventHandlersStore = new EventHandlersStore();
                    }
                    _eventHandlersStore.AddRoutedEventHandler(eventSetter.Event, eventSetter.Handler, eventSetter.HandledEventsToo);
 
                    SetModified(HasEventSetter);
 
                    // If this event setter watches the loaded/unloaded events, set the optimization
                    // flag.
 
                    if (eventSetter.Event == FrameworkElement.LoadedEvent || eventSetter.Event == FrameworkElement.UnloadedEvent)
                    {
                        _hasLoadedChangeHandler = true;
                    }
 
 
                }
            }
 
            // Process EventSetters on based on style so they get merged
            // into the EventHandlersStore for the current style.
            ProcessSetters(style._basedOn);
        }
 
        private void ProcessSelfStyles(Style style)
        {
            // Walk down to bottom of based-on chain
            if (style == null)
            {
                return;
            }
 
            ProcessSelfStyles(style._basedOn);
 
            // Merge in "self" PropertyValues while walking back up the tree
            // "Based-on" style "self" rules are always added first (lower priority)
            for (int i = 0; i < style.PropertyValues.Count; i++)
            {
                PropertyValue propertyValue = style.PropertyValues[i];
 
                StyleHelper.UpdateTables(ref propertyValue, ref ChildRecordFromChildIndex,
                    ref TriggerSourceRecordFromChildIndex, ref ResourceDependents, ref _dataTriggerRecordFromBinding,
                    null /*_childIndexFromChildID*/, ref _hasInstanceValues);
 
                // Track properties on the container that are being driven by
                // the Style so that they can be invalidated during style changes
                StyleHelper.AddContainerDependent(propertyValue.Property, false /*fromVisualTrigger*/, ref ContainerDependents);
            }
        }
 
        private void ProcessVisualTriggers(Style style)
        {
            // Walk down to bottom of based-on chain
            if (style == null)
            {
                return;
            }
 
            ProcessVisualTriggers(style._basedOn);
 
            if (style._visualTriggers != null)
            {
                // Merge in "self" and child TriggerBase PropertyValues while walking
                // back up the tree. "Based-on" style rules are always added first
                // (lower priority)
                int triggerCount = style._visualTriggers.Count;
                for (int i = 0; i < triggerCount; i++)
                {
                    TriggerBase trigger = style._visualTriggers[i];
 
                    // Set things up to handle Setter values
                    for (int j = 0; j < trigger.PropertyValues.Count; j++)
                    {
                        PropertyValue propertyValue = trigger.PropertyValues[j];
 
                        // Check for trigger rules that act on container
                        if (propertyValue.ChildName != StyleHelper.SelfName)
                        {
                            throw new InvalidOperationException(SR.StyleTriggersCannotTargetTheTemplate);
                        }
 
                        TriggerCondition[] conditions = propertyValue.Conditions;
                        for (int k=0; k<conditions.Length; k++)
                        {
                            if( conditions[k].SourceName != StyleHelper.SelfName )
                            {
                                throw new InvalidOperationException(SR.Format(SR.TriggerOnStyleNotAllowedToHaveSource, conditions[k].SourceName));
                            }
                        }
 
                        // Track properties on the container that are being driven by
                        // the Style so that they can be invalidated during style changes
                        StyleHelper.AddContainerDependent(propertyValue.Property, true /*fromVisualTrigger*/, ref this.ContainerDependents);
 
                        StyleHelper.UpdateTables(ref propertyValue, ref ChildRecordFromChildIndex,
                            ref TriggerSourceRecordFromChildIndex, ref ResourceDependents, ref _dataTriggerRecordFromBinding,
                            null /*_childIndexFromChildID*/, ref _hasInstanceValues);
                    }
 
                    // Set things up to handle TriggerActions
                    if( trigger.HasEnterActions || trigger.HasExitActions )
                    {
                        if( trigger is Trigger )
                        {
                            StyleHelper.AddPropertyTriggerWithAction( trigger, ((Trigger)trigger).Property, ref this.PropertyTriggersWithActions );
                        }
                        else if( trigger is MultiTrigger )
                        {
                            MultiTrigger multiTrigger = (MultiTrigger)trigger;
                            for( int k = 0; k < multiTrigger.Conditions.Count; k++ )
                            {
                                Condition triggerCondition = multiTrigger.Conditions[k];
 
                                StyleHelper.AddPropertyTriggerWithAction( trigger, triggerCondition.Property, ref this.PropertyTriggersWithActions );
                            }
                        }
                        else if( trigger is DataTrigger )
                        {
                            StyleHelper.AddDataTriggerWithAction( trigger, ((DataTrigger)trigger).Binding, ref this.DataTriggersWithActions );
                        }
                        else if( trigger is MultiDataTrigger )
                        {
                            MultiDataTrigger multiDataTrigger = (MultiDataTrigger)trigger;
                            for( int k = 0; k < multiDataTrigger.Conditions.Count; k++ )
                            {
                                Condition dataCondition = multiDataTrigger.Conditions[k];
 
                                StyleHelper.AddDataTriggerWithAction( trigger, dataCondition.Binding, ref this.DataTriggersWithActions );
                            }
                        }
                        else
                        {
                            throw new InvalidOperationException(SR.Format(SR.UnsupportedTriggerInStyle, trigger.GetType().Name));
                        }
                    }
 
                    // Set things up to handle EventTrigger
                    EventTrigger eventTrigger = trigger as EventTrigger;
                    if( eventTrigger != null )
                    {
                        if( eventTrigger.SourceName != null && eventTrigger.SourceName.Length > 0 )
                        {
                            throw new InvalidOperationException(SR.Format(SR.EventTriggerOnStyleNotAllowedToHaveTarget, eventTrigger.SourceName));
                        }
 
                        StyleHelper.ProcessEventTrigger(eventTrigger,
                                                        null /*_childIndexFromChildID*/,
                                                        ref _triggerActions,
                                                        ref EventDependents,
                                                        null /*_templateFactoryRoot*/,
                                                        null,
                                                        ref _eventHandlersStore,
                                                        ref _hasLoadedChangeHandler);
                    }
                }
            }
        }
 
        /// <summary>
        ///     Serves as a hash function for a particular type, suitable for use in
        ///     hashing algorithms and data structures like a hash table
        /// </summary>
        /// <returns>The Style's GlobalIndex</returns>
        public override int GetHashCode()
        {
            // Verify Context Access
            VerifyAccess();
 
            return GlobalIndex;
        }
 
        #region ISealable
 
        /// <summary>
        /// Can this style be sealed
        /// </summary>
        bool ISealable.CanSeal
        {
            get { return true; }
        }
 
        /// <summary>
        /// Is this style sealed
        /// </summary>
        bool ISealable.IsSealed
        {
            get { return IsSealed; }
        }
 
        /// <summary>
        /// Seal this style
        /// </summary>
        void ISealable.Seal()
        {
            Seal();
        }
 
        #endregion ISealable
 
        internal bool HasResourceReferences
        {
            get
            {
                return ResourceDependents.Count > 0;
            }
        }
 
        /// <summary>
        ///     Store all the event handlers for this Style TargetType
        /// </summary>
        internal EventHandlersStore EventHandlersStore
        {
            get { return _eventHandlersStore; }
        }
 
        /// <summary>
        ///     Does the current style or any of its template children
        ///     have any event setters OR event triggers
        /// </summary>
        internal bool HasEventDependents
        {
            get
            {
                return (EventDependents.Count > 0);
            }
        }
 
        /// <summary>
        ///     Does the current style or any of its template children
        ///     have event setters, ignoring event triggers.
        /// </summary>
        internal bool HasEventSetters
        {
            get
            {
                return IsModified(HasEventSetter);
            }
        }
 
        //
        //  Says if this style contains any per-instance values
        //
        internal bool HasInstanceValues
        {
            get { return _hasInstanceValues; }
        }
 
        //
        // Says if we have anything listening for the Loaded or Unloaded
        // event (used for an optimization in FrameworkElement).
        //
 
        internal bool HasLoadedChangeHandler
        {
            get { return _hasLoadedChangeHandler; }
            set { _hasLoadedChangeHandler = value; }
        }
 
        // Special equality check that takes into account 'null'
        private static bool IsEqual(object a, object b)
        {
            return (a != null) ? a.Equals(b) : (b == null);
        }
 
        internal bool IsBasedOnModified { get { return IsModified(BasedOnID); } }
 
        private EventHandlersStore _eventHandlersStore = null;
 
        private bool _sealed;
        private bool _hasInstanceValues;
 
        internal static readonly Type DefaultTargetType = typeof(IFrameworkInputElement);
 
        private bool _hasLoadedChangeHandler;
 
        private Type _targetType = DefaultTargetType;
        private Style _basedOn;
 
        private TriggerCollection _visualTriggers = null;
 
        private SetterBaseCollection _setters = null;
 
        // Holds resources that are applicable to the container
        // of this style and its sub-tree.
        internal ResourceDictionary _resources = null;
 
        /* property */ internal int GlobalIndex;
 
        // Style tables
        // Synchronized (write locks, lock-free reads): Covered by Style instance lock
        /* property */ internal FrugalStructList<ChildRecord> ChildRecordFromChildIndex = new FrugalStructList<ChildRecord>(); // Indexed by Child.ChildIndex
        // Synchronized (write locks, lock-free reads): Covered by Style instance lock
 
        //
        // Shared tables used during OnTriggerSourcePropertyInvalidated
        //
        internal FrugalStructList<ItemStructMap<TriggerSourceRecord>> TriggerSourceRecordFromChildIndex = new FrugalStructList<ItemStructMap<TriggerSourceRecord>>();
        // Dictionary of property triggers that have TriggerActions, keyed via DP.GlobalIndex affecting those triggers.
        //  Each trigger can be listed multiple times, if they are dependent on multiple properties.
        internal FrugalMap PropertyTriggersWithActions;
 
        // Original Style data (not including based-on data)
        // Synchronized (write locks, lock-free reads): Covered by Style instance lock
        /* property */ internal FrugalStructList<System.Windows.PropertyValue> PropertyValues = new FrugalStructList<System.Windows.PropertyValue>();
 
        // Properties driven on the container (by the Style) that should be
        // invalidated when the style gets applied/unapplied. These properties
        // could have been set via Style.SetValue or TriggerBase.SetValue
        // Synchronized (write locks, lock-free reads): Covered by Style instance lock
        /* property */ internal FrugalStructList<ContainerDependent> ContainerDependents = new FrugalStructList<ContainerDependent>();
 
        // Properties driven by a resource that should be invalidated
        // when a resource dictionary changes
        // Synchronized (write locks, lock-free reads): Covered by Style instance lock
        /* property */ internal FrugalStructList<ChildPropertyDependent> ResourceDependents = new FrugalStructList<ChildPropertyDependent>();
 
        // Events driven by a this style. An entry for every childIndex that has associated events.
        // childIndex '0' is used to represent events set on the style's TargetType. This data-structure
        // will be frequently looked up during event routing.
        // Synchronized (write locks, lock-free reads): Covered by Style instance lock
        /* property */ internal ItemStructList<ChildEventDependent> EventDependents = new ItemStructList<ChildEventDependent>(1);
 
        // Used by EventTrigger: Maps a RoutedEventID to a set of TriggerAction objects
        //  to be performed.
        internal HybridDictionary _triggerActions = null;
 
        // Data trigger information.  An entry for each Binding that appears in a
        // condition of a data trigger.
        // Synchronized: Covered by Style instance
        internal HybridDictionary _dataTriggerRecordFromBinding;
 
        // An entry for each Binding that appears in a DataTrigger with EnterAction or ExitAction
        //  This overlaps but should not be the same as _dataTriggerRecordFromBinding above:
        //   A DataTrigger can have Setters but no EnterAction/ExitAction.  (The reverse can also be true.)
        internal HybridDictionary DataTriggersWithActions = null;
 
        // Unique index for every instance of Style
        // Synchronized: Covered by Style.Synchronized
        private static int StyleInstanceCount = 0;
 
        // Global, cross-object synchronization
        internal static object Synchronized = new object();
 
        private const int TargetTypeID = 0x01;
        internal const int BasedOnID    = 0x02;
 
        // Using the modified flags to note whether we have an EventSetter.
        private const int HasEventSetter = 0x10;
 
        private int _modified = 0;
 
        private void SetModified(int id) { _modified |= id; }
        internal bool IsModified(int id) { return (id & _modified) != 0; }
    }
}