user1471467
user1471467

Reputation: 33

WPF DataGrid and multithreading

I have a DataGrid that I fill with a DataView (using DataContext). I have tried to do that in a separate thread but the UI still freezes. I want to prevent the UI from freezing when populating the DataGrid.

Here is the code I made so far:

private void btnOK_Click(object sender, RoutedEventArgs e)
    {
        GetFieldsBLL getFieldsBLL = new GetFieldsBLL();
        DataView dv = getFieldsBLL.GetWholeView(ViewName);
        Task task = new Task(() => ucDataExtracViewControl.PopulateGrid(dv));
        task.Start();
    }

public void PopulateGrid(DataView dv)
    {
        dgView.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(delegate{

            dgView.Columns.Clear();
            dgView.AutoGenerateColumns = false;
            foreach (DataColumn column in dv.Table.Columns)
            {
                var gridColumn = new DataGridTextColumn()
                {
                    Header = column.ColumnName,
                    Binding = new Binding("[" + column.ColumnName + "]")
                };

                dgView.Columns.Add(gridColumn);
            }
            dgView.DataContext = dv;

            DataView = dv;
        }));
    }

Thanks in advance!

Edit: The reason why I recreate the columns is because some of the column names have a dot in their name. For instance “Job No.” doesn’t yield any data when using binding. Read more here: What is it about DataTable Column Names with dots that makes them unsuitable for WPF's DataGrid control? It is not an option to make changes to the database. –

Upvotes: 3

Views: 4879

Answers (5)

Basharat Hussain
Basharat Hussain

Reputation: 237

I got the issue resolved. The C# code was not the reason for UI freeze but the rendering of records on the DataGrid. Set EnableRowVirtualization="True" EnableColumnVirtualization="True" ScrollViewer.CanContentScroll="True" in the data grid

Actually DataGrid freezes the UI on large data. So enabling virtualization resolves the issue

Upvotes: 0

pchajer
pchajer

Reputation: 1584

You can use BackgroundWorker class to perform the background process means:

Assign the below task to DoWork callback

  1. Call the getFieldsBLL.GetWholeView(ViewName);
  2. Poulate the DataView the way you want to populate

Move the UI update code (dgView.DataContext = dv;) to RunWorkerCompleted callback.

Note : You need to create a class variable for DataView so that it can be used for DoWork and RunWorkerCompleted callback.

Upvotes: 0

Barracoder
Barracoder

Reputation: 3764

Change your DispatcherPriority to Background.

dgView.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(delegate{
// your code
};

Upvotes: 1

jjrdk
jjrdk

Reputation: 1892

I've worked quite a lot with the WPF DataGrid, and rendering is a problem. In the end rendering time comes down to the amount of columns and rows that you will be showing. When the DataGrid renders it has to draw each one, which means loading and measuring the size of the content.

I've found that you can get some good speed improvements by setting fixed column widths and row heights. When used in combination with the DelayedDataGridTextColumn below, there is very little blockage of the UI thread, because each cell is rendered separately, and thus allows for other things to happen on the UI thread with a higher priority.

public class DelayedDataGridTextColumn : DataGridTextColumn
{
    protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
    {
        var textBlock = new TextBlock();
        textBlock.SetValue(FrameworkElement.StyleProperty, ElementStyle);

        Dispatcher.BeginInvoke(
            DispatcherPriority.Loaded,
            new Action<TextBlock>(x => x.SetBinding(TextBlock.TextProperty, Binding)),
        textBlock);
        return textBlock;
    }
}

Note that you can tweak the DispatcherPriority to fit the rendering speed you want. The lower the priority the more of a curtain effect you get. The higher the priority the less other items will be handled while rendering.

Upvotes: 4

Random Dev
Random Dev

Reputation: 52280

You don't actually run this in a seperate thread as the first thing you do is dispatching it back to the UI-Thread

The question is why you recreate the columns yourself? Does this really yield different results than setting AutoGenerateColumns to true?

I think you could improve on this if you would properly use the binding-features of WPF but there is just to few code to make a good suggestion on this.

Upvotes: 0

Related Questions