Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / wpf / src / Framework / System / Windows / Markup / TemplateBamlRecordReader.cs / 1 / TemplateBamlRecordReader.cs
/****************************************************************************\
*
* File: TemplateBamlRecordReader.cs
*
* Purpose: Main class to handle reading a Template Baml records from a stream
*
* History:
* 11/22/04: varsham Created
* 06/15/05: peterost Refactor for new template architecture
*
* Copyright (C) 2004 by Microsoft Corporation. All rights reserved.
*
\***************************************************************************/
using System;
using System.Xml;
using System.IO;
using System.Windows;
using System.Windows.Navigation;
using System.Text;
using System.Collections;
using System.ComponentModel;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media.Animation;
using System.Diagnostics;
using System.Reflection;
using System.Windows.Threading;
using System.Windows.Data;
using System.Globalization;
using MS.Utility;
using MS.Internal;
namespace System.Windows.Markup
{
///
/// Reads BAML from a Stream that pertains to Template elements and properties.
/// This is an internal class
///
internal class TemplateBamlRecordReader : BamlRecordReader
{
#region Constructor
///
/// TemplateBamlRecordReader constructor
///
/// The input BAML stream, if loading from a file
/// StartRecord for List of records, if loading from a dictionary
/// Index Record into list of records to start parsing at
/// The parser context
/// Reader stack from upper level parser to get
/// context for things such as dictionaries and parents.
/// list of root objects for the parse
internal TemplateBamlRecordReader(
Stream bamlStream,
BamlRecord bamlStartRecord,
BamlRecord bamlIndexRecord,
ParserContext parserContext,
ParserStack bamlReaderStack,
ArrayList rootList)
{
Debug.Assert(null != bamlStream || bamlStartRecord != null);
Debug.Assert(null != parserContext && null != parserContext.XamlTypeMapper);
// Link this baml record reader to the "parent" reader, so that
// we can see its context stack.
SetPreviousBamlRecordReader( parserContext.BamlReader );
ParserContext = parserContext;
RootList = rootList;
PreParsedRecordsStart = bamlStartRecord;
PreParsedCurrentRecord = bamlIndexRecord;
if (bamlStream != null)
{
BamlStream = bamlStream;
}
RootElement = ParserContext.RootElement;
ComponentConnector = RootElement as IComponentConnector;
ContextStack = bamlReaderStack;
_elementDepth = 0;
// We're starting out in the template element itself, e.g. tag?
if( _inTemplateElement )
{
// Yes
moreData = TryReadTemplateElementRecord( bamlRecord );
// If we're still in the template element itself, ReadRootRecord above is all the processing
// we need.
if (_inTemplateElement)
{
return moreData;
}
// Otherwise, we're on the first child of the root element (the first child of the tag)
else
{
// Put the template's TargetType into the parser context for use by Setter's etc (see comment
// on ParserContext.TargetType).
ParserContext.TargetType = _frameworkTemplate.TargetTypeInternal;
// Give the template its own copy of the parser context. It needs a copy, because it's
// going to use it later on every time it's applied.
_frameworkTemplate.CopyParserContext(ParserContext);
}
}
// Check to see if we should update _inContent.
if (_elementDepth == 1 && CurrentContext.ContextType == ReaderFlags.ClrObject)
{
// We're directly under the tag ...
if (bamlRecord.RecordType == BamlRecordType.ElementStart)
{
// ... and we're at a start element, so we must be in template content.
_inContent = true;
_optimizedTemplateContent.BeginReadingContent(ParserContext);
}
else if( _inContent )
{
// ... but we're not on an element start, and we were in content,
// so we must not be any more.
_inContent = false;
_optimizedTemplateContent.EndReadingContent();
}
}
// Are we in the template content?
if( _inContent)
{
// If this a nested template then it needs a separate set of BamlRecords
// that can be chained together to represent the template content.
if (_inNestedTemplate)
{
bamlRecord = BamlRecordManager.CloneRecord(bamlRecord);
_currentBamlRecord = bamlRecord;
}
// Yes, we're in template content. We do some minimal amount of
// processing here, but mostly pass to AddContentRecord.
// If this is a map table record (such as an attribute info), process
// it normally.
if( BamlRecordHelper.IsMapTableRecordType( bamlRecord.RecordType ))
{
base.ReadRecord(bamlRecord);
}
// Simiarly, let base process deferrable content and connection IDs
else if( bamlRecord.RecordType == BamlRecordType.DeferableContentStart
||
bamlRecord.RecordType == BamlRecordType.ConnectionId )
{
base.ReadRecord(bamlRecord);
}
// If this is an element start/end, we have to call the override directly.
else if( bamlRecord.RecordType == BamlRecordType.ElementStart )
{
// This is to avoid the checks for injected tags done by the base reader
ReadElementStartRecord((BamlElementStartRecord)bamlRecord);
}
else if( bamlRecord.RecordType == BamlRecordType.ElementEnd )
{
// This is to avoid the checks for injected tags done by the base reader
ReadElementEndRecord(false);
}
// Otherwise, hand off the record to the OptimizedTemplateContent.
else
{
AddContentRecord( bamlRecord );
}
}
else
{
// We're not in the content any more, process the triggers.
moreData = ReadNonContentRecord( bamlRecord );
}
_currentBamlRecord = null;
return _elementDepth > 0 && moreData;
}
//+-----------------------------------------------------------------------
//
// ReadRootRecord
//
// Read a baml record that's part of the template tag itself, i.e.
// part of the element. Note that this routine
// will keep the _inRootElement flag up-to-date, and will *not* process
// the last record, where it discovers that it's out of the root element.
//
//+-----------------------------------------------------------------------
internal bool TryReadTemplateElementRecord( BamlRecord bamlRecord )
{
bool moreData = true;
Debug.Assert( _inTemplateElement );
if( bamlRecord.RecordType == BamlRecordType.ElementStart )
{
// If this is an element start record, we're either on the
// tag itself, and not part of the template content (i.e. it's either the
// .Resources or the .Triggers).
//
//+------------------------------------------------------------------------------
internal bool ReadNonContentRecord( BamlRecord bamlRecord )
{
bool skipRead = false;
// Look for the "Value" and "Property" attributes in triggers, setters, and conditions.
// We won't really process these until the end of the element.
if (bamlRecord.RecordType == BamlRecordType.Property
||
bamlRecord.RecordType == BamlRecordType.PropertyWithConverter)
{
BamlPropertyRecord bamlPropertyRecord = bamlRecord as BamlPropertyRecord;
short converterTypeId = 0;
BamlPropertyWithConverterRecord bamlPropertyWithConverterRecord = bamlRecord as BamlPropertyWithConverterRecord;
if( bamlPropertyWithConverterRecord != null )
{
converterTypeId = bamlPropertyWithConverterRecord.ConverterTypeId;
}
// Is this a "Value" property? If so, see if it's part of a trigger, setter, or
// condition, and if so save it away.
string name = MapTable.GetAttributeNameFromId(bamlPropertyRecord.AttributeId);
if (name == "Value")
{
if( CurrentContext.ObjectData is Trigger || CurrentContext.ObjectData is DataTrigger )
{
Debug.Assert( _triggerValueString == null );
_triggerValueString = bamlPropertyRecord.Value;
_triggerValueConverterTypeId = converterTypeId;
skipRead = true;
}
else if( CurrentContext.ObjectData is Setter )
{
Debug.Assert( _setterValueString == null );
_setterValueString = bamlPropertyRecord.Value;
_setterValueConverterTypeId = converterTypeId;
skipRead = true;
}
else if( CurrentContext.ObjectData is Condition )
{
Debug.Assert( _conditionValueString == null );
_conditionValueString = bamlPropertyRecord.Value;
_conditionValueConverterTypeId = converterTypeId;
skipRead = true;
}
else
{
// CurrentContext.ObjectData is none of the
// types above, we need skipRead to stay false
// so we will ask base BamlRecordReader to process the data.
}
}
else if (name == "Property")
{
if( CurrentContext.ObjectData is Trigger )
{
Debug.Assert( _triggerPropertyString == null );
_triggerPropertyString = bamlPropertyRecord.Value;
skipRead = true;
}
else if( CurrentContext.ObjectData is Setter )
{
Debug.Assert( _setterPropertyString == null );
_setterPropertyString = bamlPropertyRecord.Value;
skipRead = true;
}
else if( CurrentContext.ObjectData is Condition )
{
Debug.Assert( _conditionPropertyString == null );
_conditionPropertyString = bamlPropertyRecord.Value;
skipRead = true;
}
else
{
// CurrentContext.ObjectData is none of the
// types above, we need skipRead to stay false
// so we will ask base BamlRecordReader to process the data.
}
}
else if (name == "SourceName")
{
if( CurrentContext.ObjectData is Trigger )
{
Debug.Assert( _sourceNameString == null );
_sourceNameString = bamlPropertyRecord.Value;
skipRead = true;
}
else if( CurrentContext.ObjectData is Condition )
{
Debug.Assert( _sourceNameString == null );
_sourceNameString = bamlPropertyRecord.Value;
skipRead = true;
}
}
/*
else if( attrInfo.Name == "TargetName" )
{
if( CurrentContext.ObjectData is Setter )
{
Debug.Assert( _targetNameString == null );
_targetNameString = bamlPropertyRecord.Value;
skipRead = true;
}
}
*/
}
// If this is the end element of a Trigger, Setter, or Condition,
// finish processing of it (convert the Value/Property properties).
else if (bamlRecord.RecordType == BamlRecordType.ElementEnd)
{
if (CurrentContext.ObjectData != null)
{
CompleteSetterOrTriggerOrCondition(CurrentContext.ObjectData);
}
}
// If we didn't process this record internally above, pass it up to base.
if (!skipRead)
return base.ReadRecord(bamlRecord);
else
return true;
}
protected override void ReadPropertyWithExtensionRecord(BamlPropertyWithExtensionRecord bamlPropertyRecord)
{
string propertyName = MapTable.GetAttributeNameFromId(bamlPropertyRecord.AttributeId);
object value = GetExtensionValue(bamlPropertyRecord, propertyName);
bool handled = BaseReadOptimizedMarkupExtension(propertyName, value);
if (!handled)
{
base.ReadPropertyWithExtensionRecord(bamlPropertyRecord);
}
}
protected override void ReadPropertyWithStaticResourceIdRecord(
BamlPropertyWithStaticResourceIdRecord bamlPropertyWithStaticResourceIdRecord)
{
string propertyName = MapTable.GetAttributeNameFromId(bamlPropertyWithStaticResourceIdRecord.AttributeId);
// Find the StaticResourceValue for the given Id
object value = GetStaticResourceFromId(bamlPropertyWithStaticResourceIdRecord.StaticResourceId);
bool handled = BaseReadOptimizedMarkupExtension(propertyName, value);
if (!handled)
{
base.ReadPropertyWithStaticResourceIdRecord(bamlPropertyWithStaticResourceIdRecord);
}
}
private bool BaseReadOptimizedMarkupExtension(string propertyName, object value)
{
object o = CurrentContext.ObjectData;
if (propertyName == XamlStyleSerializer.SetterValueAttributeName)
{
Setter setter = o as Setter;
if (setter != null)
{
Debug.Assert(setter.Value == DependencyProperty.UnsetValue);
Debug.Assert(_setterValueObject == DependencyProperty.UnsetValue);
_setterValueObject = value;
return true;
}
Trigger trigger = o as Trigger;
if (trigger != null)
{
Debug.Assert(trigger.Value == DependencyProperty.UnsetValue);
Debug.Assert(_triggerValueObject == DependencyProperty.UnsetValue);
_triggerValueObject = value;
return true;
}
Condition condition = o as Condition;
if (condition != null)
{
Debug.Assert(condition.Value == DependencyProperty.UnsetValue);
Debug.Assert(_conditionValueObject == DependencyProperty.UnsetValue);
_conditionValueObject = value;
return true;
}
DataTrigger dataTrigger = o as DataTrigger;
if (dataTrigger != null)
{
Debug.Assert(dataTrigger.Value == DependencyProperty.UnsetValue);
Debug.Assert(_triggerValueObject == DependencyProperty.UnsetValue);
_triggerValueObject = value;
return true;
}
}
return false;
}
protected override void ReadPropertyCustomRecord(BamlPropertyCustomRecord bamlPropertyRecord)
{
object o = CurrentContext.ObjectData;
string propertyName = MapTable.GetAttributeNameFromId(bamlPropertyRecord.AttributeId);
if (propertyName == XamlStyleSerializer.SetterPropertyAttributeName)
{
DependencyProperty propertyDP = GetCustomDependencyPropertyValue(bamlPropertyRecord);
Debug.Assert(propertyDP != null);
Setter setter = o as Setter;
if (setter != null)
{
Debug.Assert(setter.Property == null);
setter.Property = propertyDP;
return;
}
Trigger trigger = o as Trigger;
if (trigger != null)
{
Debug.Assert(trigger.Property == null);
trigger.Property = propertyDP;
return;
}
Condition condition = o as Condition;
if (condition != null)
{
Debug.Assert(condition.Property == null);
condition.Property = propertyDP;
return;
}
}
else if (propertyName == XamlStyleSerializer.SetterValueAttributeName)
{
Setter setter = o as Setter;
if (setter != null)
{
// Setter "Property" property is always set before "Value" property
Debug.Assert(setter.Property != null && setter.Value == DependencyProperty.UnsetValue);
setter.Value = GetCustomValue(bamlPropertyRecord, setter.Property.PropertyType, propertyName);
return;
}
Trigger trigger = o as Trigger;
if (trigger != null)
{
// Trigger "Property" property is always set before "Value" property
Debug.Assert(trigger.Property != null && trigger.Value == DependencyProperty.UnsetValue);
trigger.Value = GetCustomValue(bamlPropertyRecord, trigger.Property.PropertyType, propertyName);
return;
}
Condition condition = o as Condition;
if (condition != null)
{
// Condition "Property" property is always set before "Value" property
Debug.Assert(condition.Property != null && condition.Value == DependencyProperty.UnsetValue);
condition.Value = GetCustomValue(bamlPropertyRecord, condition.Property.PropertyType, propertyName);
return;
}
}
base.ReadPropertyCustomRecord(bamlPropertyRecord);
}
//+-------------------------------------------------------------------------------
//
// CompleteSetterOrTriggerOrCondition
//
// This is called when we found the end tag of a Setter, Trigger, or Condition.
// This is where we type-convert the properties that were order-dependent,
// e.g. Value property depends on knowing the Property property (so it can find
// the right type converter).
//
//+--------------------------------------------------------------------------------
private void CompleteSetterOrTriggerOrCondition(Object o)
{
string nameString;
object convertedValue = null;
DependencyProperty propertyDP = null;
// Handle setters
Setter setter = o as Setter;
if (setter != null)
{
nameString = setter.TargetName; // do not reset setter.TargetName
propertyDP = setter.Property;
convertedValue = setter.ValueInternal;
CompletePropertyAndValue(o,
ref nameString,
ref _setterPropertyString,
ref _setterValueString,
ref _setterValueObject,
ref _setterValueConverterTypeId,
ref propertyDP,
ref convertedValue);
setter.Property = propertyDP;
setter.Value = convertedValue;
return;
}
// Handle triggers
Trigger trigger = o as Trigger;
if (trigger != null)
{
trigger.SourceName = _sourceNameString;
propertyDP = trigger.Property;
convertedValue = trigger.Value;
CompletePropertyAndValue(o,
ref _sourceNameString,
ref _triggerPropertyString,
ref _triggerValueString,
ref _triggerValueObject,
ref _triggerValueConverterTypeId,
ref propertyDP,
ref convertedValue );
trigger.Property = propertyDP;
trigger.Value = convertedValue;
return;
}
// Handle conditions
Condition condition = o as Condition;
if (condition != null)
{
condition.SourceName = _sourceNameString;
propertyDP = condition.Property;
convertedValue = condition.Value;
bool copyDP = true;
if (_conditionPropertyString == null && propertyDP == null)
{
// we get here, for example, in a Condition for a MultiDataTrigger.
// This has no Property, but we should still convert the Value
// to an object, including resolving MarkupExtensions, freezing, etc.
propertyDP = ContentPresenter.ContentProperty; // any DP of type Object will do
copyDP = false;
}
CompletePropertyAndValue(o,
ref _sourceNameString,
ref _conditionPropertyString,
ref _conditionValueString,
ref _conditionValueObject,
ref _conditionValueConverterTypeId,
ref propertyDP,
ref convertedValue);
if (copyDP)
condition.Property = propertyDP;
condition.Value = convertedValue;
return;
}
// Handle data triggers
DataTrigger dataTrigger = o as DataTrigger;
if (dataTrigger != null)
{
nameString = null; // DataTrigger does not use named elements
convertedValue = dataTrigger.Value;
propertyDP = ContentPresenter.ContentProperty; // any DP of type Object will do
CompletePropertyAndValue(o,
ref nameString,
ref _triggerPropertyString,
ref _triggerValueString,
ref _triggerValueObject,
ref _triggerValueConverterTypeId,
ref propertyDP,
ref convertedValue);
dataTrigger.Value = convertedValue;
return;
}
}
//+-------------------------------------------------------------------------
//
// CompletePropertyAndValue
//
// As part of completing the Setter, Trigger, or Condition element,
// figure out the property and/or value properties.
//
//+-------------------------------------------------------------------------
private void CompletePropertyAndValue(object targetObject,
ref string targetName,
ref string propertyString,
ref string valueString,
ref object valueObject,
ref short valueConverterTypeId,
ref DependencyProperty propertyDP,
ref object convertedValue)
{
if (propertyDP == null)
{
if (propertyString == null)
{
ThrowException(SRID.StylePropertySetterMinAttrs);
}
// Get the owner type
Type ownerType = FindTypeToUseForResolvingProperty( targetName );
// Convert the property string to a DP
propertyDP = XamlTypeMapper.ParsePropertyName(ParserContext, propertyString, ref ownerType);
if (propertyDP == null)
{
ThrowException(SRID.ParserNoDPOnOwner, propertyString, ownerType.FullName);
}
}
targetName = null;
propertyString = null;
// Convert the value string to a value.
if (valueString != null)
{
Debug.Assert(DependencyProperty.UnsetValue == convertedValue);
// Yes, convert the value from string
TypeConvertContext typeConverterContext = new TypeConvertContext(ParserContext);
convertedValue = XamlTypeMapper.ParseProperty(
targetObject,
propertyDP.PropertyType,
propertyDP.Name,
propertyDP,
typeConverterContext,
ParserContext,
valueString,
valueConverterTypeId);
}
else if (DependencyProperty.UnsetValue != valueObject)
{
convertedValue = valueObject;
}
else if (DependencyProperty.UnsetValue == convertedValue)
{
ThrowException(SRID.StylePropertySetterMinAttrs);
}
// Clear all the data holding members
valueString = null;
valueObject = DependencyProperty.UnsetValue;
valueConverterTypeId = 0;
// This value will be shared by all instances of the template. So freeze it,
// call ME.ProvideValue on it, etc.
StyleHelper.ProcessSharedPropertyValue(
ParserContext,
targetObject,
propertyDP,
ref convertedValue );
}
//+---------------------------------------------------------------------------------
//
// FindTypeToUseForResolvingProperty
//
// When processing the Property property of a Condition/Setter/Trigger,
// we need to know the owner type, so that we can look up the property by name.
//
//+----------------------------------------------------------------------------------
private Type FindTypeToUseForResolvingProperty(string targetName)
{
// When parsing a CLR property, the type to use is current TypeTag's type, if
// there is one, or the type refered to by the Target="SomeID" reference.
// The types of the ID reference are held in the _templateNameToType table.
Type type = null;
// Do we have a target name?
if (targetName != null)
{
// Yes. Get the target's type, which we cached away while reading the content.
type = (Type)_templateNameToType[targetName];
if (type == null)
{
ThrowException(SRID.TemplateNoTarget, targetName);
}
}
// If we don't have an owner type yet, use the one for the
// target type of the Template.
else
{
type = _frameworkTemplate.TargetTypeInternal;
}
return type;
}
/***************************************************************************\
*
* TemplateBamlRecordReader.ReadElementStartRecord
*
* Read the start of an element. This is used to track element depth, so
* this reader knows when to stop reading the template section.
*
\***************************************************************************/
protected override bool ReadElementStartRecord(
BamlElementStartRecord bamlElementStartRecord)
{
bool usedSerializer = false;
_elementDepth++;
if( _inContent )
{
_lastElementTypeId = bamlElementStartRecord.TypeId;
AddContentRecord( bamlElementStartRecord );
}
else
{
usedSerializer = base.ReadElementStartRecord( bamlElementStartRecord );
if( usedSerializer )
{
--_elementDepth;
}
}
return usedSerializer;
}
protected override void ReadConnectionId(BamlConnectionIdRecord bamlConnectionIdRecord)
{
if( _inContent )
{
AddContentRecord( bamlConnectionIdRecord );
}
else
{
base.ReadConnectionId( bamlConnectionIdRecord );
}
return;
}
/****************************************************************************\
*
* TemplateBamlRecordReader.ReadElementEndRecord
*
* Called when parsing the end of an Element. When the end of the Template
* is reached, finish parsing the template block by setting the record
* collection on the template.
*
\***************************************************************************/
protected internal override void ReadElementEndRecord(bool fromNestedBamlRecordReader)
{
// Is this from a nested reader (e.g. a nested style or template)?
if( fromNestedBamlRecordReader )
{
// If we're in the template content, then do nothing. If we aren't
// in the content, then just pass up the call to base.
if (!_inContent)
{
base.ReadElementEndRecord(fromNestedBamlRecordReader);
}
return;
}
_elementDepth--;
// Are we somewhere other than the end of the template?
if( _elementDepth != 0 )
{
// If we're in content, just collect the record.
if( _inContent )
{
AddContentRecord(_currentBamlRecord);
}
// If we're not in content, process normally
else
{
base.ReadElementEndRecord( fromNestedBamlRecordReader );
}
}
// Otherwise, we're at the end of the template itself
else
{
// Put the shared Baml content records into the template.
// For some reason, setting the ParserContext causes it to update the BamlRecordReader
// to point to it. So update that reference here.
ParserContext = ParserContext;
// We don't need to keep the baml records any more; the template has
// kept the ones it needs.
_templateNameToType = null;
// The template is done parsing, so call back to the base to have the style added
// to the parent element, or to a resources dictionary. Check to see if we need
// to generate a dictionary key at this point. If it has not been assigned, then
// do it now.
if (GetDictionaryFromContext(ParentContext, true /*isInjected*/ ) != null && CurrentContext.Key == null)
{
// We use a DataTemplateKey or TableTemplateKey object as
// the key if we have a DataType property set on the template.
object key = null;
if (_frameworkTemplate != null)
{
if (_frameworkTemplate.DataTypeInternal != null)
{
key = new DataTemplateKey(_frameworkTemplate.DataTypeInternal);
}
}
if (key != null)
{
CurrentContext.Key = key;
}
}
// Process the end record itself
PreviousBamlRecordReader.ReadElementEndRecord(true);
// Put back the old reader into the ParserContext
ParserContext.BamlReader = PreviousBamlRecordReader;
}
}
/****************************************************************************\
*
* TemplateBamlRecordReader.ReadDeferableContentStart
*
* Called when parsing the deferable content start element.
* For the case of a ResourceDictionary inside template content, we read
* the dictionary values into a byte array while creating the template
* content. Later during template instantiation when the dictionary instance
* is created we use this buffer to create a memory stream so that the
* ResourceDictionary can use it to RealizeDeferredContent. This is required
* because at template instantiation time we do not have a stream to work with.
* The reader operates on a linked list of BamlRecords instead.
*
\***************************************************************************/
internal override void ReadDeferableContentStart(
BamlDeferableContentStartRecord bamlDeferableContentStartRecord)
{
if (!_inContent)
{
base.ReadDeferableContentStart( bamlDeferableContentStartRecord );
return;
}
AddContentRecord(bamlDeferableContentStartRecord);
Stream stream = null;
long startPosition = -1;
if (PreParsedRecordsStart == null)
{
stream = BinaryReader.BaseStream;
startPosition = stream.Position;
}
// Read past all the keys and staticresources belonging to this deferred section
BamlRecord bamlRecord;
BamlRecordType nextType = GetNextRecordType();
while (nextType == BamlRecordType.DefAttributeKeyString ||
nextType == BamlRecordType.DefAttributeKeyType ||
nextType == BamlRecordType.KeyElementStart)
{
bamlRecord = GetNextRecord();
ReadRecord(bamlRecord);
if (nextType == BamlRecordType.KeyElementStart)
{
while (nextType != BamlRecordType.KeyElementEnd)
{
bamlRecord = GetNextRecord();
ReadRecord(bamlRecord);
nextType = bamlRecord.RecordType;
}
}
nextType = GetNextRecordType();
while (nextType == BamlRecordType.StaticResourceStart ||
nextType == BamlRecordType.StaticResourceId ||
nextType == BamlRecordType.OptimizedStaticResource)
{
bamlRecord = GetNextRecord();
ReadRecord(bamlRecord);
if (nextType == BamlRecordType.StaticResourceStart)
{
while (nextType != BamlRecordType.StaticResourceEnd)
{
bamlRecord = GetNextRecord();
ReadRecord(bamlRecord);
nextType = bamlRecord.RecordType;
}
}
nextType = GetNextRecordType();
}
nextType = GetNextRecordType();
}
if (PreParsedRecordsStart == null)
{
// Copy the defer load contents into a buffer.
long endOfKeysPosition = stream.Position;
Int32 valuesSize = (Int32)(bamlDeferableContentStartRecord.ContentSize - endOfKeysPosition + startPosition);
byte[] buffer = new byte[valuesSize];
if (valuesSize > 0)
{
MS.Internal.IO.Packaging.PackagingUtilities.ReliableRead(
BinaryReader, buffer, 0, valuesSize);
}
// Cache the values buffer on the DeferableContentRecord so that it
// can then be retrieved and used during template instantiation.
bamlDeferableContentStartRecord.ValuesBuffer = buffer;
}
}
#endregion // Overrides
/***************************************************************************\
*
* AddContentRecord
*
* Add a new baml record to the collection of records to be held by the
* template.
*
\***************************************************************************/
private void AddContentRecord(BamlRecord bamlRecord )
{
// If this is a BamlElementStartRecord, convert it into a
// BamlNamedElementStartRecord, as this is what the template
// infrastructure is based on. We have to do it here, so that
// it is incorporated correctly into the linked list below.
bamlRecord = CreateNameRecordIfNecessary( bamlRecord );
// Link the baml records together. We need them linked so that we
// can call nested custom serializers, and also for unwinding when we
// discover an un-shareable property value.
if (_bamlRecordListTail != null )
{
_bamlRecordListTail.Next = bamlRecord;
}
// Pin the record so we can store it on this list.
bamlRecord.Pin();
// Also Pin any Debug extension record.
BamlRecord debugExtensionRecord=null;
if (BamlRecordHelper.HasDebugExtensionRecord(ParserContext.IsDebugBamlStream, bamlRecord))
{
// If this a nested template then it needs a separate set of BamlRecords
// that can be chained together to represent the template content.
if (_inNestedTemplate)
{
debugExtensionRecord = BamlRecordManager.CloneRecord(bamlRecord.Next);
bamlRecord.Next = debugExtensionRecord;
}
else
{
debugExtensionRecord = bamlRecord.Next;
}
debugExtensionRecord.Pin();
}
// For named elements, keep track of the type of the element. We need this to
// resolve property references in triggers.
if( bamlRecord.RecordType == BamlRecordType.Property
||
bamlRecord.RecordType == BamlRecordType.PropertyWithConverter )
{
BamlPropertyRecord bamlPropertyRecord = bamlRecord as BamlPropertyRecord;
if (MapTable.DoesAttributeMatch(bamlPropertyRecord.AttributeId, BamlAttributeUsage.RuntimeName))
{
// (Could search the baml records here instead)
_templateNameToType[bamlPropertyRecord.Value] = MapTable.GetTypeFromId( _lastElementTypeId );
}
}
_optimizedTemplateContent.AddContentRecord( bamlRecord );
// Advance the tail (including any Debug Extension record)
_bamlRecordListTail = (debugExtensionRecord == null) ? bamlRecord : debugExtensionRecord;
}
/****************************************************************************\
*
* CreateNameRecordIfNecessary
*
* This method converts a BamlElementStart record into a
* BamlNamedElementStartRecord. See the comment on BamlNamedElementStartRecord
* for more detail on that record.
*
\***************************************************************************/
private BamlRecord CreateNameRecordIfNecessary( BamlRecord bamlRecord )
{
// If this isn't a BamlElementStart record, then we don't need to do anything.
if( bamlRecord.GetType() != typeof(BamlElementStartRecord) )
{
return bamlRecord;
}
BamlElementStartRecord bamlElementStartRecord = (BamlElementStartRecord)bamlRecord;
// Or if it's already a BamlNamedElementStartRecord, then we're similarly done.
if( bamlElementStartRecord is BamlNamedElementStartRecord )
{
return bamlRecord;
}
// Convert this BamlElementStartRecord into a BamlNamedElementStartRecord
BamlNamedElementStartRecord bamlNamedElementStartRecord = new BamlNamedElementStartRecord();
bamlNamedElementStartRecord.PinnedCount = bamlElementStartRecord.PinnedCount;
bamlNamedElementStartRecord.TypeId = bamlElementStartRecord.TypeId;
bamlNamedElementStartRecord.Next = bamlElementStartRecord.Next;
bamlNamedElementStartRecord.CreateUsingTypeConverter = bamlElementStartRecord.CreateUsingTypeConverter;
bamlNamedElementStartRecord.IsInjected = bamlElementStartRecord.IsInjected;
bamlElementStartRecord.Next = null;
return bamlNamedElementStartRecord;
}
private bool InNestedTemplate()
{
BamlRecordReader reader = PreviousBamlRecordReader;
while (reader != null)
{
if (reader is TemplateApplicationHelper)
{
return true;
}
reader = reader.PreviousBamlRecordReader;
}
return false;
}
//+-----------------------------------------------------------------------------------------
//
// State
//
//+-----------------------------------------------------------------------------------------
short _lastElementTypeId;
private int _elementDepth;
private FrameworkTemplate _frameworkTemplate;
private OptimizedTemplateContent _optimizedTemplateContent;
private bool _inTemplateElement;
private bool _inContent;
private bool _inNestedTemplate;
private string _triggerValueString;
private object _triggerValueObject = DependencyProperty.UnsetValue;
private short _triggerValueConverterTypeId;
private string _triggerPropertyString;
private string _setterValueString;
private object _setterValueObject = DependencyProperty.UnsetValue;
private short _setterValueConverterTypeId;
private string _setterPropertyString;
private string _conditionValueString;
private object _conditionValueObject = DependencyProperty.UnsetValue;
private short _conditionValueConverterTypeId;
private string _conditionPropertyString;
private string _sourceNameString;
private Hashtable _templateNameToType = new Hashtable();
private BamlRecord _bamlRecordListTail;
private BamlRecord _currentBamlRecord;
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
/****************************************************************************\
*
* File: TemplateBamlRecordReader.cs
*
* Purpose: Main class to handle reading a Template Baml records from a stream
*
* History:
* 11/22/04: varsham Created
* 06/15/05: peterost Refactor for new template architecture
*
* Copyright (C) 2004 by Microsoft Corporation. All rights reserved.
*
\***************************************************************************/
using System;
using System.Xml;
using System.IO;
using System.Windows;
using System.Windows.Navigation;
using System.Text;
using System.Collections;
using System.ComponentModel;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media.Animation;
using System.Diagnostics;
using System.Reflection;
using System.Windows.Threading;
using System.Windows.Data;
using System.Globalization;
using MS.Utility;
using MS.Internal;
namespace System.Windows.Markup
{
///
/// Reads BAML from a Stream that pertains to Template elements and properties.
/// This is an internal class
///
internal class TemplateBamlRecordReader : BamlRecordReader
{
#region Constructor
///
/// TemplateBamlRecordReader constructor
///
/// The input BAML stream, if loading from a file
/// StartRecord for List of records, if loading from a dictionary
/// Index Record into list of records to start parsing at
/// The parser context
/// Reader stack from upper level parser to get
/// context for things such as dictionaries and parents.
/// list of root objects for the parse
internal TemplateBamlRecordReader(
Stream bamlStream,
BamlRecord bamlStartRecord,
BamlRecord bamlIndexRecord,
ParserContext parserContext,
ParserStack bamlReaderStack,
ArrayList rootList)
{
Debug.Assert(null != bamlStream || bamlStartRecord != null);
Debug.Assert(null != parserContext && null != parserContext.XamlTypeMapper);
// Link this baml record reader to the "parent" reader, so that
// we can see its context stack.
SetPreviousBamlRecordReader( parserContext.BamlReader );
ParserContext = parserContext;
RootList = rootList;
PreParsedRecordsStart = bamlStartRecord;
PreParsedCurrentRecord = bamlIndexRecord;
if (bamlStream != null)
{
BamlStream = bamlStream;
}
RootElement = ParserContext.RootElement;
ComponentConnector = RootElement as IComponentConnector;
ContextStack = bamlReaderStack;
_elementDepth = 0;
// We're starting out in the template element itself, e.g. tag?
if( _inTemplateElement )
{
// Yes
moreData = TryReadTemplateElementRecord( bamlRecord );
// If we're still in the template element itself, ReadRootRecord above is all the processing
// we need.
if (_inTemplateElement)
{
return moreData;
}
// Otherwise, we're on the first child of the root element (the first child of the tag)
else
{
// Put the template's TargetType into the parser context for use by Setter's etc (see comment
// on ParserContext.TargetType).
ParserContext.TargetType = _frameworkTemplate.TargetTypeInternal;
// Give the template its own copy of the parser context. It needs a copy, because it's
// going to use it later on every time it's applied.
_frameworkTemplate.CopyParserContext(ParserContext);
}
}
// Check to see if we should update _inContent.
if (_elementDepth == 1 && CurrentContext.ContextType == ReaderFlags.ClrObject)
{
// We're directly under the tag ...
if (bamlRecord.RecordType == BamlRecordType.ElementStart)
{
// ... and we're at a start element, so we must be in template content.
_inContent = true;
_optimizedTemplateContent.BeginReadingContent(ParserContext);
}
else if( _inContent )
{
// ... but we're not on an element start, and we were in content,
// so we must not be any more.
_inContent = false;
_optimizedTemplateContent.EndReadingContent();
}
}
// Are we in the template content?
if( _inContent)
{
// If this a nested template then it needs a separate set of BamlRecords
// that can be chained together to represent the template content.
if (_inNestedTemplate)
{
bamlRecord = BamlRecordManager.CloneRecord(bamlRecord);
_currentBamlRecord = bamlRecord;
}
// Yes, we're in template content. We do some minimal amount of
// processing here, but mostly pass to AddContentRecord.
// If this is a map table record (such as an attribute info), process
// it normally.
if( BamlRecordHelper.IsMapTableRecordType( bamlRecord.RecordType ))
{
base.ReadRecord(bamlRecord);
}
// Simiarly, let base process deferrable content and connection IDs
else if( bamlRecord.RecordType == BamlRecordType.DeferableContentStart
||
bamlRecord.RecordType == BamlRecordType.ConnectionId )
{
base.ReadRecord(bamlRecord);
}
// If this is an element start/end, we have to call the override directly.
else if( bamlRecord.RecordType == BamlRecordType.ElementStart )
{
// This is to avoid the checks for injected tags done by the base reader
ReadElementStartRecord((BamlElementStartRecord)bamlRecord);
}
else if( bamlRecord.RecordType == BamlRecordType.ElementEnd )
{
// This is to avoid the checks for injected tags done by the base reader
ReadElementEndRecord(false);
}
// Otherwise, hand off the record to the OptimizedTemplateContent.
else
{
AddContentRecord( bamlRecord );
}
}
else
{
// We're not in the content any more, process the triggers.
moreData = ReadNonContentRecord( bamlRecord );
}
_currentBamlRecord = null;
return _elementDepth > 0 && moreData;
}
//+-----------------------------------------------------------------------
//
// ReadRootRecord
//
// Read a baml record that's part of the template tag itself, i.e.
// part of the element. Note that this routine
// will keep the _inRootElement flag up-to-date, and will *not* process
// the last record, where it discovers that it's out of the root element.
//
//+-----------------------------------------------------------------------
internal bool TryReadTemplateElementRecord( BamlRecord bamlRecord )
{
bool moreData = true;
Debug.Assert( _inTemplateElement );
if( bamlRecord.RecordType == BamlRecordType.ElementStart )
{
// If this is an element start record, we're either on the
// tag itself, and not part of the template content (i.e. it's either the
// .Resources or the .Triggers).
//
//+------------------------------------------------------------------------------
internal bool ReadNonContentRecord( BamlRecord bamlRecord )
{
bool skipRead = false;
// Look for the "Value" and "Property" attributes in triggers, setters, and conditions.
// We won't really process these until the end of the element.
if (bamlRecord.RecordType == BamlRecordType.Property
||
bamlRecord.RecordType == BamlRecordType.PropertyWithConverter)
{
BamlPropertyRecord bamlPropertyRecord = bamlRecord as BamlPropertyRecord;
short converterTypeId = 0;
BamlPropertyWithConverterRecord bamlPropertyWithConverterRecord = bamlRecord as BamlPropertyWithConverterRecord;
if( bamlPropertyWithConverterRecord != null )
{
converterTypeId = bamlPropertyWithConverterRecord.ConverterTypeId;
}
// Is this a "Value" property? If so, see if it's part of a trigger, setter, or
// condition, and if so save it away.
string name = MapTable.GetAttributeNameFromId(bamlPropertyRecord.AttributeId);
if (name == "Value")
{
if( CurrentContext.ObjectData is Trigger || CurrentContext.ObjectData is DataTrigger )
{
Debug.Assert( _triggerValueString == null );
_triggerValueString = bamlPropertyRecord.Value;
_triggerValueConverterTypeId = converterTypeId;
skipRead = true;
}
else if( CurrentContext.ObjectData is Setter )
{
Debug.Assert( _setterValueString == null );
_setterValueString = bamlPropertyRecord.Value;
_setterValueConverterTypeId = converterTypeId;
skipRead = true;
}
else if( CurrentContext.ObjectData is Condition )
{
Debug.Assert( _conditionValueString == null );
_conditionValueString = bamlPropertyRecord.Value;
_conditionValueConverterTypeId = converterTypeId;
skipRead = true;
}
else
{
// CurrentContext.ObjectData is none of the
// types above, we need skipRead to stay false
// so we will ask base BamlRecordReader to process the data.
}
}
else if (name == "Property")
{
if( CurrentContext.ObjectData is Trigger )
{
Debug.Assert( _triggerPropertyString == null );
_triggerPropertyString = bamlPropertyRecord.Value;
skipRead = true;
}
else if( CurrentContext.ObjectData is Setter )
{
Debug.Assert( _setterPropertyString == null );
_setterPropertyString = bamlPropertyRecord.Value;
skipRead = true;
}
else if( CurrentContext.ObjectData is Condition )
{
Debug.Assert( _conditionPropertyString == null );
_conditionPropertyString = bamlPropertyRecord.Value;
skipRead = true;
}
else
{
// CurrentContext.ObjectData is none of the
// types above, we need skipRead to stay false
// so we will ask base BamlRecordReader to process the data.
}
}
else if (name == "SourceName")
{
if( CurrentContext.ObjectData is Trigger )
{
Debug.Assert( _sourceNameString == null );
_sourceNameString = bamlPropertyRecord.Value;
skipRead = true;
}
else if( CurrentContext.ObjectData is Condition )
{
Debug.Assert( _sourceNameString == null );
_sourceNameString = bamlPropertyRecord.Value;
skipRead = true;
}
}
/*
else if( attrInfo.Name == "TargetName" )
{
if( CurrentContext.ObjectData is Setter )
{
Debug.Assert( _targetNameString == null );
_targetNameString = bamlPropertyRecord.Value;
skipRead = true;
}
}
*/
}
// If this is the end element of a Trigger, Setter, or Condition,
// finish processing of it (convert the Value/Property properties).
else if (bamlRecord.RecordType == BamlRecordType.ElementEnd)
{
if (CurrentContext.ObjectData != null)
{
CompleteSetterOrTriggerOrCondition(CurrentContext.ObjectData);
}
}
// If we didn't process this record internally above, pass it up to base.
if (!skipRead)
return base.ReadRecord(bamlRecord);
else
return true;
}
protected override void ReadPropertyWithExtensionRecord(BamlPropertyWithExtensionRecord bamlPropertyRecord)
{
string propertyName = MapTable.GetAttributeNameFromId(bamlPropertyRecord.AttributeId);
object value = GetExtensionValue(bamlPropertyRecord, propertyName);
bool handled = BaseReadOptimizedMarkupExtension(propertyName, value);
if (!handled)
{
base.ReadPropertyWithExtensionRecord(bamlPropertyRecord);
}
}
protected override void ReadPropertyWithStaticResourceIdRecord(
BamlPropertyWithStaticResourceIdRecord bamlPropertyWithStaticResourceIdRecord)
{
string propertyName = MapTable.GetAttributeNameFromId(bamlPropertyWithStaticResourceIdRecord.AttributeId);
// Find the StaticResourceValue for the given Id
object value = GetStaticResourceFromId(bamlPropertyWithStaticResourceIdRecord.StaticResourceId);
bool handled = BaseReadOptimizedMarkupExtension(propertyName, value);
if (!handled)
{
base.ReadPropertyWithStaticResourceIdRecord(bamlPropertyWithStaticResourceIdRecord);
}
}
private bool BaseReadOptimizedMarkupExtension(string propertyName, object value)
{
object o = CurrentContext.ObjectData;
if (propertyName == XamlStyleSerializer.SetterValueAttributeName)
{
Setter setter = o as Setter;
if (setter != null)
{
Debug.Assert(setter.Value == DependencyProperty.UnsetValue);
Debug.Assert(_setterValueObject == DependencyProperty.UnsetValue);
_setterValueObject = value;
return true;
}
Trigger trigger = o as Trigger;
if (trigger != null)
{
Debug.Assert(trigger.Value == DependencyProperty.UnsetValue);
Debug.Assert(_triggerValueObject == DependencyProperty.UnsetValue);
_triggerValueObject = value;
return true;
}
Condition condition = o as Condition;
if (condition != null)
{
Debug.Assert(condition.Value == DependencyProperty.UnsetValue);
Debug.Assert(_conditionValueObject == DependencyProperty.UnsetValue);
_conditionValueObject = value;
return true;
}
DataTrigger dataTrigger = o as DataTrigger;
if (dataTrigger != null)
{
Debug.Assert(dataTrigger.Value == DependencyProperty.UnsetValue);
Debug.Assert(_triggerValueObject == DependencyProperty.UnsetValue);
_triggerValueObject = value;
return true;
}
}
return false;
}
protected override void ReadPropertyCustomRecord(BamlPropertyCustomRecord bamlPropertyRecord)
{
object o = CurrentContext.ObjectData;
string propertyName = MapTable.GetAttributeNameFromId(bamlPropertyRecord.AttributeId);
if (propertyName == XamlStyleSerializer.SetterPropertyAttributeName)
{
DependencyProperty propertyDP = GetCustomDependencyPropertyValue(bamlPropertyRecord);
Debug.Assert(propertyDP != null);
Setter setter = o as Setter;
if (setter != null)
{
Debug.Assert(setter.Property == null);
setter.Property = propertyDP;
return;
}
Trigger trigger = o as Trigger;
if (trigger != null)
{
Debug.Assert(trigger.Property == null);
trigger.Property = propertyDP;
return;
}
Condition condition = o as Condition;
if (condition != null)
{
Debug.Assert(condition.Property == null);
condition.Property = propertyDP;
return;
}
}
else if (propertyName == XamlStyleSerializer.SetterValueAttributeName)
{
Setter setter = o as Setter;
if (setter != null)
{
// Setter "Property" property is always set before "Value" property
Debug.Assert(setter.Property != null && setter.Value == DependencyProperty.UnsetValue);
setter.Value = GetCustomValue(bamlPropertyRecord, setter.Property.PropertyType, propertyName);
return;
}
Trigger trigger = o as Trigger;
if (trigger != null)
{
// Trigger "Property" property is always set before "Value" property
Debug.Assert(trigger.Property != null && trigger.Value == DependencyProperty.UnsetValue);
trigger.Value = GetCustomValue(bamlPropertyRecord, trigger.Property.PropertyType, propertyName);
return;
}
Condition condition = o as Condition;
if (condition != null)
{
// Condition "Property" property is always set before "Value" property
Debug.Assert(condition.Property != null && condition.Value == DependencyProperty.UnsetValue);
condition.Value = GetCustomValue(bamlPropertyRecord, condition.Property.PropertyType, propertyName);
return;
}
}
base.ReadPropertyCustomRecord(bamlPropertyRecord);
}
//+-------------------------------------------------------------------------------
//
// CompleteSetterOrTriggerOrCondition
//
// This is called when we found the end tag of a Setter, Trigger, or Condition.
// This is where we type-convert the properties that were order-dependent,
// e.g. Value property depends on knowing the Property property (so it can find
// the right type converter).
//
//+--------------------------------------------------------------------------------
private void CompleteSetterOrTriggerOrCondition(Object o)
{
string nameString;
object convertedValue = null;
DependencyProperty propertyDP = null;
// Handle setters
Setter setter = o as Setter;
if (setter != null)
{
nameString = setter.TargetName; // do not reset setter.TargetName
propertyDP = setter.Property;
convertedValue = setter.ValueInternal;
CompletePropertyAndValue(o,
ref nameString,
ref _setterPropertyString,
ref _setterValueString,
ref _setterValueObject,
ref _setterValueConverterTypeId,
ref propertyDP,
ref convertedValue);
setter.Property = propertyDP;
setter.Value = convertedValue;
return;
}
// Handle triggers
Trigger trigger = o as Trigger;
if (trigger != null)
{
trigger.SourceName = _sourceNameString;
propertyDP = trigger.Property;
convertedValue = trigger.Value;
CompletePropertyAndValue(o,
ref _sourceNameString,
ref _triggerPropertyString,
ref _triggerValueString,
ref _triggerValueObject,
ref _triggerValueConverterTypeId,
ref propertyDP,
ref convertedValue );
trigger.Property = propertyDP;
trigger.Value = convertedValue;
return;
}
// Handle conditions
Condition condition = o as Condition;
if (condition != null)
{
condition.SourceName = _sourceNameString;
propertyDP = condition.Property;
convertedValue = condition.Value;
bool copyDP = true;
if (_conditionPropertyString == null && propertyDP == null)
{
// we get here, for example, in a Condition for a MultiDataTrigger.
// This has no Property, but we should still convert the Value
// to an object, including resolving MarkupExtensions, freezing, etc.
propertyDP = ContentPresenter.ContentProperty; // any DP of type Object will do
copyDP = false;
}
CompletePropertyAndValue(o,
ref _sourceNameString,
ref _conditionPropertyString,
ref _conditionValueString,
ref _conditionValueObject,
ref _conditionValueConverterTypeId,
ref propertyDP,
ref convertedValue);
if (copyDP)
condition.Property = propertyDP;
condition.Value = convertedValue;
return;
}
// Handle data triggers
DataTrigger dataTrigger = o as DataTrigger;
if (dataTrigger != null)
{
nameString = null; // DataTrigger does not use named elements
convertedValue = dataTrigger.Value;
propertyDP = ContentPresenter.ContentProperty; // any DP of type Object will do
CompletePropertyAndValue(o,
ref nameString,
ref _triggerPropertyString,
ref _triggerValueString,
ref _triggerValueObject,
ref _triggerValueConverterTypeId,
ref propertyDP,
ref convertedValue);
dataTrigger.Value = convertedValue;
return;
}
}
//+-------------------------------------------------------------------------
//
// CompletePropertyAndValue
//
// As part of completing the Setter, Trigger, or Condition element,
// figure out the property and/or value properties.
//
//+-------------------------------------------------------------------------
private void CompletePropertyAndValue(object targetObject,
ref string targetName,
ref string propertyString,
ref string valueString,
ref object valueObject,
ref short valueConverterTypeId,
ref DependencyProperty propertyDP,
ref object convertedValue)
{
if (propertyDP == null)
{
if (propertyString == null)
{
ThrowException(SRID.StylePropertySetterMinAttrs);
}
// Get the owner type
Type ownerType = FindTypeToUseForResolvingProperty( targetName );
// Convert the property string to a DP
propertyDP = XamlTypeMapper.ParsePropertyName(ParserContext, propertyString, ref ownerType);
if (propertyDP == null)
{
ThrowException(SRID.ParserNoDPOnOwner, propertyString, ownerType.FullName);
}
}
targetName = null;
propertyString = null;
// Convert the value string to a value.
if (valueString != null)
{
Debug.Assert(DependencyProperty.UnsetValue == convertedValue);
// Yes, convert the value from string
TypeConvertContext typeConverterContext = new TypeConvertContext(ParserContext);
convertedValue = XamlTypeMapper.ParseProperty(
targetObject,
propertyDP.PropertyType,
propertyDP.Name,
propertyDP,
typeConverterContext,
ParserContext,
valueString,
valueConverterTypeId);
}
else if (DependencyProperty.UnsetValue != valueObject)
{
convertedValue = valueObject;
}
else if (DependencyProperty.UnsetValue == convertedValue)
{
ThrowException(SRID.StylePropertySetterMinAttrs);
}
// Clear all the data holding members
valueString = null;
valueObject = DependencyProperty.UnsetValue;
valueConverterTypeId = 0;
// This value will be shared by all instances of the template. So freeze it,
// call ME.ProvideValue on it, etc.
StyleHelper.ProcessSharedPropertyValue(
ParserContext,
targetObject,
propertyDP,
ref convertedValue );
}
//+---------------------------------------------------------------------------------
//
// FindTypeToUseForResolvingProperty
//
// When processing the Property property of a Condition/Setter/Trigger,
// we need to know the owner type, so that we can look up the property by name.
//
//+----------------------------------------------------------------------------------
private Type FindTypeToUseForResolvingProperty(string targetName)
{
// When parsing a CLR property, the type to use is current TypeTag's type, if
// there is one, or the type refered to by the Target="SomeID" reference.
// The types of the ID reference are held in the _templateNameToType table.
Type type = null;
// Do we have a target name?
if (targetName != null)
{
// Yes. Get the target's type, which we cached away while reading the content.
type = (Type)_templateNameToType[targetName];
if (type == null)
{
ThrowException(SRID.TemplateNoTarget, targetName);
}
}
// If we don't have an owner type yet, use the one for the
// target type of the Template.
else
{
type = _frameworkTemplate.TargetTypeInternal;
}
return type;
}
/***************************************************************************\
*
* TemplateBamlRecordReader.ReadElementStartRecord
*
* Read the start of an element. This is used to track element depth, so
* this reader knows when to stop reading the template section.
*
\***************************************************************************/
protected override bool ReadElementStartRecord(
BamlElementStartRecord bamlElementStartRecord)
{
bool usedSerializer = false;
_elementDepth++;
if( _inContent )
{
_lastElementTypeId = bamlElementStartRecord.TypeId;
AddContentRecord( bamlElementStartRecord );
}
else
{
usedSerializer = base.ReadElementStartRecord( bamlElementStartRecord );
if( usedSerializer )
{
--_elementDepth;
}
}
return usedSerializer;
}
protected override void ReadConnectionId(BamlConnectionIdRecord bamlConnectionIdRecord)
{
if( _inContent )
{
AddContentRecord( bamlConnectionIdRecord );
}
else
{
base.ReadConnectionId( bamlConnectionIdRecord );
}
return;
}
/****************************************************************************\
*
* TemplateBamlRecordReader.ReadElementEndRecord
*
* Called when parsing the end of an Element. When the end of the Template
* is reached, finish parsing the template block by setting the record
* collection on the template.
*
\***************************************************************************/
protected internal override void ReadElementEndRecord(bool fromNestedBamlRecordReader)
{
// Is this from a nested reader (e.g. a nested style or template)?
if( fromNestedBamlRecordReader )
{
// If we're in the template content, then do nothing. If we aren't
// in the content, then just pass up the call to base.
if (!_inContent)
{
base.ReadElementEndRecord(fromNestedBamlRecordReader);
}
return;
}
_elementDepth--;
// Are we somewhere other than the end of the template?
if( _elementDepth != 0 )
{
// If we're in content, just collect the record.
if( _inContent )
{
AddContentRecord(_currentBamlRecord);
}
// If we're not in content, process normally
else
{
base.ReadElementEndRecord( fromNestedBamlRecordReader );
}
}
// Otherwise, we're at the end of the template itself
else
{
// Put the shared Baml content records into the template.
// For some reason, setting the ParserContext causes it to update the BamlRecordReader
// to point to it. So update that reference here.
ParserContext = ParserContext;
// We don't need to keep the baml records any more; the template has
// kept the ones it needs.
_templateNameToType = null;
// The template is done parsing, so call back to the base to have the style added
// to the parent element, or to a resources dictionary. Check to see if we need
// to generate a dictionary key at this point. If it has not been assigned, then
// do it now.
if (GetDictionaryFromContext(ParentContext, true /*isInjected*/ ) != null && CurrentContext.Key == null)
{
// We use a DataTemplateKey or TableTemplateKey object as
// the key if we have a DataType property set on the template.
object key = null;
if (_frameworkTemplate != null)
{
if (_frameworkTemplate.DataTypeInternal != null)
{
key = new DataTemplateKey(_frameworkTemplate.DataTypeInternal);
}
}
if (key != null)
{
CurrentContext.Key = key;
}
}
// Process the end record itself
PreviousBamlRecordReader.ReadElementEndRecord(true);
// Put back the old reader into the ParserContext
ParserContext.BamlReader = PreviousBamlRecordReader;
}
}
/****************************************************************************\
*
* TemplateBamlRecordReader.ReadDeferableContentStart
*
* Called when parsing the deferable content start element.
* For the case of a ResourceDictionary inside template content, we read
* the dictionary values into a byte array while creating the template
* content. Later during template instantiation when the dictionary instance
* is created we use this buffer to create a memory stream so that the
* ResourceDictionary can use it to RealizeDeferredContent. This is required
* because at template instantiation time we do not have a stream to work with.
* The reader operates on a linked list of BamlRecords instead.
*
\***************************************************************************/
internal override void ReadDeferableContentStart(
BamlDeferableContentStartRecord bamlDeferableContentStartRecord)
{
if (!_inContent)
{
base.ReadDeferableContentStart( bamlDeferableContentStartRecord );
return;
}
AddContentRecord(bamlDeferableContentStartRecord);
Stream stream = null;
long startPosition = -1;
if (PreParsedRecordsStart == null)
{
stream = BinaryReader.BaseStream;
startPosition = stream.Position;
}
// Read past all the keys and staticresources belonging to this deferred section
BamlRecord bamlRecord;
BamlRecordType nextType = GetNextRecordType();
while (nextType == BamlRecordType.DefAttributeKeyString ||
nextType == BamlRecordType.DefAttributeKeyType ||
nextType == BamlRecordType.KeyElementStart)
{
bamlRecord = GetNextRecord();
ReadRecord(bamlRecord);
if (nextType == BamlRecordType.KeyElementStart)
{
while (nextType != BamlRecordType.KeyElementEnd)
{
bamlRecord = GetNextRecord();
ReadRecord(bamlRecord);
nextType = bamlRecord.RecordType;
}
}
nextType = GetNextRecordType();
while (nextType == BamlRecordType.StaticResourceStart ||
nextType == BamlRecordType.StaticResourceId ||
nextType == BamlRecordType.OptimizedStaticResource)
{
bamlRecord = GetNextRecord();
ReadRecord(bamlRecord);
if (nextType == BamlRecordType.StaticResourceStart)
{
while (nextType != BamlRecordType.StaticResourceEnd)
{
bamlRecord = GetNextRecord();
ReadRecord(bamlRecord);
nextType = bamlRecord.RecordType;
}
}
nextType = GetNextRecordType();
}
nextType = GetNextRecordType();
}
if (PreParsedRecordsStart == null)
{
// Copy the defer load contents into a buffer.
long endOfKeysPosition = stream.Position;
Int32 valuesSize = (Int32)(bamlDeferableContentStartRecord.ContentSize - endOfKeysPosition + startPosition);
byte[] buffer = new byte[valuesSize];
if (valuesSize > 0)
{
MS.Internal.IO.Packaging.PackagingUtilities.ReliableRead(
BinaryReader, buffer, 0, valuesSize);
}
// Cache the values buffer on the DeferableContentRecord so that it
// can then be retrieved and used during template instantiation.
bamlDeferableContentStartRecord.ValuesBuffer = buffer;
}
}
#endregion // Overrides
/***************************************************************************\
*
* AddContentRecord
*
* Add a new baml record to the collection of records to be held by the
* template.
*
\***************************************************************************/
private void AddContentRecord(BamlRecord bamlRecord )
{
// If this is a BamlElementStartRecord, convert it into a
// BamlNamedElementStartRecord, as this is what the template
// infrastructure is based on. We have to do it here, so that
// it is incorporated correctly into the linked list below.
bamlRecord = CreateNameRecordIfNecessary( bamlRecord );
// Link the baml records together. We need them linked so that we
// can call nested custom serializers, and also for unwinding when we
// discover an un-shareable property value.
if (_bamlRecordListTail != null )
{
_bamlRecordListTail.Next = bamlRecord;
}
// Pin the record so we can store it on this list.
bamlRecord.Pin();
// Also Pin any Debug extension record.
BamlRecord debugExtensionRecord=null;
if (BamlRecordHelper.HasDebugExtensionRecord(ParserContext.IsDebugBamlStream, bamlRecord))
{
// If this a nested template then it needs a separate set of BamlRecords
// that can be chained together to represent the template content.
if (_inNestedTemplate)
{
debugExtensionRecord = BamlRecordManager.CloneRecord(bamlRecord.Next);
bamlRecord.Next = debugExtensionRecord;
}
else
{
debugExtensionRecord = bamlRecord.Next;
}
debugExtensionRecord.Pin();
}
// For named elements, keep track of the type of the element. We need this to
// resolve property references in triggers.
if( bamlRecord.RecordType == BamlRecordType.Property
||
bamlRecord.RecordType == BamlRecordType.PropertyWithConverter )
{
BamlPropertyRecord bamlPropertyRecord = bamlRecord as BamlPropertyRecord;
if (MapTable.DoesAttributeMatch(bamlPropertyRecord.AttributeId, BamlAttributeUsage.RuntimeName))
{
// (Could search the baml records here instead)
_templateNameToType[bamlPropertyRecord.Value] = MapTable.GetTypeFromId( _lastElementTypeId );
}
}
_optimizedTemplateContent.AddContentRecord( bamlRecord );
// Advance the tail (including any Debug Extension record)
_bamlRecordListTail = (debugExtensionRecord == null) ? bamlRecord : debugExtensionRecord;
}
/****************************************************************************\
*
* CreateNameRecordIfNecessary
*
* This method converts a BamlElementStart record into a
* BamlNamedElementStartRecord. See the comment on BamlNamedElementStartRecord
* for more detail on that record.
*
\***************************************************************************/
private BamlRecord CreateNameRecordIfNecessary( BamlRecord bamlRecord )
{
// If this isn't a BamlElementStart record, then we don't need to do anything.
if( bamlRecord.GetType() != typeof(BamlElementStartRecord) )
{
return bamlRecord;
}
BamlElementStartRecord bamlElementStartRecord = (BamlElementStartRecord)bamlRecord;
// Or if it's already a BamlNamedElementStartRecord, then we're similarly done.
if( bamlElementStartRecord is BamlNamedElementStartRecord )
{
return bamlRecord;
}
// Convert this BamlElementStartRecord into a BamlNamedElementStartRecord
BamlNamedElementStartRecord bamlNamedElementStartRecord = new BamlNamedElementStartRecord();
bamlNamedElementStartRecord.PinnedCount = bamlElementStartRecord.PinnedCount;
bamlNamedElementStartRecord.TypeId = bamlElementStartRecord.TypeId;
bamlNamedElementStartRecord.Next = bamlElementStartRecord.Next;
bamlNamedElementStartRecord.CreateUsingTypeConverter = bamlElementStartRecord.CreateUsingTypeConverter;
bamlNamedElementStartRecord.IsInjected = bamlElementStartRecord.IsInjected;
bamlElementStartRecord.Next = null;
return bamlNamedElementStartRecord;
}
private bool InNestedTemplate()
{
BamlRecordReader reader = PreviousBamlRecordReader;
while (reader != null)
{
if (reader is TemplateApplicationHelper)
{
return true;
}
reader = reader.PreviousBamlRecordReader;
}
return false;
}
//+-----------------------------------------------------------------------------------------
//
// State
//
//+-----------------------------------------------------------------------------------------
short _lastElementTypeId;
private int _elementDepth;
private FrameworkTemplate _frameworkTemplate;
private OptimizedTemplateContent _optimizedTemplateContent;
private bool _inTemplateElement;
private bool _inContent;
private bool _inNestedTemplate;
private string _triggerValueString;
private object _triggerValueObject = DependencyProperty.UnsetValue;
private short _triggerValueConverterTypeId;
private string _triggerPropertyString;
private string _setterValueString;
private object _setterValueObject = DependencyProperty.UnsetValue;
private short _setterValueConverterTypeId;
private string _setterPropertyString;
private string _conditionValueString;
private object _conditionValueObject = DependencyProperty.UnsetValue;
private short _conditionValueConverterTypeId;
private string _conditionPropertyString;
private string _sourceNameString;
private Hashtable _templateNameToType = new Hashtable();
private BamlRecord _bamlRecordListTail;
private BamlRecord _currentBamlRecord;
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- DataGridViewCellValidatingEventArgs.cs
- SqlStatistics.cs
- TextDecorations.cs
- ContractReference.cs
- ObjectSecurity.cs
- ZoomingMessageFilter.cs
- NativeWrapper.cs
- SchemaSetCompiler.cs
- AspCompat.cs
- EventItfInfo.cs
- XmlUTF8TextWriter.cs
- TrustSection.cs
- TypeValidationEventArgs.cs
- ElapsedEventArgs.cs
- SqlDataSourceConfigureFilterForm.cs
- VectorCollection.cs
- IisTraceListener.cs
- GridViewSelectEventArgs.cs
- TextChangedEventArgs.cs
- UIElement.cs
- Subtree.cs
- SourceCollection.cs
- filewebresponse.cs
- PersonalizableAttribute.cs
- SqlConnectionPoolGroupProviderInfo.cs
- RegexEditorDialog.cs
- FontConverter.cs
- xmlglyphRunInfo.cs
- DefaultSection.cs
- WebServiceData.cs
- Membership.cs
- PrintPreviewGraphics.cs
- ToolStripScrollButton.cs
- RowBinding.cs
- MetadataArtifactLoaderXmlReaderWrapper.cs
- CodeSnippetTypeMember.cs
- ParserOptions.cs
- XmlWrappingReader.cs
- AppendHelper.cs
- UpdateTranslator.cs
- SizeAnimationUsingKeyFrames.cs
- httpserverutility.cs
- ScriptModule.cs
- Graphics.cs
- TextServicesDisplayAttributePropertyRanges.cs
- httpstaticobjectscollection.cs
- ConfigurationManagerHelper.cs
- SortDescriptionCollection.cs
- SafeReversePInvokeHandle.cs
- DLinqColumnProvider.cs
- ParentControlDesigner.cs
- PreviewPageInfo.cs
- LineUtil.cs
- EntityContainerEntitySet.cs
- ClientEventManager.cs
- WinInet.cs
- ValidationPropertyAttribute.cs
- WebAdminConfigurationHelper.cs
- StringValueSerializer.cs
- DependsOnAttribute.cs
- XPathMultyIterator.cs
- Select.cs
- ItemCheckedEvent.cs
- ListComponentEditor.cs
- DeviceContext2.cs
- LinqDataSourceValidationException.cs
- XamlToRtfWriter.cs
- RegexNode.cs
- SubMenuStyleCollection.cs
- Cursor.cs
- OleDbDataAdapter.cs
- CompositeFontInfo.cs
- DesignerActionVerbList.cs
- HandlerBase.cs
- ErrorFormatter.cs
- ActivityLocationReferenceEnvironment.cs
- XmlLangPropertyAttribute.cs
- CaseCqlBlock.cs
- Dispatcher.cs
- CodeCatchClauseCollection.cs
- OutputCacheSettings.cs
- DrawingImage.cs
- PropertyTab.cs
- UserThread.cs
- LogPolicy.cs
- TypeUtil.cs
- WorkflowRuntimeSection.cs
- NamespaceTable.cs
- SingleResultAttribute.cs
- MatrixCamera.cs
- TrustLevelCollection.cs
- ProtocolsConfiguration.cs
- WindowsClaimSet.cs
- ScriptComponentDescriptor.cs
- ElementUtil.cs
- StandardToolWindows.cs
- TextParentUndoUnit.cs
- SqlError.cs
- ConfigurationException.cs
- XmlSchemaAny.cs