views:

1173

answers:

2

I want to use data binding with an XML document to populate a simple form that shows details about a list of people. I've got it all set up and working like so right now:

<Window x:Class="DataBindingSample.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1">
<Window.Resources>
 <XmlDataProvider x:Key="xmlProvider" XPath="People" Source="c:\someuri.xml"/>
</Window.Resources>
<Grid>        
    <ListBox Name="personList" ItemsSource="{Binding Source={StaticResource xmlProvider}, XPath=Person}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding XPath=Name}" />
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

    <GroupBox Header="GroupBox" Name="groupBox1" DataContext="{Binding ElementName=personList, Path=SelectedItem}">
        <Grid>
   <TextBox Name="nameText" Text="{Binding XPath=Name}"/>
   <ComboBox Name="genderCombo" Text="{Binding XPath=Gender}">
                <ComboBoxItem>Male</ComboBoxItem>
                <ComboBoxItem>Female</ComboBoxItem>
            </ComboBox>
        </Grid>
    </GroupBox>
</Grid>
</Window>

(All position/layout elements have been removed for clarity)

Now this works great! If I provide it with some XML that matches the paths provided I get a list of names in the listbox that show both the name and gender in the appropriate fields when clicked. The problem comes when I start to try and use namespaces in my XML source. The XAML then changes to look like this:

<Window x:Class="DataBindingSample.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1">
<Window.Resources>
 <XmlNamespaceMappingCollection x:Key="namespaceMappings">
  <XmlNamespaceMapping Uri="http://www.mynamespace.com" Prefix="mns"/>
 </XmlNamespaceMappingCollection>
 <XmlDataProvider x:Key="xmlProvider" XmlNamespaceManager="{StaticResource namespaceMappings}" XPath="mns:People" Source="c:\someuriwithnamespaces.xml"/>
</Window.Resources>
<Grid>        
    <ListBox Name="personList" ItemsSource="{Binding Source={StaticResource xmlProvider}, XPath=mns:Person}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding XPath=mns:Name}" />
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

    <GroupBox Header="GroupBox" Name="groupBox1" DataContext="{Binding ElementName=personList, Path=SelectedItem}">
        <Grid>
   <TextBox Name="nameText" Text="{Binding XPath=mns:Name}"/>
   <ComboBox Name="genderCombo" Text="{Binding XPath=mns:Gender}">
                <ComboBoxItem>Male</ComboBoxItem>
                <ComboBoxItem>Female</ComboBoxItem>
            </ComboBox>
        </Grid>
    </GroupBox>
</Grid>
</Window>

With this code (and the appropriately namespaced xml, of course) the Listbox still displays the names properly, but clicking on those names no longer updates the Name and Gender fields! My suspicion is that somehow the xml namespace is reacting adversely to the groupbox's DataContext, but I'm not sure why or how. Does anyone know how to use XML namespaces in this context?

+1  A: 

You could use local names in your XPath queries like this:

 <TextBox Name="nameText">
    <TextBox.Text>
       <Binding XPath="*[local-name()='Name']" />
    </TextBox.Text>
 </TextBox>
aogan
Okay, that works but I'd like to know a bit more about why before I give the "answered" stamp. I'm resonably new to data binding, so I'm not familiar with some of the more advanced synatx. What exactly is local-name() doing here?
Toji
local-name() is a XPath function which let you select the xml element using only their local name instead of full name (namespace + localname)
aogan
Ah, thank you! That makes sense now. This is a workable solution, but I'd still love to know why it is that I lose the ability to query using the namespace at this level. Thanks for the help, though!
Toji
My apologies, but while your answer was helpful a more comprehensive one was given to me on another form. I've reproduced it here and, for the sake of community reference, am going to mark it as the accepted answer. Still, thank you for your help, and if I could rank your answer any higher I would!
Toji
A: 

I also asked this question on the MSDN WPF forms. Marco Zhou answered me with this, which is ultimately the answer I was seeking. I've reproduced it here for the benefit of anyone looking for the same answer:

This works:

    <XmlDataProvider x:Key="dataProvider"
                     XmlNamespaceManager="{StaticResource namespaceMappings}"
                     XPath="p:players/p:player">
        <x:XData>
            <p:players xmlns:p="http://www.footballism.com/2005/SoccerPlayers"&gt;
                <p:player>
                    <p:fullName>Sebastian Batistuta</p:fullName>
                    <p:age>26</p:age>
                </p:player>
                <p:player>
                    <p:fullName>Andriey Shevchenko</p:fullName>
                    <p:age>30</p:age>
                </p:player>
                <p:player>
                    <p:fullName>Paviel Nedved</p:fullName>
                    <p:age>21</p:age>
                </p:player>
                <p:player>
                    <p:fullName>David Beckham</p:fullName>
                    <p:age>19</p:age>
                </p:player>
            </p:players>
        </x:XData>
    </XmlDataProvider>
</Page.Resources>
<StackPanel>
    <TextBlock
        Text="{Binding XPath=p:fullName}"
        FontWeight="Bold"
        Binding.XmlNamespaceManager="{StaticResource namespaceMappings}"
        DataContext="{Binding ElementName=listBox, Path=SelectedItem}"/>
    <ListBox ItemsSource="{Binding Source={StaticResource dataProvider}}"
             x:Name="listBox"
             DisplayMemberPath="p:fullName">
    </ListBox>
</StackPanel> </Page>

I suppose after looking at the code, you should be able to understand why it works after specify the Binding.XmlNamespaceManager attached property for TextBlock.

ListBox is data bound to a data provider which has xml namespace mapping information, but the binding on the TextBlock doesn't have this information, that's why it fails.

Actually, when doing master detail data binding, it's more appropriate to do something like the following:

    <XmlDataProvider x:Key="dataProvider"
                     XmlNamespaceManager="{StaticResource namespaceMappings}"
                     XPath="p:players/p:player">
        <x:XData>
            <p:players xmlns:p="http://www.footballism.com/2005/SoccerPlayers"&gt;
                <p:player>
                    <p:fullName>Sebastian Batistuta</p:fullName>
                    <p:age>26</p:age>
                </p:player>
                <p:player>
                    <p:fullName>Andriey Shevchenko</p:fullName>
                    <p:age>30</p:age>
                </p:player>
                <p:player>
                    <p:fullName>Paviel Nedved</p:fullName>
                    <p:age>21</p:age>
                </p:player>
                <p:player>
                    <p:fullName>David Beckham</p:fullName>
                    <p:age>19</p:age>
                </p:player>
            </p:players>
        </x:XData>
    </XmlDataProvider>
</Page.Resources>
<StackPanel DataContext="{Binding Source={StaticResource dataProvider}}">
    <TextBlock
        Text="{Binding XPath=p:fullName}"
        FontWeight="Bold"/>
    <ListBox ItemsSource="{Binding}"
             x:Name="listBox"
             DisplayMemberPath="p:fullName"
             IsSynchronizedWithCurrentItem="True">
    </ListBox>
</StackPanel> </Page>

Hope this clears things up a little bit.

Toji