ResourceDictionary.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Framework / System / Windows / ResourceDictionary.cs / 1586720 / ResourceDictionary.cs

                            /****************************************************************************\ 
*
* File: ResourceDictionary.cs
*
*  Dictionary that holds Resources for Framework components. 
*
* Copyright (C) 2003 by Microsoft Corporation.  All rights reserved. 
* 
\***************************************************************************/
 
using System;
using System.IO;
using System.Net;
using System.Collections; 
using System.Collections.Generic;
using System.Collections.ObjectModel; 
using System.Collections.Specialized; 
using System.Diagnostics;
using System.ComponentModel; 
using System.Security;
using System.Windows.Threading;
using System.Windows.Media;
using System.IO.Packaging; 
using MS.Internal.IO.Packaging;         // for PackageCacheEntry
using System.Globalization; 
using System.Windows.Navigation; 

using MS.Internal; 
using MS.Internal.Utility;
using MS.Internal.AppModel;
using MS.Utility;
using System.Xaml; 
using System.Xaml.Permissions;
using System.Windows.Baml2006; 
using System.Windows.Markup; 

namespace System.Windows 
{
    /// 
    ///     Dictionary that holds Resources for Framework components.
    ///  
    [Localizability(LocalizationCategory.Ignore)]
    [Ambient] 
    [UsableDuringInitialization(true)] 
    public class ResourceDictionary : IDictionary, ISupportInitialize, System.Windows.Markup.IUriContext, System.Windows.Markup.INameScope
    { 
        #region Constructor

        /// 
        ///     Constructor for ResourceDictionary 
        /// 
        public ResourceDictionary() 
        { 
            _baseDictionary = new Hashtable();
            IsThemeDictionary = SystemResources.IsSystemResourcesParsing; 
        }

        #endregion Constructor
 
        #region PublicAPIs
 
        ///  
        ///     Copies the dictionary's elements to a one-dimensional
        ///     Array instance at the specified index. 
        /// 
        /// 
        ///     The one-dimensional Array that is the destination of the
        ///     DictionaryEntry objects copied from Hashtable. The Array 
        ///     must have zero-based indexing.
        ///  
        ///  
        ///     The zero-based index in array at which copying begins.
        ///  
        public void CopyTo(DictionaryEntry[] array, int arrayIndex)
        {
            if (CanBeAccessedAcrossThreads)
            { 
                lock(((ICollection)this).SyncRoot)
                { 
                    CopyToWithoutLock(array, arrayIndex); 
                }
            } 
            else
            {
                CopyToWithoutLock(array, arrayIndex);
            } 
        }
 
        private void CopyToWithoutLock(DictionaryEntry[] array, int arrayIndex) 
        {
            if (array == null) 
            {
                throw new ArgumentNullException("array");
            }
 
            _baseDictionary.CopyTo(array, arrayIndex);
 
            int length = arrayIndex + Count; 
            for (int i = arrayIndex; i < length; i++)
            { 
                DictionaryEntry entry = array[i];
                object value = entry.Value;
                bool canCache;
                if (RealizeDeferContent(entry.Key, ref value, out canCache)) 
                {
                    entry.Value = value; 
                } 
            }
        } 

        ///
        ///     List of ResourceDictionaries merged into this Resource Dictionary
        /// 
        public Collection MergedDictionaries
        { 
            get 
            {
                if (_mergedDictionaries == null) 
                {
                    _mergedDictionaries = new ResourceDictionaryCollection(this);
                    _mergedDictionaries.CollectionChanged += OnMergedDictionariesChanged;
                } 

                return _mergedDictionaries; 
            } 
        }
 
        ///
        ///     Uri to load this resource from, it will clear the current state of the ResourceDictionary
        ///
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] 
        public Uri Source
        { 
            get 
            {
                return _source; 
            }
            set
            {
                if (value == null || String.IsNullOrEmpty(value.OriginalString)) 
                {
                    throw new ArgumentException(SR.Get(SRID.ResourceDictionaryLoadFromFailure, value == null ? "''" : value.ToString())); 
                } 

                _source = value; 

                Clear();

                Uri uri = BindUriHelper.GetResolvedUri(_baseUri, _source); 
                WebRequest request = WpfWebRequestHelper.CreateRequest(uri);
                WpfWebRequestHelper.ConfigCachePolicy(request, false); 
                ContentType contentType = null; 
                Stream s = null;
 
                try
                {
                    s = WpfWebRequestHelper.GetResponseStream(request, out contentType);
                } 
                catch (System.IO.IOException)
                { 
                    if (IsSourcedFromThemeDictionary) 
                    {
                        switch (_fallbackState) 
                        {
                            case FallbackState.Classic:
                                {
                                    _fallbackState = FallbackState.Generic; 
                                    Uri classicResourceUri = ThemeDictionaryExtension.GenerateFallbackUri(this, SystemResources.ClassicResourceName);
                                    Debug.Assert(classicResourceUri != null); 
 
                                    Source = classicResourceUri;
                                    // After this recursive call has returned we are sure 
                                    // that we have tried all fallback paths and so now
                                    // reset the _fallbackState
                                    _fallbackState = FallbackState.Classic;
                                } 
                                break;
                            case FallbackState.Generic: 
                                { 
                                    _fallbackState = FallbackState.None;
                                    Uri genericResourceUri = ThemeDictionaryExtension.GenerateFallbackUri(this, SystemResources.GenericResourceName); 

                                    Debug.Assert(genericResourceUri != null);
                                    Source = genericResourceUri;
 
                                }
                                break; 
                        } 
                        return;
                    } 
                    else
                    {
                        throw;
                    } 
                }
 
                // MimeObjectFactory.GetObjectAndCloseStream will try to find the object converter basing on the mime type. 
                // It can be a [....]/async converter. It's the converter's responsiblity to close the stream.
                // If it fails to find a convert, this call will return null. 
                System.Windows.Markup.XamlReader asyncObjectConverter;
                ResourceDictionary loadedRD = MimeObjectFactory.GetObjectAndCloseStream(s, contentType, uri, false, false, false /*allowAsync*/, false /*isJournalNavigation*/, out asyncObjectConverter)
                                            as ResourceDictionary;
 
                if (loadedRD == null)
                { 
                    throw new InvalidOperationException(SR.Get(SRID.ResourceDictionaryLoadFromFailure, _source.ToString())); 
                }
 
                // ReferenceCopy all the key-value pairs in the _baseDictionary
                _baseDictionary = loadedRD._baseDictionary;

                // ReferenceCopy all the entries in the MergedDictionaries collection 
                _mergedDictionaries = loadedRD._mergedDictionaries;
 
                // ReferenceCopy all of the deferred content state 
                CopyDeferredContentFrom(loadedRD);
 
                // Copy over the HasImplicitStyles flag
                HasImplicitStyles = loadedRD.HasImplicitStyles;

                if (!IsInitializePending) 
                {
                    // Fire Invalidations for the changes made by asigning a new Source 
                    NotifyOwners(new ResourcesChangeInfo(null, this)); 
                }
            } 
        }

        #region INameScope
        ///  
        /// Registers the name - element combination
        ///  
        /// name of the element 
        /// Element where name is defined
        public void RegisterName(string name, object scopedElement) 
        {
            throw new NotSupportedException(SR.Get(SRID.NamesNotSupportedInsideResourceDictionary));
        }
 
        /// 
        /// Unregisters the name - element combination 
        ///  
        /// Name of the element
        public void UnregisterName(string name) 
        {
            // Do Nothing as Names cannot be registered on ResourceDictionary
        }
 
        /// 
        /// Find the element given name 
        ///  
        /// Name of the element
        /// null always 
        public object FindName(string name)
        {
            return null;
        } 

        #endregion INameScope 
 
        #region IUriContext
 
        /// 
        ///     Accessor for the base uri of the ResourceDictionary
        /// 
        Uri System.Windows.Markup.IUriContext.BaseUri 
        {
            get 
            { 
                return  _baseUri;
            } 
            set
            {
                _baseUri = value;
            } 
        }
 
        #endregion IUriContext 

        #endregion PublicAPIs 

        #region IDictionary

        ///  
        ///     Gets a value indicating whether the IDictionary has a fixed size.
        ///  
        public bool IsFixedSize 
        {
            get { return _baseDictionary.IsFixedSize; } 
        }

        /// 
        ///     Gets a value indicating whether the ResourceDictionary is read-only. 
        /// 
        public bool IsReadOnly 
        { 
            get { return ReadPrivateFlag(PrivateFlags.IsReadOnly); }
            internal set 
            {
                WritePrivateFlag(PrivateFlags.IsReadOnly, value);

                if (value == true) 
                {
                    // Seal all the styles and templates in this dictionary 
                    SealValues(); 
                }
 
                // Set all the merged resource dictionaries as ReadOnly
                if (_mergedDictionaries != null)
                {
                    for (int i = 0; i < _mergedDictionaries.Count; i++) 
                    {
                        _mergedDictionaries[i].IsReadOnly = value; 
                    } 
                }
            } 
        }

        /// 
        ///     Gets or sets the value associated with the specified key. 
        /// 
        ///  
        ///     Fire Invalidations only for changes made after the Init Phase 
        ///     If the key is not found on this ResourceDictionary, it will look on any MergedDictionaries for it
        ///  
        public object this[object key]
        {
            get
            { 
                bool canCache;
                return GetValue(key, out canCache); 
            } 

            set 
            {
                // Seal styles and templates within App and Theme dictionary
                SealValue(value);
 
                if (CanBeAccessedAcrossThreads)
                { 
                    lock(((ICollection)this).SyncRoot) 
                    {
                        SetValueWithoutLock(key, value); 
                    }
                }
                else
                { 
                    SetValueWithoutLock(key, value);
                } 
            } 
        }
 
        // This should only be called in the deferred BAML loading scenario.  We
        // cache all the data that we need away and then get rid of the actual object.
        // No one needs to actually get this property so we're returning null.  This
        // property has to be public since the XAML parser cannot set this internal 
        // property in this scenario.
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] 
        public DeferrableContent DeferrableContent 
        {
            get 
            {
                return null;
            }
            set 
            {
                this.SetDeferrableContent(value); 
            } 
        }
 
        private void SetValueWithoutLock(object key, object value)
        {
            if (IsReadOnly)
            { 
                throw new InvalidOperationException(SR.Get(SRID.ResourceDictionaryIsReadOnly));
            } 
 
            object oldValue = _baseDictionary[key];
 
            if (oldValue != value)
            {
                // We need to validate all the deferred references that refer
                // to the old resource before we overwrite it. 
                ValidateDeferredResourceReferences(key);
 
                if( TraceResourceDictionary.IsEnabled ) 
                {
                    TraceResourceDictionary.Trace( TraceEventType.Start, 
                                                   TraceResourceDictionary.AddResource,
                                                   this,
                                                   key,
                                                   value ); 
                }
 
 
                _baseDictionary[key] = value;
 
                // Update the HasImplicitStyles flag
                UpdateHasImplicitStyles(key);

                // Notify owners of the change and fire invalidate if already initialized 
                NotifyOwners(new ResourcesChangeInfo(key));
 
                if( TraceResourceDictionary.IsEnabled ) 
                {
                    TraceResourceDictionary.Trace( 
                                                  TraceEventType.Stop,
                                                  TraceResourceDictionary.AddResource,
                                                  this,
                                                  key, 
                                                  value );
                } 
 

            } 
        }

        internal object GetValue(object key, out bool canCache)
        { 
            if (CanBeAccessedAcrossThreads)
            { 
                lock(((ICollection)this).SyncRoot) 
                {
                    return GetValueWithoutLock(key, out canCache); 
                }
            }
            else
            { 
                return GetValueWithoutLock(key, out canCache);
            } 
        } 

        private object GetValueWithoutLock(object key, out bool canCache) 
        {
            object value = _baseDictionary[key];
            if (value != null)
            { 
                RealizeDeferContent(key, ref value, out canCache);
            } 
            else 
            {
                canCache = true; 

                //Search for the value in the Merged Dictionaries
                if (_mergedDictionaries != null)
                { 
                    for (int i = MergedDictionaries.Count - 1; (i > -1); i--)
                    { 
                        // Note that MergedDictionaries collection can also contain null values 
                        ResourceDictionary mergedDictionary = MergedDictionaries[i];
                        if (mergedDictionary != null) 
                        {
                            value = mergedDictionary.GetValue(key, out canCache);
                            if (value != null)
                            { 
                                break;
                            } 
                        } 
                    }
                } 
            }

            return value;
        } 

        // Gets the type of the value stored at the given key 
        internal Type GetValueType(object key, out bool found) 
        {
            found = false; 
            Type valueType = null;

            object value = _baseDictionary[key];
            if (value != null) 
            {
                found = true; 
 
                KeyRecord keyRecord = value as KeyRecord;
                if (keyRecord != null) 
                {
                    Debug.Assert(_numDefer > 0, "The stream was closed before all deferred content was loaded.");
                    valueType = GetTypeOfFirstObject(keyRecord);
                } 
                else
                { 
                    valueType = value.GetType(); 
                }
 
            }
            else
            {
                // Search for the value in the Merged Dictionaries 
                if (_mergedDictionaries != null)
                { 
                    for (int i = MergedDictionaries.Count - 1; (i > -1); i--) 
                    {
                        // Note that MergedDictionaries collection can also contain null values 
                        ResourceDictionary mergedDictionary = MergedDictionaries[i];
                        if (mergedDictionary != null)
                        {
                            valueType = mergedDictionary.GetValueType(key, out found); 
                            if (found)
                            { 
                                break; 
                            }
                        } 
                    }
                }
            }
 
            return valueType;
        } 
 
        /// 
        ///     Gets a copy of the ICollection containing the keys of the IDictionary. 
        /// 
        public ICollection Keys
        {
            get 
            {
                object[] keysCollection = new object[Count]; 
                _baseDictionary.Keys.CopyTo(keysCollection, 0); 
                return keysCollection;
            } 
        }

        /// 
        ///     Gets an ICollection containing the values in the Hashtable 
        /// 
        /// An ICollection containing the values in the Hashtable 
        public ICollection Values 
        {
            get 
            {
                return new ResourceValuesCollection(this);
            }
        } 

        ///  
        ///     Adds an entry 
        /// 
        ///  
        ///     Fire Invalidations only for changes made after the Init Phase
        /// 
        public void Add(object key, object value)
        { 
            // Seal styles and templates within App and Theme dictionary
            SealValue(value); 
 
            if (CanBeAccessedAcrossThreads)
            { 
                lock(((ICollection)this).SyncRoot)
                {
                    AddWithoutLock(key, value);
                } 
            }
            else 
            { 
                AddWithoutLock(key, value);
            } 

        }

        private void AddWithoutLock(object key, object value) 
        {
            if (IsReadOnly) 
            { 
                throw new InvalidOperationException(SR.Get(SRID.ResourceDictionaryIsReadOnly));
            } 

            if( TraceResourceDictionary.IsEnabled )
            {
                TraceResourceDictionary.Trace( TraceEventType.Start, 
                                               TraceResourceDictionary.AddResource,
                                               this, 
                                               key, 
                                               value );
            } 


            _baseDictionary.Add(key, value);
 
            // Update the HasImplicitKey flag
            UpdateHasImplicitStyles(key); 
 
            // Notify owners of the change and fire invalidate if already initialized
            NotifyOwners(new ResourcesChangeInfo(key)); 

            if( TraceResourceDictionary.IsEnabled )
            {
                TraceResourceDictionary.Trace( TraceEventType.Stop, 
                                               TraceResourceDictionary.AddResource,
                                               this, 
                                               key, 
                                               value );
            } 

        }

        ///  
        ///     Removes all elements from the IDictionary.
        ///  
        public void Clear() 
        {
            if (CanBeAccessedAcrossThreads) 
            {
                lock(((ICollection)this).SyncRoot)
                {
                    ClearWithoutLock(); 
                }
            } 
            else 
            {
                ClearWithoutLock(); 
            }
        }

        private void ClearWithoutLock() 
        {
            if (IsReadOnly) 
            { 
                throw new InvalidOperationException(SR.Get(SRID.ResourceDictionaryIsReadOnly));
            } 

            if (Count > 0)
            {
                // We need to validate all the deferred references that refer 
                // to the old resource before we clear it.
                ValidateDeferredResourceReferences(null); 
 
                // remove inheritance context from all values that got it from
                // this dictionary 
                RemoveInheritanceContextFromValues();

                _baseDictionary.Clear();
 
                // Notify owners of the change and fire invalidate if already initialized
                NotifyOwners(ResourcesChangeInfo.CatastrophicDictionaryChangeInfo); 
            } 
        }
 
        /// 
        ///     Determines whether the IDictionary contains an element with the specified key.
        ///     if the Key is not contained in this ResourceDictionary, it will check in the MergedDictionaries too
        ///  
        public bool Contains(object key)
        { 
            bool result = _baseDictionary.Contains(key); 

            if (result) 
            {
                KeyRecord keyRecord = _baseDictionary[key] as KeyRecord;
                if (keyRecord != null && _deferredLocationList.Contains(keyRecord))
                { 
                    return false;
                } 
            } 

            //Search for the value in the Merged Dictionaries 
            if (_mergedDictionaries != null)
            {
                for (int i = MergedDictionaries.Count - 1; (i > -1) && !result; i--)
                { 
                    // Note that MergedDictionaries collection can also contain null values
                    ResourceDictionary mergedDictionary = MergedDictionaries[i]; 
                    if (mergedDictionary != null) 
                    {
                        result = mergedDictionary.Contains(key); 
                    }
                }
            }
            return result; 
        }
 
        ///  
        ///     Determines whether the IDictionary contains a BamlObjectFactory against the specified key.
        ///     if the Key is not contained in this ResourceDictionary, it will check in the MergedDictionaries too 
        /// 
        private bool ContainsBamlObjectFactory(object key)
        {
            return GetBamlObjectFactory(key) != null; 
        }
 
        ///  
        ///     Retrieves a KeyRecord from the IDictionary using the specified key.
        ///     If the Key is not contained in this ResourceDictionary, it will check in the MergedDictionaries too 
        /// 
        private KeyRecord GetBamlObjectFactory(object key)
        {
            if (_baseDictionary.Contains(key)) 
            {
                return _baseDictionary[key] as KeyRecord; 
            } 

            //Search for the value in the Merged Dictionaries 
            if (_mergedDictionaries != null)
            {
                for (int i = MergedDictionaries.Count - 1; i > -1; i--)
                { 
                    // Note that MergedDictionaries collection can also contain null values
                    ResourceDictionary mergedDictionary = MergedDictionaries[i]; 
                    if (mergedDictionary != null) 
                    {
                        KeyRecord keyRecord = mergedDictionary.GetBamlObjectFactory(key); 
                        if (keyRecord != null)
                        {
                            return keyRecord;
                        } 
                    }
                } 
            } 

            return null; 
        }

        /// 
        ///     Returns an IDictionaryEnumerator that can iterate through the Hashtable 
        /// 
        /// An IDictionaryEnumerator for the Hashtable 
        public IDictionaryEnumerator GetEnumerator() 
        {
            return new ResourceDictionaryEnumerator(this); 
        }

        /// 
        ///     Removes an entry 
        /// 
        ///  
        ///     Fire Invalidations only for changes made after the Init Phase 
        /// 
        public void Remove(object key) 
        {
            if (CanBeAccessedAcrossThreads)
            {
                lock(((ICollection)this).SyncRoot) 
                {
                    RemoveWithoutLock(key); 
                } 
            }
            else 
            {
                RemoveWithoutLock(key);
            }
        } 

        private void RemoveWithoutLock(object key) 
        { 
            if (IsReadOnly)
            { 
                throw new InvalidOperationException(SR.Get(SRID.ResourceDictionaryIsReadOnly));
            }

            // We need to validate all the deferred references that refer 
            // to the old resource before we remove it.
            ValidateDeferredResourceReferences(key); 
 
            // remove the inheritance context from the value, if it came from
            // this dictionary 
            RemoveInheritanceContext(_baseDictionary[key]);

            _baseDictionary.Remove(key);
 
            // Notify owners of the change and fire invalidate if already initialized
            NotifyOwners(new ResourcesChangeInfo(key)); 
        } 

        #endregion IDictionary 

        #region ICollection

        ///  
        ///     Gets the number of elements contained in the ICollection.
        ///  
        public int Count 
        {
            get { return _baseDictionary.Count; } 
        }

        /// 
        ///     Gets a value indicating whether access to the ICollection is synchronized (thread-safe). 
        /// 
        bool ICollection.IsSynchronized 
        { 
            get { return _baseDictionary.IsSynchronized; }
        } 

        /// 
        ///     Gets an object that can be used to synchronize access to the ICollection.
        ///  
        object ICollection.SyncRoot
        { 
            get 
            {
                if (CanBeAccessedAcrossThreads) 
                {
                    // Notice that we are acquiring the ThemeDictionaryLock. This
                    // is because the _parserContext used for template expansion
                    // shares data-structures such as the BamlMapTable and 
                    // XamlTypeMapper with the parent ParserContext that was used
                    // to build the template in the first place. So if this template 
                    // is from the App.Resources then the ParserContext that is used for 
                    // loading deferred content in the app dictionary and the
                    // _parserContext used to load template content share the same 
                    // instances of BamlMapTable and XamlTypeMapper. Hence we need to
                    // make sure that we lock on the same object inorder to serialize
                    // access to these data-structures in multi-threaded scenarios.
                    // Look at comment in Frameworktemplate.LoadContent to understand 
                    // why we use the ThemeDictionaryLock for template expansion.
 
                    return SystemResources.ThemeDictionaryLock; 
                }
                else 
                {
                    return _baseDictionary.SyncRoot;
                }
            } 
        }
 
        ///  
        ///     Copies the dictionary's elements to a one-dimensional
        ///     Array instance at the specified index. 
        /// 
        /// 
        ///     The one-dimensional Array that is the destination of the
        ///     DictionaryEntry objects copied from Hashtable. The Array 
        ///     must have zero-based indexing.
        ///  
        ///  
        ///     The zero-based index in array at which copying begins.
        ///  
        void ICollection.CopyTo(Array array, int arrayIndex)
        {
            CopyTo(array as DictionaryEntry[], arrayIndex);
        } 

        #endregion ICollection 
 
        #region IEnumerable
 
        IEnumerator IEnumerable.GetEnumerator()
        {
            return ((IDictionary)this).GetEnumerator();
        } 

        #endregion IEnumerable 
 
        #region ISupportInitialize
 
        /// 
        ///     Mark the begining of the Init phase
        /// 
        ///  
        ///     BeginInit and EndInit follow a transaction model. BeginInit marks the
        ///     dictionary uninitialized and EndInit marks it initialized. 
        ///  
        public void BeginInit()
        { 
            // Nested BeginInits on the same instance aren't permitted
            if (IsInitializePending)
            {
                throw new InvalidOperationException(SR.Get(SRID.NestedBeginInitNotSupported)); 
            }
 
            IsInitializePending = true; 
            IsInitialized = false;
        } 

        /// 
        ///     Fire Invalidation at the end of Init phase
        ///  
        /// 
        ///     BeginInit and EndInit follow a transaction model. BeginInit marks the 
        ///     dictionary uninitialized and EndInit marks it initialized. 
        /// 
        public void EndInit() 
        {
            // EndInit without a BeginInit isn't permitted
            if (!IsInitializePending)
            { 
                throw new InvalidOperationException(SR.Get(SRID.EndInitWithoutBeginInitNotSupported));
            } 
            Debug.Assert(IsInitialized == false, "Dictionary should not be initialized when EndInit is called"); 

            IsInitializePending = false; 
            IsInitialized = true;

            // Fire Invalidations collectively for all changes made during the Init Phase
            NotifyOwners(new ResourcesChangeInfo(null, this)); 
        }
 
        #endregion ISupportInitialize 

        #region DeferContent 

        private bool CanCache(KeyRecord keyRecord, object value)
        {
            if (keyRecord.SharedSet) 
            {
                return keyRecord.Shared; 
            } 
            else
            { 
                return true;
            }
        }
 
        private bool RealizeDeferContent(object key, ref object value, out bool canCache)
        { 
            KeyRecord keyRecord = value as KeyRecord; 

            // If the value is not a key record then 
            // it has already been realized, is not deferred and is a "ready to go" value.
            if (keyRecord == null)
            {
                canCache = true; 
                return false;   /* Not deferred content */
            } 
 
            Debug.Assert(_numDefer > 0, "The stream was closed before all deferred content was loaded.");
 
            // We want to return null if a resource asks for itself. It should return null
            //