Dispatcher.BeginInvoke(new Action(() => { dataGrid.SetBinding(ItemsControl.ItemsSourceProperty, new Binding() { Source = dataTable }); 

First, I fill the DataTable with the values ​​I need, and then bind the DataGrid to this table. However, I encountered such a problem as the interface hangs at the time of the SetBinding call.

The DataTable is populated in a separate thread, from which Dispatcher.BeginInvoke is called. IsAsync=True in Binding and various priorities of BeginInvoke do not help. What can be done?

  • Looked? What exactly slows down the UI? - tym32167
  • Dispatcher Invoke - D .Stark
  • this in itself does not slow anything down. I ask what specific code does your UI thread hang? - tym32167
  • Dispatcher Invoke in the sense of the most loaded. I indicated to the question description that the call to SetBinding "hangs" the UI. How exactly the call looks like, you can also see it there ... - D .Stark
  • Sample to play - Andrew NOP

1 answer 1

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.

Clock

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.

result

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

Add line

  • I have a large number of non-rows - columns (over 100). VirtualizingPanel.IsVirtualizing and VirtualizingPanel.VirtualizationMode do not help. - D .Stark
  • You explained the reason, but did not suggest a specific solution. - D .Stark
  • Maybe using a datagrid is just not possible? - D .Stark
  • I also have another question about the DataGrid. He is off topic, but I would not want to create a separate topic for this. - D .Stark
  • @ D.Stark See the update - tym32167