(Edited: My actual problem turns out to be about setting the ItemsSource correctly, not the bindings for each DataGridTextColumn!)
Description of Problem
I am struggling with a specific data binding task where I want to bind XML data (using LINQ, parsed as XElement) to a WPF DataGrid (not a DataGridView) so that it can be edited by the user. I think the very core problem it probably boils down to is this:
What is the equivalent in C# code for the following XAML statement?
<DataGrid x:Name="dtaGrid" ItemsSource="{Binding Path=Elements[track]}"/>
I thought, it should be:
dtaGrid.ItemsSource = xml.Elements("track");
Unfortunately, the C# statement doesn't work as expected: While the data is being displayed in the DataGrid, a System.InvalidOperationException ("EditItem is not allowed for this view") occurs once the user double-clicks a DataGrid cell to edit its content. Using the XAML variant, the data is both shown and editable without error, and the changes are reflected in the XML source.
Since I don't know the actual XML file's structure at design time, I want to dynamically set the ItemSource at runtime in code behind (and thus be able to change the path used for binding).
Working Example
Here is a working example (with ItemsSource binding being done in XAML). Sorry for long code quotes, I just thought it might help clarify the problem better in context.
MainWindow.xaml (note how the DataGrid's ItemsSource is explicitly bound here - I need to do be able to change this binding at runtime in code behind):
<Window x:Class="linq_xml.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:linq_xml" mc:Ignorable="d"
Title="MainWindow" Width="1000" Height="700" >
<Grid Margin="8">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<DataGrid x:Name="dtaGrid" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"
ItemsSource="{Binding Path=Elements[track]}" AutoGenerateColumns="False"/>
<Button x:Name="btn_Save" Grid.Row="1" Grid.Column="0"
Width="100" HorizontalAlignment="Left" Margin="0 8 0 0"
Content="Save XML" Click="Btn_Save_Click"/>
</Grid>
</Window>
MainWindow.xaml.cs (note the uncommented ItemsSource statement):
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Xml.Linq;
namespace linq_xml
{
public partial class MainWindow : Window
{
private XElement xml;
private readonly string filepath = @"D:\SynologyDrive\Dev\C#\linq-xml\XML-Beispiele\random.xml";
public MainWindow()
{
InitializeComponent();
xml = XElement.Load(filepath); // load xml file
dtaGrid.DataContext = xml; // set LINQ to XML as data context
/* If the following line is used rather than the ItemsSource being bound done in XAML,
* it doesn't work as expected: Once the user tries to edit a cell at runtime,
* a System.InvalidOperationException ("EditItem is not allowed for this view") occurs. */
// dtaGrid.ItemsSource = xml.Elements("track");
List<DataGridTextColumn> columns = new List<DataGridTextColumn>();
columns.Add(new DataGridTextColumn());
columns[^1].Header = "Artist";
columns[^1].Binding = new Binding("Element[artist_name].Value");
columns.Add(new DataGridTextColumn());
columns[^1].Header = "Album";
columns[^1].Binding = new Binding("Element[album_name].Value");
columns.Add(new DataGridTextColumn());
columns[^1].Header = "Duration";
columns[^1].Binding = new Binding("Element[duration].Value");
foreach (DataGridTextColumn c in columns)
{
dtaGrid.Columns.Add(c);
}
}
private void Btn_Save_Click(object sender, RoutedEventArgs e)
{
xml.Save(filepath);
}
}
}
example.xml:
<?xml version="1.0" encoding="utf-8"?>
<data>
<track>
<id>1337</id>
<name>Wonderful World</name>
<duration>128</duration>
<artist_id>13</artist_id>
<artist_name>Trumpet</artist_name>
<album_id>22</album_id>
<album_name>Nice People</album_name>
</track>
<track>
<id>4711</id>
<name>Colorful World</name>
<duration>256</duration>
<artist_id>1</artist_id>
<artist_name>Pink</artist_name>
<album_id>11</album_id>
<album_name>I like the blues</album_name>
</track>
<track>
<id>0815</id>
<name>World</name>
<duration>512</duration>
<artist_id>9</artist_id>
<artist_name>CNN</artist_name>
<album_id>33</album_id>
<album_name>My Finger Is On The Button</album_name>
</track>
</data>