XAMLServices class and basic XAML reading or writing

XamlServices is a class provided by .NET that can be used to address XAML scenarios that don't require specific access to the XAML node stream or to XAML type system information obtained from those nodes. XamlServices API can be summarized as follows: Load or Parse to support a XAML load path, Save to support a XAML save path, and Transform to provide a technique that joins a load path and save path. Transform can be used to change from one XAML schema to another. This topic summarizes each of these API classifications and describes the differences between particular method overloads.

Load

Various overloads of Load implement the complete logic for a load path. The load path uses XAML in some form and outputs a XAML node stream. Most of these load paths use XAML in an encoded XML text-file form. However, you can also load a general stream, or you can load a preloaded XAML source that is already contained in a different XamlReader implementation.

The simplest overload for most scenarios is Load(String). This overload has a fileName parameter that is simply the name of a text file that contains the XAML to load. This is appropriate for application scenarios such as full trust applications that have previously serialized state or data to the local computer. This is also useful for frameworks where you are defining the application model and want to load one of the standard files that defines application behavior, starting UI, or other framework-defined capabilities that use XAML.

Load(Stream) has similar scenarios. This overload might be useful if you have the user choose files to load, because a Stream is a frequent output of other System.IO APIs that can access a file system. Or you could be accessing XAML sources through asynchronous downloads or other network techniques that also provide a stream. (Loading from a stream or user-selected source may have security implications. For more information, see XAML Security Considerations.)

Load(TextReader) and Load(XmlReader) are overloads that rely on readers of formats from previous versions of .NET. To use these overloads, you should have already created a reader instance and used its Create API to load the XAML in the relevant form (text or XML). If you have already moved record pointers in the other readers or performed other operations with them, this is not important. The load path logic from Load always processes the entire XAML input from the root down. The following scenarios might warrant the use of these overloads:

  • Design surfaces where you provide simple XAML editing capability from an existing XML-specific text editor.

  • Variants of the core System.IO scenarios, where you use the dedicated readers to open files or streams. Your logic performs rudimentary checking or processing of the contents before it tries to load as XAML.

You can either load a file or stream, or you can load an XmlReader, TextReader, or XamlReader that wraps your XAML input by loading with the reader's APIs.

Internally, each of the preceding overloads is ultimately Load(XmlReader), and the passed XmlReader is used to create a new XamlXmlReader.

The Load signature that provides for more advanced scenarios is Load(XamlReader). You can use this signature for one of the following cases:

  • You've defined your own implementation of a XamlReader.

  • You need to specify settings for XamlReader that vary from the default settings.

Examples of non-default settings:

AllowProtectedMembersOnRoot
BaseUri
IgnoreUidsOnPropertyElements
LocalAssembly
ValuesMustBeString.

The default reader for XamlServices is XamlXmlReader. If you provide your own XamlXmlReader with settings, the following are properties to set non-default XamlXmlReaderSettings:

CloseInput
SkipXmlCompatibilityProcessing
XmlLang
XmlSpacePreserve

Parse

Parse is like Load because it is a load path API that creates a XAML node stream from XAML input. However, in this case, the XAML input is provided directly as a string that contains all the XAML to load. Parse is a lightweight approach that is more appropriate for application scenarios than framework scenarios. For more information, see Parse. Parse is just a wrapped Load(XmlReader) call that involves a StringReader internally.

Save

Various overloads of Save implement the save path. All of the Save methods all take an object graph as input and produce output as a stream, file, or XmlWriter/TextWriter instance.

The input object is expected to be the root object of some object representation. This might be the single root of a business object, the root of an object tree for a page in a UI scenario, the working editing surface from a design tool, or other root object concepts that are appropriate for scenarios.

In many scenarios, the object tree that you save is related to an original operation that loaded XAML either with Load or with other API implemented by a framework/application model. There might be differences captured in the object tree that are due to state changes, changes where your application captured runtime settings from a user, changed XAML because your application is a XAML design surface, etc. With or without changes, the concept of first loading XAML from markup and then saving it again and comparing the two XAML markup forms is sometimes referred as a round-trip representation of the XAML.

The challenge with saving and serializing a complex object that is set in a markup form is in achieving a balance between full representation without information loss, versus verbosity that makes the XAML less human-readable. Moreover, different customers for XAML might have different definitions or expectations for how that balance should be set. The Save APIs represent one definition of that balance. The Save APIs use available XAML schema context and the default CLR-based characteristics of XamlType, XamlMember, and other XAML intrinsic and XAML type system concepts to determine where certain XAML node stream constructs can be optimized when they are saved back into markup. For example, XamlServices save paths can use CLR-based default XAML schema context to resolve XamlType for objects, can determine a XamlType.ContentProperty, and then can omit property element tags when they write the property to the XAML content of the object.

Transform

Transform converts or transforms XAML by linking a load path and a save path as a single operation. A different schema context or different backing type system can be used for XamlReader and XamlWriter, which is what influences how the resulting XAML is transformed. This works well for broad transform operations.

For operations that rely on examining each node in a XAML node stream, you typically do not use Transform. Instead you need to define your own load path-save path operation series and interject your own logic. In one of the paths, use a XAML reader/XAML writer pair around your own node loop. For example, load the initial XAML using XamlXmlReader and step into the nodes with successive Read calls. Operating at the XAML node stream level you can now adjust individual nodes (types, members, other nodes) to apply a transformation, or leave the node as-is. Then you send the node onwards to the relevant Write API of a XamlObjectWriter and write out the object. For more information, see Understanding XAML Node Stream Structures and Concepts.

See also