简体   繁体   中英

Several XPath for one XmlDataProvider WPF

I have 2 DataGrid and one XmlDataProvider for XML-file. XML-file structure looks like this:

<Setting>
  <Element Name="..." Offset="..." ID="...">
    <Item Name="..." Type="..." Count="..." ID="..." />
    <Item Name="..." Type="..." Count="..." ID="..." />
    <Item Name="..." Type="..." Count="..." ID="..." />
    ...
  </Element>
  <Element Name="..." Offset="..." ID="...">
    <Item Name="..." Type="..." Count="..." ID="..." />
    <Item Name="..." Type="..." Count="..." ID="..." />
  </Element>
  ...
</Setting>

I need to display the values of attributes of all Elements in first DataGrid , and the values of attributes of all Items in second DataGrid using same XmlDataProvider .

XAML to display the values of attributes of all Elements in first DataGrid :

...
<Grid.DataContext>
  <XmlDataProvider x:Name="xml_setting" XPath="/Setting/Element"/>
</Grid.DataContext>
<DataGrid ItemsSource="{Binding}">
  <DataGrid.Columns>
    <DataGridTextColumn Header="Name" Width="*" Binding="{Binding XPath=@Name}"/>
    <DataGridTextColumn Header="Offset" Width="80" Binding="{Binding XPath=@Offset}"/>
    <DataGridTextColumn Header="ID" Width="80" Binding="{Binding XPath=@ID}"/>
  </DataGrid.Columns>
</DataGrid>
...

I tried to set the XmlDataProvider XPath value in "/Setting" and the Columns XPath values in "/Element/@Name", "/Element/@Offset" and "/Element/@ID", but only first Element is displayed.

How can I working with one XmlDataProvider to binding to different columns of different DataGrids to display the values of attributes of different nodes of the XML-file?

The problem is you have to set the default namespace of the XML root node to empty string:

<Setting xmlns="">

Otherwise it will use the xmlns assigned in the window scope (which is of course "http://schemas.microsoft.com/winfx/2006/xaml/presentation" ) and the elements' Path will be incorrect.

To fill data for the second grid, you can bind the ItemsSource to the first DataGrid , the Path should be SelectedItem , this is exactly an XmlElement which is an IEnumerable . So you can use XPath to bind each attribute of each Item element to the corresponding DataGridTextColumn like this:

<!-- the first Grid -->
<DataGrid ItemsSource="{Binding}" AutoGenerateColumns="False" Name="grid1">
  <DataGrid.Columns>
    <DataGridTextColumn Header="Name" Width="*" Binding="{Binding XPath=@Name}"/>
    <DataGridTextColumn Header="Offset" Width="80" Binding="{Binding XPath=@Offset}"/>
    <DataGridTextColumn Header="ID" Width="80" Binding="{Binding XPath=@ID}"/>
  </DataGrid.Columns>
</DataGrid>
<!-- the second Grid -->
<DataGrid Grid.Column="1" ItemsSource="{Binding ElementName=grid1,Path=SelectedItem}"
          AutoGenerateColumns="False">
  <DataGrid.Columns>    
    <DataGridTextColumn Header="Name" Width="*" Binding="{Binding XPath=@Name}"/>
    <DataGridTextColumn Header="Type" Width="100" Binding="{Binding XPath=@Type}"/>
    <DataGridTextColumn Header="Count" Width="80" Binding="{Binding XPath=@Count}"/>
    <DataGridTextColumn Header="ID" Width="80" Binding="{Binding XPath=@ID}"/>      
  </DataGrid.Columns>
</DataGrid>

Update : The above code for the second DataGrid works OK for reading (showing) data but it does not support modifying. It's because of the way we bind the data, in fact the underlying collection is ChildNodes which is actually an XmlNodeList , this collection just implements IEnumerable interface. So it does not support editing. The underlying collection should implement the IEditableCollectionView interface. By changing the way of binding data we can set the underlying collection as an IEditableCollectionView . In fact the way XPath queries data can help us get an IEditableCollectionView . So in this case we try using XPath to get the underlying collection (instead of accessing via ChildNodes , which is implicitly enumerable via the SelectedItem of type XmlElement ):

<DataGrid Grid.Column="1" 
          DataContext="{Binding ElementName=grid1,Path=SelectedItem}"
          ItemsSource="{Binding XPath=Item}"
          AutoGenerateColumns="False">
    <!-- .... -->
</DataGrid>

Now it works like a charm (tested).

Update : To filter the Elements by Name, you have to use property element syntax (not attribute syntax) like this:

<DataGrid Grid.Column="1" AutoGenerateColumns="False">
    <DataGrid.ItemsSource>
        <Binding XPath="Setting/Element[@Name='SomeName']"/>
    </DataGrid.ItemsSource>
    <!-- ...... -->
</DataGrid>

Note that the above code is just an example of how to specify the XPath , the actual expression depends on the implicit Source .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM