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
.