Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / wpf / src / Framework / System / Windows / ResourceDictionary.cs / 1 / 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.Windows.Threading;
using System.Windows.Media;
using System.Windows.Markup;
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;
namespace System.Windows
{
///
/// Dictionary that holds Resources for Framework components.
///
[Localizability(LocalizationCategory.Ignore)]
public class ResourceDictionary : IDictionary, INameScope, ISupportInitialize, IUriContext
{
#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;
Stream s = WpfWebRequestHelper.GetResponseStream(request, out contentType);
// 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.
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
_buffer = loadedRD._buffer;
_bamlStream = loadedRD._bamlStream;
_startPosition = loadedRD._startPosition;
_contentSize = loadedRD._contentSize;
_context = loadedRD._context;
_rootElement = loadedRD._rootElement;
_reader = loadedRD._reader;
_numDefer = loadedRD._numDefer;
// 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 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);
}
}
}
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;
IBamlDictionaryKey keyRecord = value as IBamlDictionaryKey;
if (keyRecord != null)
{
Debug.Assert(_numDefer > 0, "The stream was closed before all deferred content was loaded.");
Int32 valuePosition = keyRecord.ValuePosition;
BamlRecordReader previousRecordReader = _context.BamlReader;
BamlRecordReader reader = Reader;
_context.BamlReader = reader;
try
{
// Remember the starting stream position, so that it can be repositioned
// after the read to handle recursive operations on the same stream.
Int64 startPosition = _bamlStream.Position;
_bamlStream.Position = _startPosition + valuePosition;
BamlElementStartRecord bamlElementStartRecord = (BamlElementStartRecord)reader.GetNextRecord();
valueType = reader.MapTable.GetTypeFromId(bamlElementStartRecord.TypeId);
_bamlStream.Position = startPosition;
}
finally
{
// Restore the previousRecordReader into the context
_context.BamlReader = previousRecordReader;
}
}
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);
//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
///
internal bool ContainsBamlObjectFactory(object key)
{
return GetBamlObjectFactory(key) != null;
}
///
/// Retrieves an IBamlDictionaryKey from the IDictionary using the specified key.
/// If the Key is not contained in this ResourceDictionary, it will check in the MergedDictionaries too
///
internal IBamlDictionaryKey GetBamlObjectFactory(object key)
{
if (_baseDictionary.Contains(key))
{
return _baseDictionary[key] as IBamlDictionaryKey;
}
//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)
{
IBamlDictionaryKey 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(IBamlDictionaryKey keyRecord, object value)
{
if (keyRecord.SharedSet)
{
return keyRecord.Shared;
}
else
{
return true;
}
}
private bool RealizeDeferContent(ref object value)
{
bool canCache;
return RealizeDeferContent(null, ref value, out canCache);
}
private bool RealizeDeferContent(object key, ref object value, out bool canCache)
{
IBamlDictionaryKey keyRecord = value as IBamlDictionaryKey;
if (keyRecord != null)
{
Debug.Assert(_numDefer > 0, "The stream was closed before all deferred content was loaded.");
Int32 valuePosition = keyRecord.ValuePosition;
// Store the StaticResources list for the current key record into the parserContext so that
// StaticResourceId records within the current value can use it for lookup.
_context.StaticResourcesStack.Add(keyRecord.StaticResourceValues);
try
{
if( TraceResourceDictionary.IsEnabled )
{
TraceResourceDictionary.Trace(
TraceEventType.Start,
TraceResourceDictionary.RealizeDeferContent,
this,
key,
value );
}
value = CreateObject(valuePosition, key);
}
finally
{
if( TraceResourceDictionary.IsEnabled )
{
TraceResourceDictionary.Trace(
TraceEventType.Stop,
TraceResourceDictionary.RealizeDeferContent,
this,
key,
value );
}
_context.StaticResourcesStack.RemoveAt(_context.StaticResourcesStack.Count-1);
}
if (key != null)
{
canCache = CanCache(keyRecord, value);
if (canCache)
{
// Seal styles and templates within App and Theme dictionary
SealValue(value);
_numDefer--;
_baseDictionary[key] = value;
if (_numDefer == 0)
{
_reader = null;
_context = null;
// Only close the stream if it was a Memory Stream
// built over our buffer.
if(null != _buffer)
{
_bamlStream.Close();
_bamlStream = null;
_buffer = null;
}
}
}
}
else
{
canCache = true;
}
return true; /* Defer content */
}
canCache = true;
return false; /* Not defer content */
}
///
/// Add a byte array that contains deferable content
///
internal void SetDeferableContent(
byte[] buffer,
ParserContext context,
object rootElement,
ArrayList keyCollection,
List staticResourceValuesList)
{
if (buffer == null)
{
throw new ArgumentNullException("buffer");
}
if (context == null)
{
throw new ArgumentNullException("context");
}
if (keyCollection == null)
{
throw new ArgumentNullException("keyCollection");
}
// If we already have the Source set then we can ignore
// this deferable content section
if (_source == null)
{
if (_context == null)
{
_buffer = buffer;
_bamlStream = new MemoryStream(buffer);
_startPosition = 0;
_contentSize = buffer.Length;
_context = context.Clone();
_rootElement = rootElement;
SetKeys(keyCollection, staticResourceValuesList, context);
}
else
{
throw new InvalidOperationException(SR.Get(SRID.ResourceDictionaryDuplicateDeferredContent));
}
}
else if (keyCollection.Count > 0)
{
throw new InvalidOperationException(SR.Get(SRID.ResourceDictionaryDeferredContentFailure));
}
}
///
/// Add a stream that contains deferable content
///
internal void SetDeferableContent(
Stream bamlStream,
Int64 startPosition,
Int32 contentSize,
ParserContext context,
object rootElement,
ArrayList keyCollection,
List staticResourceValuesList)
{
if (bamlStream == null)
{
throw new ArgumentNullException("bamlStream");
}
if (!bamlStream.CanRead)
{
throw new ArgumentException(SR.Get(SRID.InputStreamMustBeReadable,"bamlStream"));
}
if (context == null)
{
throw new ArgumentNullException("context");
}
if (keyCollection == null)
{
throw new ArgumentNullException("keyCollection");
}
if (startPosition < 0)
{
throw new ArgumentOutOfRangeException("startPosition");
}
if (contentSize < 0)
{
throw new ArgumentOutOfRangeException("contentSize");
}
// If we already have the Source set then we can ignore
// this deferable content section
if (_source == null)
{
if (_context == null)
{
_bamlStream = bamlStream;
_startPosition = startPosition;
_contentSize = contentSize;
_context = context.Clone();
// We need to clear the Journal bit, because we know we won't be able to honor it correctly.
// Journaling journals the properties in the logical tree, so doesn't journal properties in the
// Template/Resources. This shouldn't be hard-coded here, but is an internal solution for V1.
_context.SkipJournaledProperties = false;
_rootElement = rootElement;
SetKeys(keyCollection, staticResourceValuesList, context);
}
else
{
throw new InvalidOperationException(SR.Get(SRID.ResourceDictionaryDuplicateDeferredContent));
}
}
else if (keyCollection.Count > 0)
{
throw new InvalidOperationException(SR.Get(SRID.ResourceDictionaryDeferredContentFailure));
}
}
private void SetKeys(
ArrayList keyCollection,
List staticResourceValuesList,
ParserContext context)
{
_numDefer = keyCollection.Count;
// Use the array Count property to avoid range checking inside the loop
for (int i = 0; i < keyCollection.Count; i++)
{
IBamlDictionaryKey keyRecord = keyCollection[i] as IBamlDictionaryKey;
if (keyRecord != null)
{
SetStaticResources(staticResourceValuesList[i], context);
keyRecord.StaticResourceValues = staticResourceValuesList[i];
// Update the HasImplicitStyles flag
UpdateHasImplicitStyles(keyRecord.KeyObject);
_baseDictionary.Add(keyRecord.KeyObject, keyRecord);
if( TraceResourceDictionary.IsEnabled )
{
TraceResourceDictionary.TraceActivityItem(
TraceResourceDictionary.SetKey,
this,
keyRecord.KeyObject );
}
}
else
{
throw new ArgumentException(SR.Get(SRID.KeyCollectionHasInvalidKey));
}
}
// Notify owners of the HasImplicitStyles flag value
// but there is not need to fire an invalidation.
NotifyOwners(new ResourcesChangeInfo(null, this));
// Get the XamlObjectIds (Name, Uid, and Key) that are currently in our context.
// When we realize tese resources, we'll pass in that information, so that they
// can generate good exception messages.
Type objectType;
XamlParseException.GetObjectContext(context.BamlReader,
null, // currentObjectIdentifiers
_contextXamlObjectIds,
out objectType );
}
private void SetStaticResources(object[] staticResourceValues, ParserContext context)
{
if (staticResourceValues != null && staticResourceValues.Length > 0)
{
bool inDeferredSection = context.InDeferredSection;
for (int i=0; i(1);
}
else if (_ownerFEs.Contains(fe) && ContainsCycle(this))
{
throw new InvalidOperationException(SR.Get(SRID.ResourceDictionaryInvalidMergedDictionary));
}
// Propagate the HasImplicitStyles flag to the new owner
if (HasImplicitStyles)
{
fe.ShouldLookupImplicitStyles = true;
}
_ownerFEs.Add(fe);
}
else
{
FrameworkContentElement fce = owner as FrameworkContentElement;
if (fce != null)
{
if (_ownerFCEs == null)
{
_ownerFCEs = new List(1);
}
else if (_ownerFCEs.Contains(fce) && ContainsCycle(this))
{
throw new InvalidOperationException(SR.Get(SRID.ResourceDictionaryInvalidMergedDictionary));
}
// Propagate the HasImplicitStyles flag to the new owner
if (HasImplicitStyles)
{
fce.ShouldLookupImplicitStyles = true;
}
_ownerFCEs.Add(fce);
}
else
{
Application app = owner as Application;
if (app != null)
{
if (_ownerApps == null)
{
_ownerApps = new List(1);
}
else if (_ownerApps.Contains(app) && ContainsCycle(this))
{
throw new InvalidOperationException(SR.Get(SRID.ResourceDictionaryInvalidMergedDictionary));
}
// Propagate the HasImplicitStyles flag to the new owner
if (HasImplicitStyles)
{
app.HasImplicitStylesInResources = true;
}
_ownerApps.Add(app);
// An Application ResourceDictionary can be accessed across threads
CanBeAccessedAcrossThreads = true;
// Seal all the styles and templates in this app dictionary
SealValues();
}
}
}
AddOwnerToAllMergedDictionaries(owner);
// This dictionary will be marked initialized if no one has called BeginInit on it.
// This is done now because having an owner is like a parenting operation for the dictionary.
TryInitialize();
}
// Remove an owner for this dictionary
internal void RemoveOwner(DispatcherObject owner)
{
FrameworkElement fe = owner as FrameworkElement;
if (fe != null)
{
if (_ownerFEs != null)
{
_ownerFEs.Remove(fe);
if (_ownerFEs.Count == 0)
{
_ownerFEs = null;
}
}
}
else
{
FrameworkContentElement fce = owner as FrameworkContentElement;
if (fce != null)
{
if (_ownerFCEs != null)
{
_ownerFCEs.Remove(fce);
if (_ownerFCEs.Count == 0)
{
_ownerFCEs = null;
}
}
}
else
{
Application app = owner as Application;
if (app != null)
{
if (_ownerApps != null)
{
_ownerApps.Remove(app);
if (_ownerApps.Count == 0)
{
_ownerApps = null;
}
}
}
}
}
RemoveOwnerFromAllMergedDictionaries(owner);
}
// Check if the given is an owner to this dictionary
internal bool ContainsOwner(DispatcherObject owner)
{
FrameworkElement fe = owner as FrameworkElement;
if (fe != null)
{
return (_ownerFEs != null && _ownerFEs.Contains(fe));
}
else
{
FrameworkContentElement fce = owner as FrameworkContentElement;
if (fce != null)
{
return (_ownerFCEs != null && _ownerFCEs.Contains(fce));
}
else
{
Application app = owner as Application;
if (app != null)
{
return (_ownerApps != null && _ownerApps.Contains(app));
}
}
}
return false;
}
// Helper method that tries to set IsInitialized to true if BeginInit hasn't been called before this.
// This method is called on AddOwner
private void TryInitialize()
{
if (!IsInitializePending &&
!IsInitialized)
{
IsInitialized = true;
}
}
// Call FrameworkElement.InvalidateTree with the right data
private void NotifyOwners(ResourcesChangeInfo info)
{
bool shouldInvalidate = IsInitialized;
bool hasImplicitStyles = info.IsResourceAddOperation && HasImplicitStyles;
if (shouldInvalidate || hasImplicitStyles)
{
// Invalidate all FE owners
if (_ownerFEs != null)
{
for (int i=0; i<_ownerFEs.Count; i++)
{
// Set the HasImplicitStyles flag on the owner
if (hasImplicitStyles)
_ownerFEs[i].ShouldLookupImplicitStyles = true;
// If this dictionary has been initialized fire an invalidation
// to let the tree know of this change.
if (shouldInvalidate)
TreeWalkHelper.InvalidateOnResourcesChange(_ownerFEs[i], null, info);
}
}
// Invalidate all FCE owners
if (_ownerFCEs != null)
{
for (int i=0; i<_ownerFCEs.Count; i++)
{
// Set the HasImplicitStyles flag on the owner
if (hasImplicitStyles)
_ownerFCEs[i].ShouldLookupImplicitStyles = true;
// If this dictionary has been initialized fire an invalidation
// to let the tree know of this change.
if (shouldInvalidate)
TreeWalkHelper.InvalidateOnResourcesChange(null, _ownerFCEs[i], info);
}
}
// Invalidate all App owners
if (_ownerApps != null)
{
for (int i=0; i<_ownerApps.Count; i++)
{
// Set the HasImplicitStyles flag on the owner
if (hasImplicitStyles)
_ownerApps[i].HasImplicitStylesInResources = true;
// If this dictionary has been initialized fire an invalidation
// to let the tree know of this change.
if (shouldInvalidate)
_ownerApps[i].InvalidateResourceReferences(info);
}
}
}
}
///
/// Fetches the resource corresponding to the given key from this dictionary.
/// Returns a DeferredResourceReference if the object has not been inflated yet.
///
internal object FetchResource(
object resourceKey,
bool allowDeferredResourceReference,
bool mustReturnDeferredResourceReference,
out bool canCache)
{
Debug.Assert(resourceKey != null, "ResourceKey cannot be null");
if (allowDeferredResourceReference)
{
if (ContainsBamlObjectFactory(resourceKey) ||
(mustReturnDeferredResourceReference && Contains(resourceKey)))
{
canCache = false;
DeferredResourceReference deferredResourceReference;
if (!IsThemeDictionary)
{
if (_ownerApps != null)
{
deferredResourceReference = new DeferredAppResourceReference(this, resourceKey);
}
else
{
deferredResourceReference = new DeferredResourceReference(this, resourceKey);
}
// Cache the deferredResourceReference so that it can be validated
// in case of a dictionary change prior to its inflation
if (_deferredResourceReferences == null)
{
_deferredResourceReferences = new List();
}
_deferredResourceReferences.Add(deferredResourceReference);
}
else
{
deferredResourceReference = new DeferredThemeResourceReference(this, resourceKey);
}
return deferredResourceReference;
}
}
return GetValue(resourceKey, out canCache);
}
///
/// Validate the deferredResourceReference with the given key. Key could be null meaning
/// some catastrophic operation occurred so simply validate all DeferredResourceReferences
///
private void ValidateDeferredResourceReferences(object resourceKey)
{
if (_deferredResourceReferences != null)
{
for (int i=0; i<_deferredResourceReferences.Count; i++)
{
DeferredResourceReference deferredResourceReference = _deferredResourceReferences[i];
if (resourceKey == null || Object.Equals(resourceKey, deferredResourceReference.Key))
{
#if DEBUG
int oldCount = _deferredResourceReferences.Count;
#endif
deferredResourceReference.GetValue(BaseValueSourceInternal.Unknown);
#if DEBUG
int newCount = _deferredResourceReferences.Count;
Debug.Assert(newCount == oldCount-1,
"The deferredResourceReference that was just validated should have also been removed from the list as part of the GetValue call.");
#endif
// Adjust i to accomodate the removal of an entry
i--;
}
}
}
}
///
/// Called when the MergedDictionaries collection changes
///
///
///
private void OnMergedDictionariesChanged(object sender, NotifyCollectionChangedEventArgs e)
{
List oldDictionaries = null;
List newDictionaries = null;
ResourceDictionary mergedDictionary;
ResourcesChangeInfo info;
if (e.Action != NotifyCollectionChangedAction.Reset)
{
Invariant.Assert(
(e.NewItems != null && e.NewItems.Count > 0) ||
(e.OldItems != null && e.OldItems.Count > 0),
"The NotifyCollectionChanged event fired when no dictionaries were added or removed");
// If one or more resource dictionaries were removed we
// need to remove the owners they were given by their
// parent ResourceDictionary.
if (e.Action == NotifyCollectionChangedAction.Remove
|| e.Action == NotifyCollectionChangedAction.Replace)
{
oldDictionaries = new List(e.OldItems.Count);
for (int i = 0; i < e.OldItems.Count; i++)
{
mergedDictionary = (ResourceDictionary)e.OldItems[i];
oldDictionaries.Add(mergedDictionary);
RemoveParentOwners(mergedDictionary);
}
}
// If one or more resource dictionaries were added to the merged
// dictionaries collection we need to send down the parent
// ResourceDictionary's owners.
if (e.Action == NotifyCollectionChangedAction.Add
|| e.Action == NotifyCollectionChangedAction.Replace)
{
newDictionaries = new List(e.NewItems.Count);
for (int i = 0; i < e.NewItems.Count; i++)
{
mergedDictionary = (ResourceDictionary)e.NewItems[i];
newDictionaries.Add(mergedDictionary);
// If the merged dictionary HasImplicitStyle mark the outer dictionary the same.
if (!HasImplicitStyles && mergedDictionary.HasImplicitStyles)
{
HasImplicitStyles = true;
}
// (03/12/08) Backing out the change for binary compat reasons.
// If the parent dictionary is a theme dictionary mark the merged dictionary the same.
// if (IsThemeDictionary)
// {
// mergedDictionary.IsThemeDictionary = true;
// }
PropagateParentOwners(mergedDictionary);
}
}
info = new ResourcesChangeInfo(oldDictionaries, newDictionaries, false, false, null);
}
else
{
// Case when MergedDictionary collection is cleared
info = ResourcesChangeInfo.CatastrophicDictionaryChangeInfo;
}
// Notify the owners of the change and fire
// invalidation if already initialized
NotifyOwners(info);
}
///
/// Adds the given owner to all merged dictionaries of this ResourceDictionary
///
///
private void AddOwnerToAllMergedDictionaries(DispatcherObject owner)
{
if (_mergedDictionaries != null)
{
for (int i = 0; i < _mergedDictionaries.Count; i++)
{
_mergedDictionaries[i].AddOwner(owner);
}
}
}
///
///
///
///
private void RemoveOwnerFromAllMergedDictionaries(DispatcherObject owner)
{
if (_mergedDictionaries != null)
{
for (int i = 0; i < _mergedDictionaries.Count; i++)
{
_mergedDictionaries[i].RemoveOwner(owner);
}
}
}
///
/// This sends down the owners of this ResourceDictionary into the given
/// merged dictionary. We do this because whenever a merged dictionary
/// changes it should invalidate all owners of its parent ResourceDictionary.
///
/// Note that AddOwners throw if the merged dictionary already has one of the
/// parent's owners. This implies that either we're putting a dictionary
/// into its own MergedDictionaries collection or we're putting the same
/// dictionary into the collection twice, neither of which are legal.
///
///
private void PropagateParentOwners(ResourceDictionary mergedDictionary)
{
if (_ownerFEs != null)
{
Invariant.Assert(_ownerFEs.Count > 0);
if (mergedDictionary._ownerFEs == null)
{
mergedDictionary._ownerFEs = new List(_ownerFEs.Count);
}
for (int i = 0; i < _ownerFEs.Count; i++)
{
mergedDictionary.AddOwner(_ownerFEs[i]);
}
}
if (_ownerFCEs != null)
{
Invariant.Assert(_ownerFCEs.Count > 0);
if (mergedDictionary._ownerFCEs == null)
{
mergedDictionary._ownerFCEs = new List(_ownerFCEs.Count);
}
for (int i = 0; i < _ownerFCEs.Count; i++)
{
mergedDictionary.AddOwner(_ownerFCEs[i]);
}
}
if (_ownerApps != null)
{
Invariant.Assert(_ownerApps.Count > 0);
if (mergedDictionary._ownerApps == null)
{
mergedDictionary._ownerApps = new List(_ownerApps.Count);
}
for (int i = 0; i < _ownerApps.Count; i++)
{
mergedDictionary.AddOwner(_ownerApps[i]);
}
}
}
///
/// Removes the owners of this ResourceDictionary from the given
/// merged dictionary. The merged dictionary will be left with
/// whatever owners it had before being merged.
///
///
internal void RemoveParentOwners(ResourceDictionary mergedDictionary)
{
if (_ownerFEs != null)
{
Invariant.Assert(_ownerFEs.Count > 0);
for (int i = 0; i < _ownerFEs.Count; i++)
{
mergedDictionary.RemoveOwner(_ownerFEs[i]);
}
}
if (_ownerFCEs != null)
{
Invariant.Assert(_ownerFCEs.Count > 0);
for (int i = 0; i < _ownerFCEs.Count; i++)
{
mergedDictionary.RemoveOwner(_ownerFCEs[i]);
}
}
if (_ownerApps != null)
{
Invariant.Assert(_ownerApps.Count > 0);
for (int i = 0; i < _ownerApps.Count; i++)
{
mergedDictionary.RemoveOwner(_ownerApps[i]);
}
}
}
private bool ContainsCycle(ResourceDictionary origin)
{
for (int i=0; i DeferredResourceReferences
{
get { return _deferredResourceReferences; }
}
#endregion Properties
#region Enumeration
///
/// Iterates the dictionary's entries, handling deferred content.
///
private class ResourceDictionaryEnumerator : IDictionaryEnumerator
{
internal ResourceDictionaryEnumerator(ResourceDictionary owner)
{
_owner = owner;
_keysEnumerator = _owner.Keys.GetEnumerator();
}
#region IEnumerator
object IEnumerator.Current
{
get
{
return ((IDictionaryEnumerator)this).Entry;
}
}
bool IEnumerator.MoveNext()
{
return _keysEnumerator.MoveNext();
}
void IEnumerator.Reset()
{
_keysEnumerator.Reset();
}
#endregion
#region IDictionaryEnumerator
DictionaryEntry IDictionaryEnumerator.Entry
{
get
{
object key = _keysEnumerator.Current;
object value = _owner[key];
return new DictionaryEntry(key, value);
}
}
object IDictionaryEnumerator.Key
{
get
{
return _keysEnumerator.Current;
}
}
object IDictionaryEnumerator.Value
{
get
{
return _owner[_keysEnumerator.Current];
}
}
#endregion
#region Data
private ResourceDictionary _owner;
private IEnumerator _keysEnumerator;
#endregion
}
///
/// Iterator for the dictionary's Values collection, handling deferred content.
///
private class ResourceValuesEnumerator : IEnumerator
{
internal ResourceValuesEnumerator(ResourceDictionary owner)
{
_owner = owner;
_keysEnumerator = _owner.Keys.GetEnumerator();
}
#region IEnumerator
object IEnumerator.Current
{
get
{
return _owner[_keysEnumerator.Current];
}
}
bool IEnumerator.MoveNext()
{
return _keysEnumerator.MoveNext();
}
void IEnumerator.Reset()
{
_keysEnumerator.Reset();
}
#endregion
#region Data
private ResourceDictionary _owner;
private IEnumerator _keysEnumerator;
#endregion
}
///
/// Represents the dictionary's Values collection, handling deferred content.
///
private class ResourceValuesCollection : ICollection
{
internal ResourceValuesCollection(ResourceDictionary owner)
{
_owner = owner;
}
#region ICollection
int ICollection.Count
{
get
{
return _owner.Count;
}
}
bool ICollection.IsSynchronized
{
get
{
return false;
}
}
object ICollection.SyncRoot
{
get
{
return this;
}
}
void ICollection.CopyTo(Array array, int index)
{
foreach(object key in _owner.Keys)
{
array.SetValue(_owner[key], index++);
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return new ResourceValuesEnumerator(_owner);
}
#endregion
#region Data
private ResourceDictionary _owner;
#endregion
}
#endregion Enumeration
#region PrivateMethods
//
// This method
// 1. Seals all the freezables/styles/templates that belong to this App/Theme/Style/Template ResourceDictionary
//
private void SealValues()
{
Debug.Assert(IsThemeDictionary || _ownerApps != null || IsReadOnly, "This must be an App/Theme/Style/Template ResourceDictionary");
foreach (object value in _baseDictionary.Values)
{
SealValue(value);
}
}
//
// This method
// 1. Sets the InheritanceContext of the value to the dictionary's principal owner
// 2. Seals the freezable/style/template that is to be placed in an App/Theme/Style/Template ResourceDictionary
//
private void SealValue(object value)
{
if (_inheritanceContext != null)
{
AddInheritanceContext(value);
}
if (IsThemeDictionary || _ownerApps != null || IsReadOnly)
{
// If the value is a ISealable then seal it
StyleHelper.SealIfSealable(value);
}
}
// add inheritance context to a value
private void AddInheritanceContext(object value)
{
// The VisualBrush.Visual property is the "friendliest", i.e. the
// most likely to be accepted by the resource as FEs need to accept
// being rooted by a VisualBrush.
//
// NOTE: Freezable.Debug_VerifyContextIsValid() contains a special
// case to allow this with the VisualBrush.Visual property.
// Changes made here will require updates in Freezable.cs
if (_inheritanceContext.ProvideSelfAsInheritanceContext(value, VisualBrush.VisualProperty))
{
// if the assignment was successful, seal the value's InheritanceContext.
// This makes sure the resource always gets inheritance-related information
// from its point of definition, not from its point of use.
DependencyObject doValue = value as DependencyObject;
if (doValue != null)
{
doValue.IsInheritanceContextSealed = true;
}
}
}
// add inheritance context to all values that came from this dictionary
private void AddInheritanceContextToValues()
{
foreach (object value in _baseDictionary.Values)
{
AddInheritanceContext(value);
}
}
// remove inheritance context from a value, if it came from this dictionary
private void RemoveInheritanceContext(object value)
{
DependencyObject doValue = value as DependencyObject;
if (doValue != null && _inheritanceContext != null &&
doValue.IsInheritanceContextSealed &&
doValue.InheritanceContext == _inheritanceContext)
{
doValue.IsInheritanceContextSealed = false;
_inheritanceContext.RemoveSelfAsInheritanceContext(doValue, VisualBrush.VisualProperty);
}
}
// remove inheritance context from all values that came from this dictionary
private void RemoveInheritanceContextFromValues()
{
foreach (object value in _baseDictionary.Values)
{
RemoveInheritanceContext(value);
}
}
private BamlRecordReader Reader
{
get
{
if (_reader == null)
{
_reader = new BamlRecordReader(_bamlStream, _context);
_reader.RootElement = _rootElement;
_reader.ComponentConnector = _rootElement as IComponentConnector;
}
return _reader;
}
}
// Sets the HasImplicitStyles flag if the given key is of type Type.
private void UpdateHasImplicitStyles(object key)
{
// Update the HasImplicitStyles flag
if (!HasImplicitStyles && key is Type)
{
HasImplicitStyles = true;
}
}
private bool IsInitialized
{
get { return ReadPrivateFlag(PrivateFlags.IsInitialized); }
set { WritePrivateFlag(PrivateFlags.IsInitialized, value); }
}
private bool IsInitializePending
{
get { return ReadPrivateFlag(PrivateFlags.IsInitializePending); }
set { WritePrivateFlag(PrivateFlags.IsInitializePending, value); }
}
private bool IsThemeDictionary
{
get { return ReadPrivateFlag(PrivateFlags.IsThemeDictionary); }
set { WritePrivateFlag(PrivateFlags.IsThemeDictionary, value); }
// (03/12/08) Backing out the change for binary compat reasons.
// {
// if (IsThemeDictionary != value)
// {
// WritePrivateFlag(PrivateFlags.IsThemeDictionary, value);
//
// if (_mergedDictionaries != null)
// {
// for (int i=0; i<_mergedDictionaries.Count; i++)
// {
// _mergedDictionaries[i].IsThemeDictionary = value;
// }
// }
// }
// }
}
internal bool HasImplicitStyles
{
get { return ReadPrivateFlag(PrivateFlags.HasImplicitStyles); }
set { WritePrivateFlag(PrivateFlags.HasImplicitStyles, value); }
}
internal bool CanBeAccessedAcrossThreads
{
get { return ReadPrivateFlag(PrivateFlags.CanBeAccessedAcrossThreads); }
set { WritePrivateFlag(PrivateFlags.CanBeAccessedAcrossThreads, value); }
}
private void WritePrivateFlag(PrivateFlags bit, bool value)
{
if (value)
{
_flags |= bit;
}
else
{
_flags &= ~bit;
}
}
private bool ReadPrivateFlag(PrivateFlags bit)
{
return (_flags & bit) != 0;
}
#endregion PrivateMethods
#region PrivateDataStructures
private enum PrivateFlags : byte
{
IsInitialized = 0x01,
IsInitializePending = 0x02,
IsReadOnly = 0x04,
IsThemeDictionary = 0x08,
HasImplicitStyles = 0x10,
CanBeAccessedAcrossThreads = 0x20,
// Unused bit = 0x40,
// Unused bit = 0x80,
}
#endregion PrivateDataStructures
#region Data
private Hashtable _baseDictionary = null;
private List _ownerFEs = null;
private List _ownerFCEs = null;
private List _ownerApps = null;
private List _deferredResourceReferences = null;
private ObservableCollection _mergedDictionaries = null;
private Uri _source = null;
private Uri _baseUri = null;
private PrivateFlags _flags = 0;
// Buffer that contains deferable content. This may be null if a stream was passed
// instead of a buffer. If a buffer was passed, then a memory stream is made on the buffer
private byte[] _buffer;
// Persistent Stream that contains values.
private Stream _bamlStream;
// Start position in the stream where the first value record is located. All offsets for
// the keys are relative to this position.
private Int64 _startPosition;
// Size of the delay loaded content, which only includes the value section and not the keys.
private Int32 _contentSize;
// Parser context that is in effect when the Defer load block is encountered in the baml
// stream. This should include all information, types, properties, etc that is needed to
// parse ALL of the values in the defer load section.
private ParserContext _context;
// The root element at the time the deferred content information was given to the dictionary.
private object _rootElement;
// Baml reader associated with this chunk of defer loaded content.
private BamlRecordReader _reader;
// The number of keys that correspond to deferred content. When this reaches 0,
// the stream can be closed.
private int _numDefer;
// The object that becomes the InheritanceContext of all eligible
// values in the dictionary - typically the principal owner of the dictionary.
private DependencyObject _inheritanceContext;
XamlObjectIds _contextXamlObjectIds = new XamlObjectIds();
#endregion Data
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
/****************************************************************************\
*
* 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.Windows.Threading;
using System.Windows.Media;
using System.Windows.Markup;
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;
namespace System.Windows
{
///
/// Dictionary that holds Resources for Framework components.
///
[Localizability(LocalizationCategory.Ignore)]
public class ResourceDictionary : IDictionary, INameScope, ISupportInitialize, IUriContext
{
#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;
Stream s = WpfWebRequestHelper.GetResponseStream(request, out contentType);
// 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.
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
_buffer = loadedRD._buffer;
_bamlStream = loadedRD._bamlStream;
_startPosition = loadedRD._startPosition;
_contentSize = loadedRD._contentSize;
_context = loadedRD._context;
_rootElement = loadedRD._rootElement;
_reader = loadedRD._reader;
_numDefer = loadedRD._numDefer;
// 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 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);
}
}
}
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;
IBamlDictionaryKey keyRecord = value as IBamlDictionaryKey;
if (keyRecord != null)
{
Debug.Assert(_numDefer > 0, "The stream was closed before all deferred content was loaded.");
Int32 valuePosition = keyRecord.ValuePosition;
BamlRecordReader previousRecordReader = _context.BamlReader;
BamlRecordReader reader = Reader;
_context.BamlReader = reader;
try
{
// Remember the starting stream position, so that it can be repositioned
// after the read to handle recursive operations on the same stream.
Int64 startPosition = _bamlStream.Position;
_bamlStream.Position = _startPosition + valuePosition;
BamlElementStartRecord bamlElementStartRecord = (BamlElementStartRecord)reader.GetNextRecord();
valueType = reader.MapTable.GetTypeFromId(bamlElementStartRecord.TypeId);
_bamlStream.Position = startPosition;
}
finally
{
// Restore the previousRecordReader into the context
_context.BamlReader = previousRecordReader;
}
}
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);
//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
///
internal bool ContainsBamlObjectFactory(object key)
{
return GetBamlObjectFactory(key) != null;
}
///
/// Retrieves an IBamlDictionaryKey from the IDictionary using the specified key.
/// If the Key is not contained in this ResourceDictionary, it will check in the MergedDictionaries too
///
internal IBamlDictionaryKey GetBamlObjectFactory(object key)
{
if (_baseDictionary.Contains(key))
{
return _baseDictionary[key] as IBamlDictionaryKey;
}
//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)
{
IBamlDictionaryKey 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(IBamlDictionaryKey keyRecord, object value)
{
if (keyRecord.SharedSet)
{
return keyRecord.Shared;
}
else
{
return true;
}
}
private bool RealizeDeferContent(ref object value)
{
bool canCache;
return RealizeDeferContent(null, ref value, out canCache);
}
private bool RealizeDeferContent(object key, ref object value, out bool canCache)
{
IBamlDictionaryKey keyRecord = value as IBamlDictionaryKey;
if (keyRecord != null)
{
Debug.Assert(_numDefer > 0, "The stream was closed before all deferred content was loaded.");
Int32 valuePosition = keyRecord.ValuePosition;
// Store the StaticResources list for the current key record into the parserContext so that
// StaticResourceId records within the current value can use it for lookup.
_context.StaticResourcesStack.Add(keyRecord.StaticResourceValues);
try
{
if( TraceResourceDictionary.IsEnabled )
{
TraceResourceDictionary.Trace(
TraceEventType.Start,
TraceResourceDictionary.RealizeDeferContent,
this,
key,
value );
}
value = CreateObject(valuePosition, key);
}
finally
{
if( TraceResourceDictionary.IsEnabled )
{
TraceResourceDictionary.Trace(
TraceEventType.Stop,
TraceResourceDictionary.RealizeDeferContent,
this,
key,
value );
}
_context.StaticResourcesStack.RemoveAt(_context.StaticResourcesStack.Count-1);
}
if (key != null)
{
canCache = CanCache(keyRecord, value);
if (canCache)
{
// Seal styles and templates within App and Theme dictionary
SealValue(value);
_numDefer--;
_baseDictionary[key] = value;
if (_numDefer == 0)
{
_reader = null;
_context = null;
// Only close the stream if it was a Memory Stream
// built over our buffer.
if(null != _buffer)
{
_bamlStream.Close();
_bamlStream = null;
_buffer = null;
}
}
}
}
else
{
canCache = true;
}
return true; /* Defer content */
}
canCache = true;
return false; /* Not defer content */
}
///
/// Add a byte array that contains deferable content
///
internal void SetDeferableContent(
byte[] buffer,
ParserContext context,
object rootElement,
ArrayList keyCollection,
List staticResourceValuesList)
{
if (buffer == null)
{
throw new ArgumentNullException("buffer");
}
if (context == null)
{
throw new ArgumentNullException("context");
}
if (keyCollection == null)
{
throw new ArgumentNullException("keyCollection");
}
// If we already have the Source set then we can ignore
// this deferable content section
if (_source == null)
{
if (_context == null)
{
_buffer = buffer;
_bamlStream = new MemoryStream(buffer);
_startPosition = 0;
_contentSize = buffer.Length;
_context = context.Clone();
_rootElement = rootElement;
SetKeys(keyCollection, staticResourceValuesList, context);
}
else
{
throw new InvalidOperationException(SR.Get(SRID.ResourceDictionaryDuplicateDeferredContent));
}
}
else if (keyCollection.Count > 0)
{
throw new InvalidOperationException(SR.Get(SRID.ResourceDictionaryDeferredContentFailure));
}
}
///
/// Add a stream that contains deferable content
///
internal void SetDeferableContent(
Stream bamlStream,
Int64 startPosition,
Int32 contentSize,
ParserContext context,
object rootElement,
ArrayList keyCollection,
List staticResourceValuesList)
{
if (bamlStream == null)
{
throw new ArgumentNullException("bamlStream");
}
if (!bamlStream.CanRead)
{
throw new ArgumentException(SR.Get(SRID.InputStreamMustBeReadable,"bamlStream"));
}
if (context == null)
{
throw new ArgumentNullException("context");
}
if (keyCollection == null)
{
throw new ArgumentNullException("keyCollection");
}
if (startPosition < 0)
{
throw new ArgumentOutOfRangeException("startPosition");
}
if (contentSize < 0)
{
throw new ArgumentOutOfRangeException("contentSize");
}
// If we already have the Source set then we can ignore
// this deferable content section
if (_source == null)
{
if (_context == null)
{
_bamlStream = bamlStream;
_startPosition = startPosition;
_contentSize = contentSize;
_context = context.Clone();
// We need to clear the Journal bit, because we know we won't be able to honor it correctly.
// Journaling journals the properties in the logical tree, so doesn't journal properties in the
// Template/Resources. This shouldn't be hard-coded here, but is an internal solution for V1.
_context.SkipJournaledProperties = false;
_rootElement = rootElement;
SetKeys(keyCollection, staticResourceValuesList, context);
}
else
{
throw new InvalidOperationException(SR.Get(SRID.ResourceDictionaryDuplicateDeferredContent));
}
}
else if (keyCollection.Count > 0)
{
throw new InvalidOperationException(SR.Get(SRID.ResourceDictionaryDeferredContentFailure));
}
}
private void SetKeys(
ArrayList keyCollection,
List staticResourceValuesList,
ParserContext context)
{
_numDefer = keyCollection.Count;
// Use the array Count property to avoid range checking inside the loop
for (int i = 0; i < keyCollection.Count; i++)
{
IBamlDictionaryKey keyRecord = keyCollection[i] as IBamlDictionaryKey;
if (keyRecord != null)
{
SetStaticResources(staticResourceValuesList[i], context);
keyRecord.StaticResourceValues = staticResourceValuesList[i];
// Update the HasImplicitStyles flag
UpdateHasImplicitStyles(keyRecord.KeyObject);
_baseDictionary.Add(keyRecord.KeyObject, keyRecord);
if( TraceResourceDictionary.IsEnabled )
{
TraceResourceDictionary.TraceActivityItem(
TraceResourceDictionary.SetKey,
this,
keyRecord.KeyObject );
}
}
else
{
throw new ArgumentException(SR.Get(SRID.KeyCollectionHasInvalidKey));
}
}
// Notify owners of the HasImplicitStyles flag value
// but there is not need to fire an invalidation.
NotifyOwners(new ResourcesChangeInfo(null, this));
// Get the XamlObjectIds (Name, Uid, and Key) that are currently in our context.
// When we realize tese resources, we'll pass in that information, so that they
// can generate good exception messages.
Type objectType;
XamlParseException.GetObjectContext(context.BamlReader,
null, // currentObjectIdentifiers
_contextXamlObjectIds,
out objectType );
}
private void SetStaticResources(object[] staticResourceValues, ParserContext context)
{
if (staticResourceValues != null && staticResourceValues.Length > 0)
{
bool inDeferredSection = context.InDeferredSection;
for (int i=0; i(1);
}
else if (_ownerFEs.Contains(fe) && ContainsCycle(this))
{
throw new InvalidOperationException(SR.Get(SRID.ResourceDictionaryInvalidMergedDictionary));
}
// Propagate the HasImplicitStyles flag to the new owner
if (HasImplicitStyles)
{
fe.ShouldLookupImplicitStyles = true;
}
_ownerFEs.Add(fe);
}
else
{
FrameworkContentElement fce = owner as FrameworkContentElement;
if (fce != null)
{
if (_ownerFCEs == null)
{
_ownerFCEs = new List(1);
}
else if (_ownerFCEs.Contains(fce) && ContainsCycle(this))
{
throw new InvalidOperationException(SR.Get(SRID.ResourceDictionaryInvalidMergedDictionary));
}
// Propagate the HasImplicitStyles flag to the new owner
if (HasImplicitStyles)
{
fce.ShouldLookupImplicitStyles = true;
}
_ownerFCEs.Add(fce);
}
else
{
Application app = owner as Application;
if (app != null)
{
if (_ownerApps == null)
{
_ownerApps = new List(1);
}
else if (_ownerApps.Contains(app) && ContainsCycle(this))
{
throw new InvalidOperationException(SR.Get(SRID.ResourceDictionaryInvalidMergedDictionary));
}
// Propagate the HasImplicitStyles flag to the new owner
if (HasImplicitStyles)
{
app.HasImplicitStylesInResources = true;
}
_ownerApps.Add(app);
// An Application ResourceDictionary can be accessed across threads
CanBeAccessedAcrossThreads = true;
// Seal all the styles and templates in this app dictionary
SealValues();
}
}
}
AddOwnerToAllMergedDictionaries(owner);
// This dictionary will be marked initialized if no one has called BeginInit on it.
// This is done now because having an owner is like a parenting operation for the dictionary.
TryInitialize();
}
// Remove an owner for this dictionary
internal void RemoveOwner(DispatcherObject owner)
{
FrameworkElement fe = owner as FrameworkElement;
if (fe != null)
{
if (_ownerFEs != null)
{
_ownerFEs.Remove(fe);
if (_ownerFEs.Count == 0)
{
_ownerFEs = null;
}
}
}
else
{
FrameworkContentElement fce = owner as FrameworkContentElement;
if (fce != null)
{
if (_ownerFCEs != null)
{
_ownerFCEs.Remove(fce);
if (_ownerFCEs.Count == 0)
{
_ownerFCEs = null;
}
}
}
else
{
Application app = owner as Application;
if (app != null)
{
if (_ownerApps != null)
{
_ownerApps.Remove(app);
if (_ownerApps.Count == 0)
{
_ownerApps = null;
}
}
}
}
}
RemoveOwnerFromAllMergedDictionaries(owner);
}
// Check if the given is an owner to this dictionary
internal bool ContainsOwner(DispatcherObject owner)
{
FrameworkElement fe = owner as FrameworkElement;
if (fe != null)
{
return (_ownerFEs != null && _ownerFEs.Contains(fe));
}
else
{
FrameworkContentElement fce = owner as FrameworkContentElement;
if (fce != null)
{
return (_ownerFCEs != null && _ownerFCEs.Contains(fce));
}
else
{
Application app = owner as Application;
if (app != null)
{
return (_ownerApps != null && _ownerApps.Contains(app));
}
}
}
return false;
}
// Helper method that tries to set IsInitialized to true if BeginInit hasn't been called before this.
// This method is called on AddOwner
private void TryInitialize()
{
if (!IsInitializePending &&
!IsInitialized)
{
IsInitialized = true;
}
}
// Call FrameworkElement.InvalidateTree with the right data
private void NotifyOwners(ResourcesChangeInfo info)
{
bool shouldInvalidate = IsInitialized;
bool hasImplicitStyles = info.IsResourceAddOperation && HasImplicitStyles;
if (shouldInvalidate || hasImplicitStyles)
{
// Invalidate all FE owners
if (_ownerFEs != null)
{
for (int i=0; i<_ownerFEs.Count; i++)
{
// Set the HasImplicitStyles flag on the owner
if (hasImplicitStyles)
_ownerFEs[i].ShouldLookupImplicitStyles = true;
// If this dictionary has been initialized fire an invalidation
// to let the tree know of this change.
if (shouldInvalidate)
TreeWalkHelper.InvalidateOnResourcesChange(_ownerFEs[i], null, info);
}
}
// Invalidate all FCE owners
if (_ownerFCEs != null)
{
for (int i=0; i<_ownerFCEs.Count; i++)
{
// Set the HasImplicitStyles flag on the owner
if (hasImplicitStyles)
_ownerFCEs[i].ShouldLookupImplicitStyles = true;
// If this dictionary has been initialized fire an invalidation
// to let the tree know of this change.
if (shouldInvalidate)
TreeWalkHelper.InvalidateOnResourcesChange(null, _ownerFCEs[i], info);
}
}
// Invalidate all App owners
if (_ownerApps != null)
{
for (int i=0; i<_ownerApps.Count; i++)
{
// Set the HasImplicitStyles flag on the owner
if (hasImplicitStyles)
_ownerApps[i].HasImplicitStylesInResources = true;
// If this dictionary has been initialized fire an invalidation
// to let the tree know of this change.
if (shouldInvalidate)
_ownerApps[i].InvalidateResourceReferences(info);
}
}
}
}
///
/// Fetches the resource corresponding to the given key from this dictionary.
/// Returns a DeferredResourceReference if the object has not been inflated yet.
///
internal object FetchResource(
object resourceKey,
bool allowDeferredResourceReference,
bool mustReturnDeferredResourceReference,
out bool canCache)
{
Debug.Assert(resourceKey != null, "ResourceKey cannot be null");
if (allowDeferredResourceReference)
{
if (ContainsBamlObjectFactory(resourceKey) ||
(mustReturnDeferredResourceReference && Contains(resourceKey)))
{
canCache = false;
DeferredResourceReference deferredResourceReference;
if (!IsThemeDictionary)
{
if (_ownerApps != null)
{
deferredResourceReference = new DeferredAppResourceReference(this, resourceKey);
}
else
{
deferredResourceReference = new DeferredResourceReference(this, resourceKey);
}
// Cache the deferredResourceReference so that it can be validated
// in case of a dictionary change prior to its inflation
if (_deferredResourceReferences == null)
{
_deferredResourceReferences = new List();
}
_deferredResourceReferences.Add(deferredResourceReference);
}
else
{
deferredResourceReference = new DeferredThemeResourceReference(this, resourceKey);
}
return deferredResourceReference;
}
}
return GetValue(resourceKey, out canCache);
}
///
/// Validate the deferredResourceReference with the given key. Key could be null meaning
/// some catastrophic operation occurred so simply validate all DeferredResourceReferences
///
private void ValidateDeferredResourceReferences(object resourceKey)
{
if (_deferredResourceReferences != null)
{
for (int i=0; i<_deferredResourceReferences.Count; i++)
{
DeferredResourceReference deferredResourceReference = _deferredResourceReferences[i];
if (resourceKey == null || Object.Equals(resourceKey, deferredResourceReference.Key))
{
#if DEBUG
int oldCount = _deferredResourceReferences.Count;
#endif
deferredResourceReference.GetValue(BaseValueSourceInternal.Unknown);
#if DEBUG
int newCount = _deferredResourceReferences.Count;
Debug.Assert(newCount == oldCount-1,
"The deferredResourceReference that was just validated should have also been removed from the list as part of the GetValue call.");
#endif
// Adjust i to accomodate the removal of an entry
i--;
}
}
}
}
///
/// Called when the MergedDictionaries collection changes
///
///
///
private void OnMergedDictionariesChanged(object sender, NotifyCollectionChangedEventArgs e)
{
List oldDictionaries = null;
List newDictionaries = null;
ResourceDictionary mergedDictionary;
ResourcesChangeInfo info;
if (e.Action != NotifyCollectionChangedAction.Reset)
{
Invariant.Assert(
(e.NewItems != null && e.NewItems.Count > 0) ||
(e.OldItems != null && e.OldItems.Count > 0),
"The NotifyCollectionChanged event fired when no dictionaries were added or removed");
// If one or more resource dictionaries were removed we
// need to remove the owners they were given by their
// parent ResourceDictionary.
if (e.Action == NotifyCollectionChangedAction.Remove
|| e.Action == NotifyCollectionChangedAction.Replace)
{
oldDictionaries = new List(e.OldItems.Count);
for (int i = 0; i < e.OldItems.Count; i++)
{
mergedDictionary = (ResourceDictionary)e.OldItems[i];
oldDictionaries.Add(mergedDictionary);
RemoveParentOwners(mergedDictionary);
}
}
// If one or more resource dictionaries were added to the merged
// dictionaries collection we need to send down the parent
// ResourceDictionary's owners.
if (e.Action == NotifyCollectionChangedAction.Add
|| e.Action == NotifyCollectionChangedAction.Replace)
{
newDictionaries = new List(e.NewItems.Count);
for (int i = 0; i < e.NewItems.Count; i++)
{
mergedDictionary = (ResourceDictionary)e.NewItems[i];
newDictionaries.Add(mergedDictionary);
// If the merged dictionary HasImplicitStyle mark the outer dictionary the same.
if (!HasImplicitStyles && mergedDictionary.HasImplicitStyles)
{
HasImplicitStyles = true;
}
// (03/12/08) Backing out the change for binary compat reasons.
// If the parent dictionary is a theme dictionary mark the merged dictionary the same.
// if (IsThemeDictionary)
// {
// mergedDictionary.IsThemeDictionary = true;
// }
PropagateParentOwners(mergedDictionary);
}
}
info = new ResourcesChangeInfo(oldDictionaries, newDictionaries, false, false, null);
}
else
{
// Case when MergedDictionary collection is cleared
info = ResourcesChangeInfo.CatastrophicDictionaryChangeInfo;
}
// Notify the owners of the change and fire
// invalidation if already initialized
NotifyOwners(info);
}
///
/// Adds the given owner to all merged dictionaries of this ResourceDictionary
///
///
private void AddOwnerToAllMergedDictionaries(DispatcherObject owner)
{
if (_mergedDictionaries != null)
{
for (int i = 0; i < _mergedDictionaries.Count; i++)
{
_mergedDictionaries[i].AddOwner(owner);
}
}
}
///
///
///
///
private void RemoveOwnerFromAllMergedDictionaries(DispatcherObject owner)
{
if (_mergedDictionaries != null)
{
for (int i = 0; i < _mergedDictionaries.Count; i++)
{
_mergedDictionaries[i].RemoveOwner(owner);
}
}
}
///
/// This sends down the owners of this ResourceDictionary into the given
/// merged dictionary. We do this because whenever a merged dictionary
/// changes it should invalidate all owners of its parent ResourceDictionary.
///
/// Note that AddOwners throw if the merged dictionary already has one of the
/// parent's owners. This implies that either we're putting a dictionary
/// into its own MergedDictionaries collection or we're putting the same
/// dictionary into the collection twice, neither of which are legal.
///
///
private void PropagateParentOwners(ResourceDictionary mergedDictionary)
{
if (_ownerFEs != null)
{
Invariant.Assert(_ownerFEs.Count > 0);
if (mergedDictionary._ownerFEs == null)
{
mergedDictionary._ownerFEs = new List(_ownerFEs.Count);
}
for (int i = 0; i < _ownerFEs.Count; i++)
{
mergedDictionary.AddOwner(_ownerFEs[i]);
}
}
if (_ownerFCEs != null)
{
Invariant.Assert(_ownerFCEs.Count > 0);
if (mergedDictionary._ownerFCEs == null)
{
mergedDictionary._ownerFCEs = new List(_ownerFCEs.Count);
}
for (int i = 0; i < _ownerFCEs.Count; i++)
{
mergedDictionary.AddOwner(_ownerFCEs[i]);
}
}
if (_ownerApps != null)
{
Invariant.Assert(_ownerApps.Count > 0);
if (mergedDictionary._ownerApps == null)
{
mergedDictionary._ownerApps = new List(_ownerApps.Count);
}
for (int i = 0; i < _ownerApps.Count; i++)
{
mergedDictionary.AddOwner(_ownerApps[i]);
}
}
}
///
/// Removes the owners of this ResourceDictionary from the given
/// merged dictionary. The merged dictionary will be left with
/// whatever owners it had before being merged.
///
///
internal void RemoveParentOwners(ResourceDictionary mergedDictionary)
{
if (_ownerFEs != null)
{
Invariant.Assert(_ownerFEs.Count > 0);
for (int i = 0; i < _ownerFEs.Count; i++)
{
mergedDictionary.RemoveOwner(_ownerFEs[i]);
}
}
if (_ownerFCEs != null)
{
Invariant.Assert(_ownerFCEs.Count > 0);
for (int i = 0; i < _ownerFCEs.Count; i++)
{
mergedDictionary.RemoveOwner(_ownerFCEs[i]);
}
}
if (_ownerApps != null)
{
Invariant.Assert(_ownerApps.Count > 0);
for (int i = 0; i < _ownerApps.Count; i++)
{
mergedDictionary.RemoveOwner(_ownerApps[i]);
}
}
}
private bool ContainsCycle(ResourceDictionary origin)
{
for (int i=0; i DeferredResourceReferences
{
get { return _deferredResourceReferences; }
}
#endregion Properties
#region Enumeration
///
/// Iterates the dictionary's entries, handling deferred content.
///
private class ResourceDictionaryEnumerator : IDictionaryEnumerator
{
internal ResourceDictionaryEnumerator(ResourceDictionary owner)
{
_owner = owner;
_keysEnumerator = _owner.Keys.GetEnumerator();
}
#region IEnumerator
object IEnumerator.Current
{
get
{
return ((IDictionaryEnumerator)this).Entry;
}
}
bool IEnumerator.MoveNext()
{
return _keysEnumerator.MoveNext();
}
void IEnumerator.Reset()
{
_keysEnumerator.Reset();
}
#endregion
#region IDictionaryEnumerator
DictionaryEntry IDictionaryEnumerator.Entry
{
get
{
object key = _keysEnumerator.Current;
object value = _owner[key];
return new DictionaryEntry(key, value);
}
}
object IDictionaryEnumerator.Key
{
get
{
return _keysEnumerator.Current;
}
}
object IDictionaryEnumerator.Value
{
get
{
return _owner[_keysEnumerator.Current];
}
}
#endregion
#region Data
private ResourceDictionary _owner;
private IEnumerator _keysEnumerator;
#endregion
}
///
/// Iterator for the dictionary's Values collection, handling deferred content.
///
private class ResourceValuesEnumerator : IEnumerator
{
internal ResourceValuesEnumerator(ResourceDictionary owner)
{
_owner = owner;
_keysEnumerator = _owner.Keys.GetEnumerator();
}
#region IEnumerator
object IEnumerator.Current
{
get
{
return _owner[_keysEnumerator.Current];
}
}
bool IEnumerator.MoveNext()
{
return _keysEnumerator.MoveNext();
}
void IEnumerator.Reset()
{
_keysEnumerator.Reset();
}
#endregion
#region Data
private ResourceDictionary _owner;
private IEnumerator _keysEnumerator;
#endregion
}
///
/// Represents the dictionary's Values collection, handling deferred content.
///
private class ResourceValuesCollection : ICollection
{
internal ResourceValuesCollection(ResourceDictionary owner)
{
_owner = owner;
}
#region ICollection
int ICollection.Count
{
get
{
return _owner.Count;
}
}
bool ICollection.IsSynchronized
{
get
{
return false;
}
}
object ICollection.SyncRoot
{
get
{
return this;
}
}
void ICollection.CopyTo(Array array, int index)
{
foreach(object key in _owner.Keys)
{
array.SetValue(_owner[key], index++);
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return new ResourceValuesEnumerator(_owner);
}
#endregion
#region Data
private ResourceDictionary _owner;
#endregion
}
#endregion Enumeration
#region PrivateMethods
//
// This method
// 1. Seals all the freezables/styles/templates that belong to this App/Theme/Style/Template ResourceDictionary
//
private void SealValues()
{
Debug.Assert(IsThemeDictionary || _ownerApps != null || IsReadOnly, "This must be an App/Theme/Style/Template ResourceDictionary");
foreach (object value in _baseDictionary.Values)
{
SealValue(value);
}
}
//
// This method
// 1. Sets the InheritanceContext of the value to the dictionary's principal owner
// 2. Seals the freezable/style/template that is to be placed in an App/Theme/Style/Template ResourceDictionary
//
private void SealValue(object value)
{
if (_inheritanceContext != null)
{
AddInheritanceContext(value);
}
if (IsThemeDictionary || _ownerApps != null || IsReadOnly)
{
// If the value is a ISealable then seal it
StyleHelper.SealIfSealable(value);
}
}
// add inheritance context to a value
private void AddInheritanceContext(object value)
{
// The VisualBrush.Visual property is the "friendliest", i.e. the
// most likely to be accepted by the resource as FEs need to accept
// being rooted by a VisualBrush.
//
// NOTE: Freezable.Debug_VerifyContextIsValid() contains a special
// case to allow this with the VisualBrush.Visual property.
// Changes made here will require updates in Freezable.cs
if (_inheritanceContext.ProvideSelfAsInheritanceContext(value, VisualBrush.VisualProperty))
{
// if the assignment was successful, seal the value's InheritanceContext.
// This makes sure the resource always gets inheritance-related information
// from its point of definition, not from its point of use.
DependencyObject doValue = value as DependencyObject;
if (doValue != null)
{
doValue.IsInheritanceContextSealed = true;
}
}
}
// add inheritance context to all values that came from this dictionary
private void AddInheritanceContextToValues()
{
foreach (object value in _baseDictionary.Values)
{
AddInheritanceContext(value);
}
}
// remove inheritance context from a value, if it came from this dictionary
private void RemoveInheritanceContext(object value)
{
DependencyObject doValue = value as DependencyObject;
if (doValue != null && _inheritanceContext != null &&
doValue.IsInheritanceContextSealed &&
doValue.InheritanceContext == _inheritanceContext)
{
doValue.IsInheritanceContextSealed = false;
_inheritanceContext.RemoveSelfAsInheritanceContext(doValue, VisualBrush.VisualProperty);
}
}
// remove inheritance context from all values that came from this dictionary
private void RemoveInheritanceContextFromValues()
{
foreach (object value in _baseDictionary.Values)
{
RemoveInheritanceContext(value);
}
}
private BamlRecordReader Reader
{
get
{
if (_reader == null)
{
_reader = new BamlRecordReader(_bamlStream, _context);
_reader.RootElement = _rootElement;
_reader.ComponentConnector = _rootElement as IComponentConnector;
}
return _reader;
}
}
// Sets the HasImplicitStyles flag if the given key is of type Type.
private void UpdateHasImplicitStyles(object key)
{
// Update the HasImplicitStyles flag
if (!HasImplicitStyles && key is Type)
{
HasImplicitStyles = true;
}
}
private bool IsInitialized
{
get { return ReadPrivateFlag(PrivateFlags.IsInitialized); }
set { WritePrivateFlag(PrivateFlags.IsInitialized, value); }
}
private bool IsInitializePending
{
get { return ReadPrivateFlag(PrivateFlags.IsInitializePending); }
set { WritePrivateFlag(PrivateFlags.IsInitializePending, value); }
}
private bool IsThemeDictionary
{
get { return ReadPrivateFlag(PrivateFlags.IsThemeDictionary); }
set { WritePrivateFlag(PrivateFlags.IsThemeDictionary, value); }
// (03/12/08) Backing out the change for binary compat reasons.
// {
// if (IsThemeDictionary != value)
// {
// WritePrivateFlag(PrivateFlags.IsThemeDictionary, value);
//
// if (_mergedDictionaries != null)
// {
// for (int i=0; i<_mergedDictionaries.Count; i++)
// {
// _mergedDictionaries[i].IsThemeDictionary = value;
// }
// }
// }
// }
}
internal bool HasImplicitStyles
{
get { return ReadPrivateFlag(PrivateFlags.HasImplicitStyles); }
set { WritePrivateFlag(PrivateFlags.HasImplicitStyles, value); }
}
internal bool CanBeAccessedAcrossThreads
{
get { return ReadPrivateFlag(PrivateFlags.CanBeAccessedAcrossThreads); }
set { WritePrivateFlag(PrivateFlags.CanBeAccessedAcrossThreads, value); }
}
private void WritePrivateFlag(PrivateFlags bit, bool value)
{
if (value)
{
_flags |= bit;
}
else
{
_flags &= ~bit;
}
}
private bool ReadPrivateFlag(PrivateFlags bit)
{
return (_flags & bit) != 0;
}
#endregion PrivateMethods
#region PrivateDataStructures
private enum PrivateFlags : byte
{
IsInitialized = 0x01,
IsInitializePending = 0x02,
IsReadOnly = 0x04,
IsThemeDictionary = 0x08,
HasImplicitStyles = 0x10,
CanBeAccessedAcrossThreads = 0x20,
// Unused bit = 0x40,
// Unused bit = 0x80,
}
#endregion PrivateDataStructures
#region Data
private Hashtable _baseDictionary = null;
private List _ownerFEs = null;
private List _ownerFCEs = null;
private List _ownerApps = null;
private List _deferredResourceReferences = null;
private ObservableCollection _mergedDictionaries = null;
private Uri _source = null;
private Uri _baseUri = null;
private PrivateFlags _flags = 0;
// Buffer that contains deferable content. This may be null if a stream was passed
// instead of a buffer. If a buffer was passed, then a memory stream is made on the buffer
private byte[] _buffer;
// Persistent Stream that contains values.
private Stream _bamlStream;
// Start position in the stream where the first value record is located. All offsets for
// the keys are relative to this position.
private Int64 _startPosition;
// Size of the delay loaded content, which only includes the value section and not the keys.
private Int32 _contentSize;
// Parser context that is in effect when the Defer load block is encountered in the baml
// stream. This should include all information, types, properties, etc that is needed to
// parse ALL of the values in the defer load section.
private ParserContext _context;
// The root element at the time the deferred content information was given to the dictionary.
private object _rootElement;
// Baml reader associated with this chunk of defer loaded content.
private BamlRecordReader _reader;
// The number of keys that correspond to deferred content. When this reaches 0,
// the stream can be closed.
private int _numDefer;
// The object that becomes the InheritanceContext of all eligible
// values in the dictionary - typically the principal owner of the dictionary.
private DependencyObject _inheritanceContext;
XamlObjectIds _contextXamlObjectIds = new XamlObjectIds();
#endregion Data
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.