How do can I read through a baml stream that contains a ResourceDictionaory
using the Baml2006Reader
and without acually instantiating the the ResourceDictionary
?
I can ready through regular baml that just contains a UserControl just fine and I can examine the XAML tree using Baml2006Reader.NodeType
etc.
But once the reader hits a ResourceDictionary
, Baml2006Reader.Member.Name
is "DeferrableContent"
and Baml2006Reader.Value
contains a MemoryStream
that can not be parsed by another instance of Baml2006Reader
. I can't event instantiate the reader:
System.IO.EndOfStreamException occurred HResult=-2147024858
Message=Unable to read beyond the end of the stream. Source=mscorlib StackTrace: at System.IO.MemoryStream.InternalReadInt32() at System.Windows.Baml2006.Baml2006Reader.Process_Header() at WpfApplication10.AssemblyExtensions.Read(Stream stream, List`1 result) in d:\Documents\Visual Studio 2012\Projects\WpfApplication10\WpfApplication10\AssemblyExtensions.cs:line 84 InnerException:
It seems that whenever the Baml2006Reader encounters an element where Baml2006Reader.Member.Name
is "DeferrableContent"
it is followed by another node where BamlReader.Value
is a MemoryStream
. It seems that this stream only contains a baml fragment and does not have a header (that's why System.Windows.Baml2006.Baml2006Reader.Process_Header()
fails.)
So we need to tell the baml reader to read a baml fragment. This can be done be giving the reader an instance of System.Windows.Baml2006.Baml2006ReaderSettings
where the IsBamlFragment
property istrue
.
Unfortunately both the Baml2006ReaderSettings
class and the appropriate constructor of Baml2006Reader
are internal. So we need to resort to reflection:
private static string PresentationFrameworkAssemblyName = "PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35";
private static Baml2006Reader CreateBamlFragmentReader(MemoryStream substream, XamlSchemaContext schemaContext)
{
var bamlSettingsType =
Type.GetType(
"System.Windows.Baml2006.Baml2006ReaderSettings, " + PresentationFrameworkAssemblyName);
var settingsCtor =
bamlSettingsType.GetConstructor(Type.EmptyTypes);
var bamlSettings = settingsCtor.Invoke(null);
var isBamlFragmentProp = bamlSettingsType.GetProperty("IsBamlFragment",
BindingFlags.NonPublic |
BindingFlags.Instance);
isBamlFragmentProp.SetValue(bamlSettings, true, null);
var ctor = typeof (Baml2006Reader).GetConstructor(
BindingFlags.Instance | BindingFlags.NonPublic,
null,
new[]
{
typeof (Stream),
Type.GetType(
"System.Windows.Baml2006.Baml2006SchemaContext, " + PresentationFrameworkAssemblyName),
bamlSettingsType
},
null);
return (Baml2006Reader)ctor.Invoke(new[] { substream, schemaContext, bamlSettings });
}
usage:
var substream = reader.Value as MemoryStream;
if (substream != null)
{
using (var subReader = CreateBamlFragmentReader(substream, reader.SchemaContext))
{
// continue reading with subReader
}
}
I know this is rather fragile code and very hackish, but what the heck - it works (for me, currently)!