C# Windows Форма - Как мне выполнить экспорт в Excel в другом потоке И где я могу включить код для индикатора выполнения? - PullRequest
0 голосов
/ 11 февраля 2020

Время от времени я получаю более 100000+ строк в представлении сетки данных и хочу, чтобы пользователи могли быстрее их экспортировать. В настоящее время моя форма windows переходит в стадию «Не отвечает», но фактически выполняет экспорт в серверную часть. Я хочу выполнить экспорт в другом потоке, чтобы он был быстрее, и я хочу добавить индикатор выполнения, чтобы показать ход самого экспорта.

Я пробовал следующее:

  • Создание новой задачи - время экспорта фактически увеличивается
  • Создание другой темы для запуска с использованием Thread thread = new Thread (Export) - выдает ошибку, когда запускается строка Show Dialogue Box

Мой код выглядит следующим образом:

 private void BtnSearchExportCSV_Click(object sender, EventArgs e)
    {
        Export();
    }

    private void CopyAllToClipBoard()
    {
        dgvSearchFilter.ClipboardCopyMode = DataGridViewClipboardCopyMode.EnableAlwaysIncludeHeaderText;
        dgvSearchFilter.MultiSelect = true;
        dgvSearchFilter.RowHeadersVisible = false;
        dgvSearchFilter.SelectAll();

        DataObject dataObj = dgvSearchFilter.GetClipboardContent();
        if (dataObj != null)
        {
            Invoke((Action)(() => { Clipboard.SetDataObject(dataObj); }));
            //Clipboard.SetDataObject(dataObj);
        }
    }

    private void releaseObject(object obj)
    {
        try
        {
            System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
            obj = null;
        }
        catch (Exception ex)
        {
            obj = null;
            MessageBox.Show("Exception Occurred while releasing object " + ex.ToString());
        }
        finally
        {
            GC.Collect();
        }
    }

    private void Export()
    {
        try
        {
            SaveFileDialog sfd = new SaveFileDialog();
            sfd.Filter = "Excel Documents (*.xls)|*.xls";
            sfd.FileName = "Export.xls";
            if (sfd.ShowDialog() == DialogResult.OK)
            {
                // Copy DataGridView results to clipboard
                CopyAllToClipBoard();

                object misValue = System.Reflection.Missing.Value;
                Excel.Application xlexcel = new Excel.Application();
                // Without this you will get two confirm overwrite prompts
                xlexcel.DisplayAlerts = false;
                Excel.Workbook xlWorkBook = xlexcel.Workbooks.Add(misValue);
                Excel.Worksheet xlWorkSheet = (Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1);
                // Paste clipboard results to worksheet range
                Excel.Range CR = (Excel.Range)xlWorkSheet.Cells[1, 1];
                CR.Select();
                xlWorkSheet.PasteSpecial(CR, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, true);

                // For some reason column A is always blank in the worksheet. ¯\_(ツ)_/¯
                // Delete blank column A and select cell A1
                //Excel.Range delRng = xlWorkSheet.get_Range("A:A").Cells;
                //delRng.Delete(Type.Missing);
                //xlWorkSheet.get_Range("A1").Select();

                // Save the excel file under the captured location from the SaveFileDialog
                xlWorkBook.SaveAs(sfd.FileName, Excel.XlFileFormat.xlWorkbookNormal, misValue, misValue, misValue, misValue, Excel.XlSaveAsAccessMode.xlExclusive, misValue, misValue, misValue, misValue, misValue);
                xlexcel.DisplayAlerts = true;
                xlWorkBook.Close(true, misValue, misValue);
                xlexcel.Quit();

                releaseObject(xlWorkSheet);
                releaseObject(xlWorkBook);
                releaseObject(xlexcel);

                // Clear Clipboard and DataGridView selection
                Clipboard.Clear();
                dgvSearchFilter.ClearSelection();

                // Open the newly saved excel file
                if (File.Exists(sfd.FileName))
                    System.Diagnostics.Process.Start(sfd.FileName);
            }
        }
        catch (Exception exception)
        {
            MessageBox.Show("The following exception occurred: " + exception.ToString());
        }
    }
}

Я знакомлюсь с C#. Однако я впервые столкнулся с чем-то подобным.

Спасибо.

Ответы [ 2 ]

0 голосов
/ 10 марта 2020

Итак, я ходил, пробовал разные методы и виды кодов, а также реализовывал свой собственный. На данный момент наиболее успешный и быстрый код, который я встречал (и модифицировал), выглядит следующим образом:

        var headers = dgvSearchFilter.Columns.Cast<DataGridViewColumn>();
        string delimiter = ",";
        DataTable dt = new DataTable();
        foreach (DataGridViewColumn col in dgvSearchFilter.Columns)
        {
            dt.Columns.Add(new DataColumn(col.Name, typeof(string)));
        }

        foreach (DataGridViewRow row in dgvSearchFilter.Rows)
        {
            DataRow dataRow = dt.NewRow();
            foreach (DataGridViewCell cell in row.Cells)
            {
                if (row.Cells[cell.ColumnIndex].Value == null || row.Cells[cell.ColumnIndex].Value == DBNull.Value || String.IsNullOrWhiteSpace(row.Cells[cell.ColumnIndex].Value.ToString()))
                {
                    dataRow[cell.ColumnIndex] = " ";
                }
                else
                {
                    dataRow[cell.ColumnIndex] = cell.Value.ToString();
                }
            }
            dt.Rows.Add(dataRow);
        }

        string unique = DateTime.Now.ToString("yyyyMMddHHmmssffff");
        string fileName = "SQLQueryOutput_" + unique + ".csv";
        using (StreamWriter swr = new StreamWriter(File.Open(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), fileName), FileMode.CreateNew), Encoding.Default, 1000000))
        {
            swr.WriteLine(string.Join(",", headers.Select(column => "\"" + column.HeaderText + "\"").ToArray()));
            foreach (DataRow dr in dt.Rows)
            {
                var line = dr.ItemArray.Select(r => r.ToString().Contains(delimiter) || r.ToString().Contains("\n") ? "\"" + r + "\"" : r);
                swr.WriteLine(string.Join(delimiter, line));
            }
        }

        MessageBox.Show("Your file was generated and its ready for use.");

Это не формат Excel, а CSV. Однако вы можете использовать его в другом потоке. Он генерирует CSV на вашем рабочем столе с name_uniqueValue.

По сути, вы преобразуете столбцы таблицы данных в значения, разделенные запятыми. Затем вы добавляете их в DataTable. Один за другим вы проводите oop через DataGridView и добавляете значения в DataTable. Затем вы используете StreamWriter для записи этих значений в CSV. 1 миллион строк почти / меньше минуты.

Попробуйте все, кто хочет преобразовать DataGridView в CSV.

0 голосов
/ 11 февраля 2020
  1. Перемещение вашей операции в другой поток НЕ сделает ее быстрее, но больше не будет блокировать пользовательский интерфейс. Пользователь не увидит "неотвечающее приложение".

    Поскольку ваш код запускается Button.Click, который выполняется потоком пользовательского интерфейса. Если ваша операция занимает время, ваш пользовательский интерфейс будет заблокирован вашей операцией.

  2. Не смешивайте код пользовательского интерфейса SaveFileDialog и логики операций c вместе.

  3. Использование досрочного возврата улучшит читабельность вашего кода. Это уменьшает размер вложенного оператора. Вы можете Google для этого.

  4. System.Threading.ThreadStateException произошло, потому что вы используете SaveFileDialog и ClipBoard в потоке. Чтобы решить эту проблему, вам нужно убрать эти две функции из функции вызова Thread. Если вы действительно хотите, чтобы это сработало. Следующее может заставить это работать. Однако я не предлагаю эту реализацию.

    Thread op = new Thread( operation );
    op.SetApartmentState( ApartmentState.STA );
    op.Start();
    

Пример ниже, включая то, куда вы помещаете окно прогресса:

private void Export()
{
  // Do UI check first
  SaveFileDialog sfd = new SaveFileDialog();
  sfd.Filter = "Excel Documents (*.xls)|*.xls";
  sfd.FileName = "Export.xls";

  // If failed , early return
  if (sfd.ShowDialog() != DialogResult.OK)
  {
    return;
  }
  ProgressWindow prg = new ProgressWindow();
  prg.Show();
  // Do your copy and export code below, you may use task or thread if you don't want to let current form unresponsive.
  operation();
  // After finished, close your progress window
  prg.Close();
}

void operation()
{
     // Copy DataGridView results to clipboard
     CopyAllToClipBoard();

     object misValue = System.Reflection.Missing.Value;
     Excel.Application xlexcel = new Excel.Application();
     // Without this you will get two confirm overwrite prompts
     xlexcel.DisplayAlerts = false;
     Excel.Workbook xlWorkBook = xlexcel.Workbooks.Add(misValue);
     Excel.Worksheet xlWorkSheet = (Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1);
     // Paste clipboard results to worksheet range
     Excel.Range CR = (Excel.Range)xlWorkSheet.Cells[1, 1];
     CR.Select();
     xlWorkSheet.PasteSpecial(CR, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, true);

     // For some reason column A is always blank in the worksheet. ¯\_(ツ)_/¯
     // Delete blank column A and select cell A1
     //Excel.Range delRng = xlWorkSheet.get_Range("A:A").Cells;
     //delRng.Delete(Type.Missing);
     //xlWorkSheet.get_Range("A1").Select();

     // Save the excel file under the captured location from the SaveFileDialog
     xlWorkBook.SaveAs(sfd.FileName, Excel.XlFileFormat.xlWorkbookNormal, misValue, misValue, misValue, misValue, Excel.XlSaveAsAccessMode.xlExclusive, misValue, misValue, misValue, misValue, misValue);
     xlexcel.DisplayAlerts = true;
     xlWorkBook.Close(true, misValue, misValue);
     xlexcel.Quit();

     releaseObject(xlWorkSheet);
     releaseObject(xlWorkBook);
     releaseObject(xlexcel);

     // Clear Clipboard and DataGridView selection
     Clipboard.Clear();
     dgvSearchFilter.ClearSelection();

     // Open the newly saved excel file
     if (File.Exists(sfd.FileName))
         System.Diagnostics.Process.Start(sfd.FileName);
}

Согласно вашему описанию, ProgressWindow может быть Form без кнопки «Закрыть» .

...