Приложение фризит после подключения DataTable к DataGridView

Мой код ищет файлы по названию в путях и и в таблицу добавляет результат. Для этого я использую Task, но после подключения DataTable к DataGridView, приложение фризит даже в Task'е и перестаёт отвечать хотя до подключения DataTable такого не было.

DataTable dt = new DataTable();

public Main()
{
    InitializeComponent();
    found_datagridview.DataSource = dt;
    dt.Columns.Add("Type");
    dt.Columns.Add("Date");
    dt.Columns.Add("Size");
    dt.Columns.Add("Location");
    found_datagridview.Columns[0].Width = 60;
    found_datagridview.Columns[1].Width = 141;
    found_datagridview.Columns[2].Width = 130;
    found_datagridview.Columns[3].Width = 418;
}

async Task FindFiles(CancellationToken ct)
{
    await Task.Run(() =>
    {
        path = path_textbox.Text;
        name = name_textbox.Text;

        dt.Rows.Clear();
        try
        {
            ct.ThrowIfCancellationRequested();
            if (path == "\\\\")
            {
                int any = 0;

                timer.Start();
                foreach (DriveInfo drive in DriveInfo.GetDrives())
                {
                    IEnumerable<string> dirs = Directory.EnumerateDirectories(drive.RootDirectory.ToString(), $"*{name}*", new EnumerationOptions { IgnoreInaccessible = true, RecurseSubdirectories = true });
                    IEnumerable<string> files = Directory.EnumerateFiles(drive.RootDirectory.ToString(), $"*{name}*", new EnumerationOptions { IgnoreInaccessible = true, RecurseSubdirectories = true });
                    if (dirs.Any() || files.Any())
                    {
                        if (dirs.Any())
                        {
                            foreach (string dir in dirs)
                            {
                                DirectoryInfo info = new DirectoryInfo(dir);
                                string time = info.CreationTime.ToString("dd.MM.yyyy HH:mm:ss");
                                string size = Directory.EnumerateFiles(dir, $"*", new EnumerationOptions { IgnoreInaccessible = true, RecurseSubdirectories = true }).Sum(file => new FileInfo(file).Length).ToString("N0");

                                ct.ThrowIfCancellationRequested();
                                dt.Rows.Add(new object[] { "<DIR>", time, $"{size} bytes", info.FullName });
                            }
                        }
                        if (files.Any())
                        {
                            foreach (string file in files)
                            {
                                FileInfo info = new FileInfo(file);
                                string time = info.CreationTime.ToString("dd.MM.yyyy HH:mm:ss");
                                string size = info.Length.ToString("N0");

                                ct.ThrowIfCancellationRequested();
                                dt.Rows.Add(new object[] { "<FILE>", time, $"{size} bytes", info.FullName });
                            }
                        }
                        any++;
                    }
                    else
                    {
                        if (any == 0)
                        {
                            MessageBox.Show($"No matches found", "Nothing found", MessageBoxButtons.OK, MessageBoxIcon.Information);
                            break;
                        }
                    }
                }
                timer.Stop();
            }
            else
            {
                timer.Start();
                IEnumerable<string> dirs = Directory.EnumerateDirectories(path, $"*{name}*", new EnumerationOptions { IgnoreInaccessible = true, RecurseSubdirectories = true });
                IEnumerable<string> files = Directory.EnumerateFiles(path, $"*{name}*", new EnumerationOptions { IgnoreInaccessible = true, RecurseSubdirectories = true });
                if (dirs.Any() || files.Any())
                {
                    if (dirs.Any())
                    {
                        foreach (string dir in dirs)
                        {
                            DirectoryInfo info = new DirectoryInfo(dir);
                            string time = info.CreationTime.ToString("dd.MM.yyyy HH:mm:ss");
                            string size = Directory.EnumerateFiles(dir, $"*", new EnumerationOptions { IgnoreInaccessible = true, RecurseSubdirectories = true }).Sum(file => new FileInfo(file).Length).ToString("N0");

                            ct.ThrowIfCancellationRequested();
                            dt.Rows.Add(new object[] { "<DIR>", time, $"{size} bytes", info.FullName });
                        }
                    }
                    if (files.Any())
                    {
                        foreach (string file in files)
                        {
                            FileInfo info = new FileInfo(file);
                            string time = info.CreationTime.ToString("dd.MM.yyyy HH:mm:ss");
                            string size = info.Length.ToString("N0");

                            ct.ThrowIfCancellationRequested();
                            dt.Rows.Add(new object[] { "<FILE>", time, $"{size} bytes", info.FullName });
                        }
                    }
                }
                else
                {
                    MessageBox.Show("No matches found", "Nothing found", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
                timer.Stop();
            }
        }
        catch (OperationCanceledException) { }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }, ct);
}

Ответы (1 шт):

Автор решения: Alexander Petrov

Некоторые участки кода у вас дублируются - их можно вынести в отдельные методы.
Также можно вынести в отдельные методы логически связанные части кода.

Внутри таски оставляем только код перебора директорий и файлов.
Саму задачу оборачиваем в try-catch.

Также выносим лишний код из конструктора формы в событие Load.

Предлагаю посмотреть на такой вариант:

private void Form1_Load(object sender, EventArgs e)
{
    dt.Columns.Add("Type");
    dt.Columns.Add("Date");
    dt.Columns.Add("Size");
    dt.Columns.Add("Location");

    found_datagridview.DataSource = dt;

    SetColumnsSize();
}

private void SetColumnsSize()
{
    found_datagridview.Columns[0].Width = 60;
    found_datagridview.Columns[1].Width = 141;
    found_datagridview.Columns[2].Width = 130;
    found_datagridview.Columns[3].Width = 418;

    // вместо ручного задания размеров:
    //found_datagridview.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCells);
}

async Task FindFiles(CancellationToken ct = default)
{
    found_datagridview.DataSource = null;

    try
    {
        await Task.Run(() =>
        {
            string path = path_textbox.Text;
            string name = name_textbox.Text;
            dt.Rows.Clear();

            if (path == "\\\\")
            {
                foreach (DriveInfo drive in DriveInfo.GetDrives())
                {
                    FindFiles(drive.RootDirectory.Name, name, ct);
                }
            }
            else
            {
                FindFiles(path, name, ct);
            }
        }, ct);
    }
    catch (OperationCanceledException) { }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }

    if (dt.Rows.Count == 0)
    {
        MessageBox.Show("No matches found", "Nothing found", MessageBoxButtons.OK, MessageBoxIcon.Information);
    }

    found_datagridview.DataSource = dt;
    SetColumnsSize();
}

private void FindFiles(string path, string name, CancellationToken ct = default)
{
    var options = new EnumerationOptions { IgnoreInaccessible = true, RecurseSubdirectories = true };
    var dirs = Directory.EnumerateDirectories(path, $"*{name}*", options);
    var files = Directory.EnumerateFiles(path, $"*{name}*", options);

    foreach (string dir in dirs)
    {
        ct.ThrowIfCancellationRequested();

        DirectoryInfo info = new DirectoryInfo(dir);
        string time = info.CreationTime.ToString("dd.MM.yyyy HH:mm:ss");
        string size = Directory.EnumerateFiles(dir, $"*", options).Sum(file => new FileInfo(file).Length).ToString("N0");
        dt.Rows.Add(new object[] { "<DIR>", time, $"{size} bytes", info.FullName });
    }

    foreach (string file in files)
    {
        ct.ThrowIfCancellationRequested();

        FileInfo info = new FileInfo(file);
        string time = info.CreationTime.ToString("dd.MM.yyyy HH:mm:ss");
        string size = info.Length.ToString("N0");
        dt.Rows.Add(new object[] { "<FILE>", time, $"{size} bytes", info.FullName });
    }
}

Проверки dirs.Any(), files.Any() не нужны, т. к. цикл foreach не выполнит ни одной итерации в случае пустых коллекций.
От переменной any тоже можно избавиться, проверяя свойство dt.Rows.Count.

Я явно указываю дефолтный параметр CancellationToken ct = default. Таково требование Framework Design Guidelines.

Вы стартуете и останавливаете какой-то таймер. Я не знаю, что он делает, поэтому просто убрал эти строки. Верните их на место, если необходимо.

→ Ссылка