views:

201

answers:

1

I am struggling to work out how to use default namespaces with XmlDataProvider and XPath bindings.

There's an ugly answer using local-name <Binding XPath="*[local-name()='Name']" /> but that is not acceptable to the client who wants this XAML to be highly maintainable.

The fallback is to force them to use non-default namespaces in the report XML but that is an undesirable solution.

The XML report file looks like the following. It will only work if I remove xmlns="http://www.acme.com/xml/schemas/report so there is no default namespace.

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type='text/xsl' href='PreviewReportImages.xsl'?>
<Report xsl:schemaLocation="http://www.acme.com/xml/schemas/report BlahReport.xsd" xmlns:xsl="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.acme.com/xml/schemas/report"&gt;
  <Service>Muncher</Service>
  <Analysis>
    <Date>27 Apr 2010</Date>
    <Time>0:09</Time>
    <Authoriser>Service Centre Manager</Authoriser>

Which I am presenting in a window with XAML:

<Window x:Class="AcmeTest.ReportPreview"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     Title="ReportPreview" Height="300" Width="300" >
    <Window.Resources>
        <XmlDataProvider x:Key="Data"/>
    </Window.Resources>
    <StackPanel Orientation="Vertical" DataContext="{Binding Source={StaticResource Data}, XPath=Report}">
        <TextBlock Text="{Binding XPath=Service}"/>
    </StackPanel>
</Window>

with code-behind used to load an XmlDocument into the XmlDataProvider (seems the only way to have loading from a file or object varying at runtime).

public partial class ReportPreview : Window
{
    private void InitXmlProvider(XmlDocument doc)
    {
        XmlDataProvider xd = (XmlDataProvider)Resources["Data"];
        xd.Document = doc;
    }

    public ReportPreview(XmlDocument doc)
    {
        InitializeComponent();
        InitXmlProvider(doc);
    }

    public ReportPreview(String reportPath)
    {
        InitializeComponent();

        var doc = new XmlDocument();
        doc.Load(reportPath);
        InitXmlProvider(doc);
    }
}
A: 

I hadn't realised that I don't need to add a prefix to the client XML data, just use a prefix in my XPath expressions that maps to the same URI as the default namespace (obvious when I slept on it!).

So, the fix was to add a namespace mapping as shown here, note the use of the r: prefix on the elements.

<Window x:Class="AcmeTest.ReportPreview"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     Title="ReportPreview" Height="300" Width="300" >
    <Window.Resources>
        <XmlDataProvider x:Key="Data">
            <XmlDataProvider.XmlNamespaceManager>
                <XmlNamespaceMappingCollection>
                    <XmlNamespaceMapping 
                       Uri="http://www.acme.com/xml/schemas/report" 
                       Prefix="r" />
                </XmlNamespaceMappingCollection>
            </XmlDataProvider.XmlNamespaceManager>
        </XmlDataProvider>
    </Window.Resources>
    <StackPanel Orientation="Vertical" DataContext="{Binding Source={StaticResource Data}, XPath=Report}">
        <TextBlock Text="{Binding XPath=r:Service}"/>
        <TextBlock Text=" "/>
        <TextBlock Text="{Binding XPath=r:Name/r:Last}"/>
    </StackPanel>
</Window>
Andy Dent