Could repeat your experiment - really slowly working out. But it is not the binding that works slowly, but the code that performs the data AFTER the binding.
For example:
public class MyWnd : Window { DataGrid dg; public MyWnd() { dg = new System.Windows.Controls.DataGrid() {AutoGenerateColumns = true}; var bt = new Button() { Content = "Load" }; bt.Click += Load; var sp = new StackPanel() { Orientation = Orientation.Vertical}; sp.Children.Add(bt); sp.Children.Add(dg); this.Content = sp; } public async void Load(object sender, EventArgs e) { DataTable dt = null; await Task.Run(() => { dt = new DataTable(); for (var i = 0; i < 10; i++) { dt.Columns.Add($"Column{i}"); } for (var i = 0; i < 1000; i++) { var row = dt.NewRow(); for (var j = 0; j < 10; j++) { var col = $"Column{j}"; row[col] = i + j; } dt.Rows.Add(row); } }); var sw = new Stopwatch(); sw.Start(); dg.SetBinding(ItemsControl.ItemsSourceProperty, new Binding() { Source = dt, Mode = BindingMode.OneWay }); sw.Stop(); MessageBox.Show($"Elapsed: {sw.Elapsed}"); } }
When you click the Load button, this code will first create a table, bind it, stop the clock, and after that (but before displaying the mesadbox) it will hang. That's why my watch always shows a very small time interval.

I think this is related to the DataGrid itself. He either does not know how to virtualize and therefore tries to render all the lines at once (at 10 lines he works quickly), or even without that he has performance problems, for example, one , two . Although most likely this and that.
UPD
To overcome this, you need to properly enable virtualization and put the grid in the control, which will limit its size. It looks like this:
public class MyWnd : Window { DataGrid dg; public MyWnd() { dg = new System.Windows.Controls.DataGrid() { AutoGenerateColumns = true, EnableRowVirtualization = true, EnableColumnVirtualization = true }; VirtualizingPanel.SetIsVirtualizing(dg, true); ScrollViewer.SetCanContentScroll(dg, true); Grid.SetRow(dg, 1); var bt = new Button() { Content = "Load" }; bt.Click += Load; var grid = new Grid(); grid.RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto }); grid.RowDefinitions.Add(new RowDefinition() { }); grid.Children.Add(bt); grid.Children.Add(dg); this.Content = grid; } public async void Load(object sender, EventArgs e) { DataTable dt = null; await Task.Run(() => { dt = new DataTable(); for (var i = 0; i < 10; i++) { dt.Columns.Add($"Column{i}"); } for (var i = 0; i < 1000; i++) { var row = dt.NewRow(); for (var j = 0; j < 10; j++) { var col = $"Column{j}"; row[col] = i + j; } dt.Rows.Add(row); } }); var sw = new Stopwatch(); sw.Start(); dg.SetBinding(ItemsControl.ItemsSourceProperty, new Binding() { Source = dt }); sw.Stop(); MessageBox.Show($"Elapsed: {sw.Elapsed}"); } }
Since the size of the control is limited and virtualization is enabled, the grid will not try to draw everything at once, but will draw only the visible part.

FRT 2
Add line
public class MyWnd : Window { DataGrid dg; public MyWnd() { dg = new System.Windows.Controls.DataGrid() { AutoGenerateColumns = true, EnableRowVirtualization = true, EnableColumnVirtualization = true }; VirtualizingPanel.SetIsVirtualizing(dg, true); ScrollViewer.SetCanContentScroll(dg, true); Grid.SetRow(dg, 1); var bt = new Button() { Content = "Load" }; bt.Click += Load; var btrow = new Button() { Content = "AddRow" }; btrow.Click += AddRow; var sp = new StackPanel() { Orientation = Orientation.Horizontal }; sp.Children.Add(bt); sp.Children.Add(btrow); var grid = new Grid(); grid.RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto }); grid.RowDefinitions.Add(new RowDefinition() { }); grid.Children.Add(sp); grid.Children.Add(dg); this.Content = grid; } DataTable dt = null; Random r = new Random(); public async void AddRow(object sender, EventArgs e) { var newRow = dt.NewRow(); for (var j = 0; j < 10; j++) { var col = $"Column{j}"; newRow[col] = r.Next(10); } dt.Rows.Add(newRow); } public async void Load(object sender, EventArgs e) { await Task.Run(() => { dt = new DataTable(); for (var i = 0; i < 10; i++) { dt.Columns.Add($"Column{i}"); } for (var i = 0; i < 10; i++) { var row = dt.NewRow(); for (var j = 0; j < 10; j++) { var col = $"Column{j}"; row[col] = i + j; } dt.Rows.Add(row); } }); var sw = new Stopwatch(); sw.Start(); dg.SetBinding(ItemsControl.ItemsSourceProperty, new Binding() { Source = dt }); sw.Stop(); MessageBox.Show($"Elapsed: {sw.Elapsed}"); } }
Result
