Search code examples
xmlwpfxpathbindingxmldataprovider

XmlDataProvider binding controls with element children


In the following code, I'm unable to reach the parameters elements in the XML file. The ListBox displays all the instructions in the XML file. The ComboBox is supposed to display all the Parameters element related to the selected instruction in the ListBox. The content of the ComboBox is where I have a problem. Nothing is displayed with the provided code below.

<Window x:Class="LinqToXmlDataBinding.L2XDBForm"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WPF Data Binding using LINQ-to-XML" Height="750" Width="500" ResizeMode="CanResize">

<Window.Resources>
    <XmlDataProvider x:Key="XMLInstructionsMapping" Source="XMLMapping.xml"       XPath="InstructionsMapping/Instruction"/>

    <!-- Template for use in Books List listbox. -->
    <DataTemplate x:Key="InstructionTemplate">
        <StackPanel Orientation="Horizontal">
            <TextBlock Margin="3" Text="{Binding XPath=@Name}"/>
            <TextBlock Margin="3" Text="-"/>
            <TextBlock Margin="3" Text="ConvertedFrom: "/>
            <TextBlock Margin="3" Text="{Binding XPath=@ConvertedFrom}"/>
        </StackPanel>          
    </DataTemplate>
    <DataTemplate x:Key="ParamterTemplate">
        <StackPanel Orientation="Horizontal">
            <TextBlock Margin="3" Text="Name: "/>
            <TextBlock Margin="3" Text="{Binding XPath=@Name}"/>
            <TextBlock Margin="3" Text="-"/>
            <TextBlock Margin="3" Text="DataType: "/>
            <TextBlock Margin="3" Text="{Binding XPath=@DataType}"/>
            <TextBlock Margin="3" Text="-"/>
            <TextBlock Margin="3" Text="Direction: "/>
            <TextBlock Margin="3" Text="{Binding XPath=@Direction}"/>
        </StackPanel>
    </DataTemplate>
</Window.Resources>

<!-- Main visual content container -->
<StackPanel Background="lightblue" DataContext="{Binding Source={StaticResource XMLInstructionsMapping}}">

    <!-- List box to display all instructions section -->
    <DockPanel Margin="5">
        <Label  Background="Gray" FontSize="12" BorderBrush="Black" BorderThickness="1" FontWeight="Bold">Instruction List
            <Label.LayoutTransform>
                <RotateTransform Angle="90"/>
            </Label.LayoutTransform>
        </Label>

        <ListBox x:Name="lbBooks" Height="200" Width="415" 
                 ItemsSource="{Binding Source={StaticResource XMLInstructionsMapping}}"
                 ItemTemplate ="{StaticResource InstructionTemplate}"                    
                 IsSynchronizedWithCurrentItem="True" SelectionMode="Single" Visibility="Visible">
        </ListBox>            
    </DockPanel>

    <Label  Background="Gray" FontSize="12" BorderBrush="Black" BorderThickness="1" FontWeight="Bold">Parameter List
    </Label>
    <!-- Combobox to display all selected instruction's parameters -->
    <ComboBox x:Name="lstParams" Margin="5" Height="30" Width="415"
                 ItemsSource="{Binding Source={StaticResource XMLInstructionsMapping}, XPath=InstructionsMapping/Instruction/Parameters/Parameter}"
                 ItemTemplate ="{StaticResource ParamterTemplate}"                    
                 IsSynchronizedWithCurrentItem="True" Visibility="Visible">
    </ComboBox>  
</StackPanel>

Here is the XML file I'm binding to:

<?xml version="1.0" encoding="utf-8"?>
<InstructionsMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <Instruction Name="XIE" ConvertedFrom="XIC" >
    <Parameters>
      <Parameter Name="In1" Direction="Input" DataType="Bool" />
      <Parameter Name="In1" Direction="Input" DataType="Bool" />
      <Parameter Name="In1" Direction="Input" DataType="Bool" />
      <Parameter Name="In1" Direction="Input" DataType="Bool" />
      <Parameter Name="In1" Direction="Input" DataType="Bool" />
    </Parameters>
  </Instruction>
  <Instruction Name="XIC" ConvertedFrom="XIC" >
    <Parameters>
      <Parameter Name="In1" Direction="Input" DataType="Bool" />
      <Parameter Name="In1" Direction="Input" DataType="Bool" />
      <Parameter Name="In1" Direction="Input" DataType="Bool" />
      <Parameter Name="In1" Direction="Input" DataType="Bool" />
      <Parameter Name="In1" Direction="Input" DataType="Bool" />
    </Parameters>
  </Instruction>
</InstructionsMapping>

I'm trying to populate the ComboBox with the Parameters of the selected instruction in the ListBox

I'm using an XmlDataProvider to bind with the XML file. I can't figure out the XPath expression (maybe I'm missing something else) that I need in order to get the children of the instruction elements displayed.

Any help would be welcomed on the necessary XPath expression to do that.


Solution

  • First, remove XPath from resource XMLInstructionsMapping. Declare it like this:

    <XmlDataProvider x:Key="XMLInstructionsMapping" Source="XMLMapping.xml"/>
    

    Explanation - Let XMLDataProvider load complete XML file instead of specific node in XML.


    Second, set XPath on ItemsSource of ListBox like this:

    <ListBox x:Name="lbBooks" Height="200" Width="415" 
             ItemsSource="{Binding Source={StaticResource XMLInstructionsMapping},
                                   XPath=InstructionsMapping/Instruction}" <-- HERE
             ItemTemplate ="{StaticResource InstructionTemplate}"                    
             IsSynchronizedWithCurrentItem="True" SelectionMode="Single" 
             Visibility="Visible">
    </ListBox>
    

    Explanation - Move the XPath from resource to here to get specific node.


    Third, update XPath in comboBox ItemsSource to this:

    <ComboBox x:Name="lstParams" Margin="5" Height="30" Width="415"
            ItemsSource="{Binding Source={StaticResource XMLInstructionsMapping}, 
                           XPath=InstructionsMapping/Parameters/Parameter}" <-- HERE
            ItemTemplate ="{StaticResource ParamterTemplate}"                    
            IsSynchronizedWithCurrentItem="True" Visibility="Visible">
    </ComboBox>
    

    Explanation - Set the correct XPath to point to the nodes you want comboBox to populate with.


    UPDATE

    In case you want to show only items corresponding to item selected in listBox, you can bind using ElementName with ChildNodes like this:

    <ComboBox x:Name="lstParams" Margin="5" Height="30" Width="415"
            ItemsSource="{Binding Path=SelectedItem.ChildNodes[0].ChildNodes, 
                                  ElementName=lbBooks}"
            ItemTemplate ="{StaticResource ParamterTemplate}"                    
            IsSynchronizedWithCurrentItem="True" Visibility="Visible">
    </ComboBox>