Во-первых, позвольте мне сказать, что я уже почти 2 дня занимаюсь этим вопросом: пробовал различные варианты и читал множество вопросов с вашего сайта с похожими проблемами.
Сначала позвольте мне вставить мой код, чтобы вы получили полную картину.
РЕДАКТИРОВАТЬ: Удален код, который может быть ненужным для проблемы, на основе запроса от участника форума.
Форма 1.cs
namespace TestApp
{
public partial class Form1 : Form
{
//global declarations
private static Form1 myForm;
public static Form1 MyForm
{
get
{
return myForm;
}
}
List<DataSet> DataSets = new List<DataSet>();
int typeOfDataset = 0;
//delegate for background thread to communicate with the UI thread _
//and update the metadata autodetection progress bar
public delegate void UpdateProgressBar(int updateProgress);
public UpdateProgressBar myDelegate;
//***************************
//**** Form Events ********
//***************************
public Form1()
{
InitializeComponent();
if (myForm == null)
{
myForm = this;
}
DataSets.Add(new DSVDataSet());
DataSets[typeOfDataset].BWorker = new BackgroundWorker();
DataSets[typeOfDataset].BWorker.WorkerSupportsCancellation = true;
myDelegate = new UpdateProgressBar(UpdateProgressBarMethod);
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
//e.Cancel = true;
if (DataSets[typeOfDataset].BWorker != null || DataSets[typeOfDataset].BWorker.IsBusy)
{
Thread.Sleep(1000);
DataSets[typeOfDataset].BWorker.CancelAsync();
}
Application.Exit();
}
//***************************
//*** Menu Items Events ***
//***************************
private void cSVToolStripMenuItem_Click(object sender, EventArgs e)
{
LoadFillDSVData(',');
}
//***************************
//*** DataGridViews Events **
//***************************
private void dataGridView1_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e)
{
using (SolidBrush b = new SolidBrush(this.dataGridView1.RowHeadersDefaultCellStyle.ForeColor))
{
e.Graphics.DrawString(e.RowIndex.ToString(System.Globalization.CultureInfo.CurrentUICulture), this.dataGridView1.DefaultCellStyle.Font, b, e.RowBounds.Location.X + 20, e.RowBounds.Location.Y + 4);
}
int rowHeaderWidth = TextRenderer.MeasureText(e.RowIndex.ToString(), dataGridView1.Font).Width;
if (rowHeaderWidth + 22 > dataGridView1.RowHeadersWidth)
{
dataGridView1.RowHeadersWidth = rowHeaderWidth + 22;
}
}
private void dataGridView2_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e)
{
using (SolidBrush b = new SolidBrush(this.dataGridView2.RowHeadersDefaultCellStyle.ForeColor))
{
e.Graphics.DrawString(e.RowIndex.ToString(System.Globalization.CultureInfo.CurrentUICulture), this.dataGridView2.DefaultCellStyle.Font, b, e.RowBounds.Location.X + 20, e.RowBounds.Location.Y + 4);
}
int rowHeaderWidth = TextRenderer.MeasureText(e.RowIndex.ToString(), dataGridView2.Font).Width;
if (rowHeaderWidth + 22 > dataGridView2.RowHeadersWidth)
{
dataGridView2.RowHeadersWidth = rowHeaderWidth + 22;
}
}
//***************************
//****** Other Methods ******
//***************************
private void LoadFillDSVData(char delimiter)
{
//load file through openFileDialog
//some more openFileDialog code removed for simplicity
if (DataSets[typeOfDataset].BWorker != null || DataSets[typeOfDataset].BWorker.IsBusy)
{
Thread.Sleep(1000);
DataSets[typeOfDataset].BWorker.CancelAsync();
DataSets[typeOfDataset].BWorker.Dispose();
}
//if file was loaded, instantiate the class
//and populate it
dataGridView1.Rows.Clear();
dataGridView2.Rows.Clear();
typeOfDataset = 0;
DataSets[typeOfDataset] = new DSVDataSet();
DataSets[typeOfDataset].DataGrid = this.dataGridView1;
DataSets[typeOfDataset].BWorker = new BackgroundWorker();
DataSets[typeOfDataset].InputFile = openFileDialog1.FileName;
DataSets[typeOfDataset].FileName = Path.GetFileName(DataSets[typeOfDataset].InputFile);
DataSets[typeOfDataset].InputPath = Path.GetDirectoryName(DataSets[typeOfDataset].InputFile);
DataSets[typeOfDataset].Delimiter = delimiter;
//read file to get number of objects and attributes
DataSets[typeOfDataset].LoadFile(DataSets[typeOfDataset].InputFile, DataSets[typeOfDataset].Delimiter);
//ask to autodetect metadata
DialogResult dr = MessageBox.Show("Auto detect attributes?", "TestApp", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
switch (dr)
{
case DialogResult.Yes:
toolStripStatusLabel1.Text = "Autodetecting attributes...";
toolStripProgressBar1.Value = 0;
toolStripProgressBar1.Maximum = 100;
toolStripProgressBar1.Style = ProgressBarStyle.Continuous;
DataSets[typeOfDataset].AutoDetectMetadata(DataSets[typeOfDataset].Attributes);
break;
case DialogResult.No:
break;
default:
break;
}
}
public void UpdateProgressBarMethod(int progress)
{
if (progress > 99)
{
toolStripProgressBar1.Value = progress;
toolStripStatusLabel1.Text = "Done.";
}
else
{
toolStripProgressBar1.Value = progress;
}
}
}
}
И еще один класс:
DSVDataSet.cs
namespace TestApp
{
public class DSVDataSet : DataSet
{
static Form1 myForm = Form1.MyForm;
//constructor(s)
public DSVDataSet()
{
InputType = DataSetType.DSV;
}
//autodetects metadata from the file if
//the user wishes to do so
public override void AutoDetectMetadata(List<Attribute> attributeList)
{
BWorker.WorkerReportsProgress = true;
BWorker.WorkerSupportsCancellation = true;
BWorker.DoWork += worker_DoWork;
BWorker.ProgressChanged += worker_ProgressChanged;
BWorker.RunWorkerCompleted += worker_RunWorkerCompleted;
//send this to another thread as it is computationally intensive
BWorker.RunWorkerAsync(attributeList);
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
List<Attribute> attributeList = (List<Attribute>)e.Argument;
using (StreamReader sr = new StreamReader(InputFile))
{
for (int i = 0; i < NumberOfAttributes; i++)
{
Attribute a = new Attribute();
attributeList.Add(a);
}
for (int i = 0; i < NumberOfObjects; i++)
{
string[] DSVLine = sr.ReadLine().Split(Delimiter);
int hoistedCount = DSVLine.Count();
string str = string.Empty;
for (int j = 0; j < hoistedCount; j++)
{
bool newValue = true;
str = DSVLine[j];
for (int k = 0; k < attributeList[j].Categories.Count; k++)
{
if (str == attributeList[j].Categories[k])
{
newValue = false;
}
}
if (newValue == true)
{
attributeList[j].Categories.Add(str);
//removed some code for simplicity
}
}
int currentProgress = (int)((i * 100) / NumberOfObjects);
if (BWorker.CancellationPending)
{
Thread.Sleep(1000);
e.Cancel = true;
return;
}
BWorker.ReportProgress(currentProgress); //report progress
}
}
e.Result = 100; //final result (100%)
}
private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
int update = e.ProgressPercentage;
myForm.BeginInvoke(myForm.myDelegate, update);
}
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
return;
}
if (e.Cancelled)
{
return;
}
else
{
int update = (int)e.Result;
myForm.Invoke(myForm.myDelegate, update);
for (int i = 0; i < Attributes.Count; i++)
{
DataGridViewRow item = new DataGridViewRow();
item.CreateCells(DataGrid);
item.Cells[0].Value = Attributes[i].Name;
switch (Attributes[i].Type)
{
case AttributeType.Categorical:
item.Cells[1].Value = "Categorical";
break;
//removed some cases for simplicity
default:
item.Cells[1].Value = "Categorical";
break;
}
item.Cells[2].Value = Attributes[i].Convert;
DataGrid.Rows.Add(item);
}
BWorker.Dispose();
}
}
}
}
Короче говоря, у меня есть файл backGroundWorker в DSVDataset.cs, который выполняет некоторые «тяжелые вычисления», чтобы пользовательский интерфейс не зависал (новичок в этом), и я использую делегат для создания фонового потока для связи с пользовательским интерфейсом. поток, чтобы обновить некоторые значения индикатора выполнения. Если пользователь решает создать новый DSVDataSet (через cSVToolStripMenuItem_Click или tSVToolStripMenuItem_Click), тогда я добавлю несколько ifs везде, где я сочту целесообразным, проверить, есть ли уже запущенный backGroundWorker: если есть, вызовите CancelAsync, чтобы он прекратил все, что делает, и тогда. Распоряжайся.
Теперь, иногда, когда я пытаюсь выйти из Form1 во время работы backGroundWorker (проверьте, например, Form1_FormClosing), я получаю сообщение об ошибке в заголовке этого поста. Ошибка приводит меня к этому фрагменту кода в DSVDataSet.cs:
private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
int update = e.ProgressPercentage;
myForm.BeginInvoke(myForm.myDelegate, update);
}
Нажатие F10 переводит меня в Program1.cs:
Application.Run(new Form1());
Основываясь на другой статье, которую я прочитал (/509425/oshibka-kompilyatsii-invoke-begininvoke-mogut-vyzvany-dlya-elementa-upravleniya-poka-budet-sozdan-deskriptor-okna, посмотрите на ответ), я реализовал эту логику, выставив экземпляр самого класса Main, чтобы он всегда указывал на этот экземпляр main.
Так в Form1.cs:
private static Form1 myForm;
public static Form1 MyForm
{
get
{
return myForm;
}
}
public Form1()
{
InitializeComponent();
if (myForm == null)
{
myForm = this;
}
}
А в DSVDataset.cs:
static Form1 myForm = Form1.MyForm;
и я использую myForm везде, где это необходимо.
Тем не менее, я не могу обойти эту ошибку, поэтому могу только предположить, что я не реализовал это решение правильно, или я делаю что-то не так с обработкой backGroundWorkers.
Любая помощь будет принята с благодарностью, и я приложил все усилия, чтобы быть максимально подробным (надеюсь, это не было излишним:)
Привет