c# - Loading data to dataGridView freezes the window during loading, why threading does not work here? - Stack Overflow

时间: 2025-01-06 admin 业界

I have the following method that loads the data to DataGridView in a separate function. When I call the method in a button click, first two methods runs without hanging the windows since they dod not have any UI updates.

    private async void btnSearch_Click(object sender, EventArgs e)
    {

        btnFolder.Enabled = false;
        btnSearch.Enabled = false;
        btnStop.Enabled = true;

        try
        {
            var foldersTask = Task.Run(() => GetAccessibleDirectories(txtFolderPath.Text, chkSubFolders.Checked));
            var folders = await foldersTask;
            var BF = await Task.Run(() => getFolderdetails(folders));

            await Task.Run(() => this.BeginInvoke(new Action(() => loadFoldersToGrid(BF)))); // Runs on UI thread
        }
        finally
        {
            btnFolder.Enabled = true;
            btnSearch.Enabled = true;
            btnStop.Enabled = false;
        }
    }

But when it starts running 'loadFoldersToGrid,' the window freezes (it backs to normal once it is loaded completely.). I don't understand it why.

private async void loadFoldersToGrid(BrowserFolders BF)
{
    int i = 0;
    long MBcon = 1048576;
    long MB, Total = 0;

    dgFolders.Rows.Clear();
    dgFolders.Rows.Add(BF.folders.Count);

    foreach (var rec in BF.folders)
    {
        dgFolders.Rows[i].Cells[0].Value = i;
        dgFolders.Rows[i].Cells[1].Value = rec.folderPath;
        dgFolders.Rows[i].Cells[2].Value = rec.fileCount;
        long folderSize = GetFolderSize(rec.folderPath);
        string megabytes = (folderSize / MBcon).ToString("F1");
        dgFolders.Rows[i].Cells[3].Value = megabytes;
        dgFolders.Rows[i].Cells[4].Value = rec.fileCreated.ToString("MM/dd/yyyy");
        Total = Total + folderSize;
        i += 1;
    }

    txtFolderCount.Text = BF.folders.Count.ToString();
    txtSize.Text = string.Format("{0:N0} MB", Total / MBcon);
}

Any help ? tasks to not helping here.

I have the following method that loads the data to DataGridView in a separate function. When I call the method in a button click, first two methods runs without hanging the windows since they dod not have any UI updates.

    private async void btnSearch_Click(object sender, EventArgs e)
    {

        btnFolder.Enabled = false;
        btnSearch.Enabled = false;
        btnStop.Enabled = true;

        try
        {
            var foldersTask = Task.Run(() => GetAccessibleDirectories(txtFolderPath.Text, chkSubFolders.Checked));
            var folders = await foldersTask;
            var BF = await Task.Run(() => getFolderdetails(folders));

            await Task.Run(() => this.BeginInvoke(new Action(() => loadFoldersToGrid(BF)))); // Runs on UI thread
        }
        finally
        {
            btnFolder.Enabled = true;
            btnSearch.Enabled = true;
            btnStop.Enabled = false;
        }
    }

But when it starts running 'loadFoldersToGrid,' the window freezes (it backs to normal once it is loaded completely.). I don't understand it why.

private async void loadFoldersToGrid(BrowserFolders BF)
{
    int i = 0;
    long MBcon = 1048576;
    long MB, Total = 0;

    dgFolders.Rows.Clear();
    dgFolders.Rows.Add(BF.folders.Count);

    foreach (var rec in BF.folders)
    {
        dgFolders.Rows[i].Cells[0].Value = i;
        dgFolders.Rows[i].Cells[1].Value = rec.folderPath;
        dgFolders.Rows[i].Cells[2].Value = rec.fileCount;
        long folderSize = GetFolderSize(rec.folderPath);
        string megabytes = (folderSize / MBcon).ToString("F1");
        dgFolders.Rows[i].Cells[3].Value = megabytes;
        dgFolders.Rows[i].Cells[4].Value = rec.fileCreated.ToString("MM/dd/yyyy");
        Total = Total + folderSize;
        i += 1;
    }

    txtFolderCount.Text = BF.folders.Count.ToString();
    txtSize.Text = string.Format("{0:N0} MB", Total / MBcon);
}

Any help ? tasks to not helping here.

Share Improve this question asked 17 hours ago PCGPCG 2,2995 gold badges29 silver badges49 bronze badges 4
  • You could try to double-buffer the DGV – TaW Commented 16 hours ago
  • 1 UI controls make terrible data containers. Loading the data to a Datasource such as a List<T> or possibly a data table will speed up the process considerably since there is no redrawing required until you assign the data source....which the control is optimized for. – Ňɏssa Pøngjǣrdenlarp Commented 16 hours ago
  • You actually answer the question yourself with the comment "// Runs on UI Thread". If GetFolderSize(rec.folderPath) is intensive, that needs to be an object property created before you get to the BeginInvoke of loadFoldersToGrid. Each rec should already have a property with the value predetermined. – Ctznkane525 Commented 16 hours ago
  • As a side note, you are not allowed to interact with UI components from any other thread than the UI thread. So reading the txtFolderPath.Text and chkSubFolders.Checked should be done on the UI thread. Your code reads them from a ThreadPool thread. – Theodor Zoulias Commented 9 hours ago
Add a comment  | 

1 Answer 1

Reset to default -1

The issue arises because loadFoldersToGrid performs heavy operations and updates the UI within a foreach loop. UI updates in Windows Forms run on the main thread, and if you perform long-running computations or file I/O on the same thread, the UI will freeze until the task completes.

To fix this, you can split the heavy computation (GetFolderSize) and UI updates into separate threads. Use await and async to ensure the UI remains responsive.

Solution Here’s a refactored version of your code:

Updated btnSearch_Click

private async void btnSearch_Click(object sender, EventArgs e)
{
  btnFolder.Enabled = false;
  btnSearch.Enabled = false;
  btnStop.Enabled = true;

  try
  {
    // Get folders and folder details on background threads
    var foldersTask = Task.Run(() => GetAccessibleDirectories(txtFolderPath.Text, chkSubFolders.Checked));
    var folders = await foldersTask;
    var BF = await Task.Run(() => getFolderdetails(folders));

    // Update the DataGridView asynchronously
    await LoadFoldersToGridAsync(BF);
  }
  finally
  {
    btnFolder.Enabled = true;
    btnSearch.Enabled = true;
    btnStop.Enabled = false;
  }
}

Asynchronous LoadFoldersToGridAsync

private async Task LoadFoldersToGridAsync(BrowserFolders BF)
{
int i = 0;
long MBcon = 1048576;
long Total = 0;

// Clear and prepare the DataGridView rows on the UI thread
await Task.Run(() =>
{
    this.BeginInvoke(new Action(() =>
    {
        dgFolders.Rows.Clear();
        dgFolders.Rows.Add(BF.folders.Count);
    }));
});

foreach (var rec in BF.folders)
{
    // Perform heavy computation (GetFolderSize) on a background thread
    long folderSize = await Task.Run(() => GetFolderSize(rec.folderPath));
    string megabytes = (folderSize / MBcon).ToString("F1");
    Total += folderSize;

    // Update the DataGridView on the UI thread
    int currentIndex = i; // Capture the current index for the closure
    await Task.Run(() =>
    {
        this.BeginInvoke(new Action(() =>
        {
            dgFolders.Rows[currentIndex].Cells[0].Value = currentIndex;
            dgFolders.Rows[currentIndex].Cells[1].Value = rec.folderPath;
            dgFolders.Rows[currentIndex].Cells[2].Value = rec.fileCount;
            dgFolders.Rows[currentIndex].Cells[3].Value = megabytes;
            dgFolders.Rows[currentIndex].Cells[4].Value = rec.fileCreated.ToString("MM/dd/yyyy");
        }));
    });

    i++;
}

// Update the total count and size on the UI thread
await Task.Run(() =>
{
    this.BeginInvoke(new Action(() =>
    {
        txtFolderCount.Text = BF.folders.Count.ToString();
        txtSize.Text = string.Format("{0:N0} MB", Total / MBcon);
    }));
});
}

Optional Optimization: Parallel Folder Processing

If you want faster execution, process the folders in parallel (e.g., with Parallel.ForEach or Task.WhenAll for asynchronous operations). Here's an example using Task.WhenAll:

private async Task LoadFoldersToGridAsync(BrowserFolders BF)
{
long MBcon = 1048576;
long Total = 0;

var tasks = BF.folders.Select(async (rec, index) =>
{
    long folderSize = await Task.Run(() => GetFolderSize(rec.folderPath));
    string megabytes = (folderSize / MBcon).ToString("F1");

    await Task.Run(() =>
    {
        this.BeginInvoke(new Action(() =>
        {
            dgFolders.Rows[index].Cells[0].Value = index;
            dgFolders.Rows[index].Cells[1].Value = rec.folderPath;
            dgFolders.Rows[index].Cells[2].Value = rec.fileCount;
            dgFolders.Rows[index].Cells[3].Value = megabytes;
            dgFolders.Rows[index].Cells[4].Value = rec.fileCreated.ToString("MM/dd/yyyy");
        }));
    });

    Interlocked.Add(ref Total, folderSize);
});

await Task.WhenAll(tasks);

// Update total count and size on the UI thread
await Task.Run(() =>
{
    this.BeginInvoke(new Action(() =>
    {
        txtFolderCount.Text = BF.folders.Count.ToString();
        txtSize.Text = string.Format("{0:N0} MB", Total / MBcon);
    }));
});
}