FsXaml will sometimes fail to report the real problem with a XAML file
BillHally opened this issue · 0 comments
Description
FsXaml will sometimes fail to report the real problem with a XAML file, instead reporting:
System.Xaml.XamlObjectWriterException: XAML Node Stream: Missing CurrentObject before EndObject.
Repro steps
- Edit the file
demos/WpfSimpleMvvmApplication/MainView.xaml
so that itsUserControl.Resources
section changes from:
<fsxaml:BooleanToCollapsedConverter x:Key="TrueToCollapsed" />
to:
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/FsXaml.Wpf;component/NoSuchResourceDictionary.xaml" />
</ResourceDictionary.MergedDictionaries>
<fsxaml:BooleanToCollapsedConverter x:Key="TrueToCollapsed" />
</ResourceDictionary>
This inserts a reference to a ResourceDictionary
that doesn't exist.
-
Edit the project file to change the
OutputType
fromWinExe
toExe
(so that if you run the app from the command line, the exception is visible) -
Build and run the application
Expected behavior
The exception raised provides the error message explaining what the problem is i.e. that the resource can't be found.
Unhandled Exception: System.Windows.Markup.XamlParseException: 'The invocation of the constructor on type 'Views.MainView' that matches the specified binding constraints threw an exception.' Line number '12' and line position '19'. ---> System.Xaml.XamlException: Error parsing XAML contents from MainView.xaml.
Error at line 0, column 0. ---> System.Xaml.XamlObjectWriterException: Set property 'System.Windows.ResourceDictionary.Source' threw an exception. ---> System.IO.IOException: Cannot locate resource 'nosuchresourcedictionary.xaml'.
Actual behavior
The exception reports "XAML Node Stream: Missing CurrentObject before EndObject".
Unhandled Exception: System.Windows.Markup.XamlParseException: 'The invocation of the constructor on type 'Views.MainView' that matches the specified binding constraints threw an exception.' Line number '12' and line position '19'. ---> System.Xaml.XamlObjectWriterException: XAML Node Stream: Missing CurrentObject before EndObject.
Explanation
This happens because the XamlObjectWriter
created in InjectXaml.from will sometimes (depending on the XAML being handled) raise an exception when it goes out of scope, preventing the useful exception the code raises from propagating.
Proposed fix
Explicity closing it in a try ... catch
block avoids this problem, allowing the useful exception to propagate as normal, i.e. inserting this:
try writer.Dispose() with _ -> ()
into the relevant exception handler:
use writer = new XamlObjectWriter(reader.SchemaContext, writerSettings)
try
while reader.Read() do
writer.WriteNode(reader)
with
| :? XamlException as xamlException -> // From writer
// Explicitly close the writer. Otherwise, as it is disposed
// when it goes out of scope it may raise an exception, which will
// prevent the propagation of the helpful one we're just about to raise
try writer.Dispose() with _ -> () <---------------------------- HERE
let message =
if reader.HasLineInfo then
sprintf "Error parsing XAML contents from %s.\n Error at line %d, column %d.\n Element beginning at line %d, column %d." file xamlException.LineNumber xamlException.LinePosition reader.LineNumber reader.LinePosition
else
sprintf "Error parsing XAML contents from %s.\n Error at line %d, column %d." file xamlException.LineNumber xamlException.LinePosition
raise <| XamlException(message, xamlException)
| :? System.Xml.XmlException as xmlE -> // From reader
let message =
if reader.HasLineInfo then
sprintf "Error parsing XAML contents from %s.\n Error at line %d, column %d.\n Element beginning at line %d, column %d." file xmlE.LineNumber xmlE.LinePosition reader.LineNumber reader.LinePosition
else
sprintf "Error parsing XAML contents from %s.\n Error at line %d, column %d." file xmlE.LineNumber xmlE.LinePosition
raise <| XamlException(message, xmlE)
writer.Result
|> ignore