0

Мой код ищет файлы по названию в путях и и в таблицу добавляет результат. Для этого я использую 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);

}

  • Вы не можете работать с DataTable, подключенной к интерфейсу из стороннего потока. Подключайте после того как код отработает, а не до, либо используйте Invoke() для обновления привязанной таблицы. И вообще использование DataTable здесь не оправдано https://ru.stackoverflow.com/a/1199692/373567. И что значит "фризит"? Насовсем или на время? – aepot Jul 23 '22 at 08:07
  • @aepot спасибо, проблема решилась использованием Invoke() – JustChickNugget Jul 23 '22 at 09:00
  • Зачем вы удалили новый вопрос? Я почти дописал ответ на него. – aepot Aug 04 '22 at 07:18

1 Answers1

2

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

Внутри таски оставляем только код перебора директорий и файлов.
Саму задачу оборачиваем в 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.

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