C# 错误:在创建窗口句柄之前,无法对控件调用Invoke或BeginInvoke

C# 错误:在创建窗口句柄之前,无法对控件调用Invoke或BeginInvoke,c#,c#-4.0,C#,C# 4.0,首先,让我说,我已经在这个问题上讨论了差不多两天了:尝试了各种替代方案,并从您的网站上阅读了大量类似问题的问题 首先,让我粘贴我的代码,这样您就可以了解整个情况 编辑:根据论坛成员的请求,删除了一些问题可能不需要的代码 表格1.cs namespace TestApp { public partial class Form1 : Form { //global declarations private static Form1 myForm; public st

首先,让我说,我已经在这个问题上讨论了差不多两天了:尝试了各种替代方案,并从您的网站上阅读了大量类似问题的问题

首先,让我粘贴我的代码,这样您就可以了解整个情况

编辑:根据论坛成员的请求,删除了一些问题可能不需要的代码

表格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;
        }
    } 

}
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();
        }
    }
}
按F10键进入Program1.cs:

   Application.Run(new Form1());
根据我读到的另一篇文章(http://stackoverflow.com/questions/513131/c-sharp-compile-error-invoke-or-begininvoke-cannot-be-called-on-a-control-unti ,看看答案),我通过公开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;
我在需要的地方使用我的表单

尽管如此,我还是无法克服这个错误,所以我只能假设我没有正确地实现这个解决方案,或者我在处理后台工作人员方面做了一些错误的事情。 任何帮助都将不胜感激,我尽我所能做到尽可能详细(希望这不是一个过分的做法:)


关于

代码太多,无法理解所有代码。但是当您关闭表单时,后台工作人员似乎仍在工作,因此,后台工作人员的进度更改将使您的程序崩溃

对于快速解决方案,请在进度更改时检查表单状态,因为我对winform没有太多经验,但它必须存在一个方法来检查表单是否正在关闭或处理


不建议这样做,因为它会将UI和逻辑结合起来。

代码太多,无法理解所有这些。但是当您关闭表单时,后台工作人员似乎仍在工作,因此,后台工作人员的进度更改将使您的程序崩溃

对于快速解决方案,请在进度更改时检查表单状态,因为我对winform没有太多经验,但它必须存在一个方法来检查表单是否正在关闭或处理


不建议这样做,因为它会将UI和逻辑结合起来。

正如dBear所说,在处理表单之后,后台工作人员可能仍在触发进度更改事件。最好在后台工作程序完成之前阻止窗体关闭,或者在关闭窗体之前杀死后台工作程序。无论您选择哪种选项,最好使用事件进行此类通信。将以下事件定义添加到DSVDataSet,并修改进度更改事件处理程序:

public event ProgressChangedEventHandler ProgressChanged;

private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    if (ProgressChanged != null) {
        ProgressChanged(this, e);
    }
}
dsv.ProgressChanged += new ProgressChangedEventHandler(dsv_ProgressChanged);
完成此操作后,需要在Form1中进行更改,以便在创建DSVDataSet的新实例后,添加事件处理程序:

public event ProgressChangedEventHandler ProgressChanged;

private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    if (ProgressChanged != null) {
        ProgressChanged(this, e);
    }
}
dsv.ProgressChanged += new ProgressChangedEventHandler(dsv_ProgressChanged);
将显示进度所需的任何代码放入
dsv\u ProgressChanged
的主体中,类似于:

void dsv_ProgressChanged(object sender, ProgressChangedEventArgs e) {
     myForm.Invoke(myForm.myDelegate, update);
}

正如dBear所说,在处理表单之后,您的后台工作人员可能仍在触发进度更改事件。最好在后台工作程序完成之前阻止窗体关闭,或者在关闭窗体之前杀死后台工作程序。无论您选择哪种选项,最好使用事件进行此类通信。将以下事件定义添加到DSVDataSet,并修改进度更改事件处理程序:

public event ProgressChangedEventHandler ProgressChanged;

private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    if (ProgressChanged != null) {
        ProgressChanged(this, e);
    }
}
dsv.ProgressChanged += new ProgressChangedEventHandler(dsv_ProgressChanged);
完成此操作后,需要在Form1中进行更改,以便在创建DSVDataSet的新实例后,添加事件处理程序:

public event ProgressChangedEventHandler ProgressChanged;

private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    if (ProgressChanged != null) {
        ProgressChanged(this, e);
    }
}
dsv.ProgressChanged += new ProgressChangedEventHandler(dsv_ProgressChanged);
将显示进度所需的任何代码放入
dsv\u ProgressChanged
的主体中,类似于:

void dsv_ProgressChanged(object sender, ProgressChangedEventArgs e) {
     myForm.Invoke(myForm.myDelegate, update);
}

首先,看起来好像您正在尝试创建Form1的单例实例。最好的方法是使用创建表单新实例的静态方法、属性或变量创建私有构造函数:

public class Form1 : Form
{
    private static Form1 instance;
    public static Form1 Instance
    {
        get 
        { 
           if(instance == null) { instance = new Form1(); }
           return instance;
        }
    }

    private Form1()
    {
        ......
    }
}
然后改变:

    Application.Run(new Form1());
致:

然后,您可以在任何其他类中使用Form1.Instance来访问主窗口。 现在,窗口句柄错误通常与未在主线程上创建的表单(Form1)相关联


遵循我提供的模式可能会解决您的问题。

首先,您似乎正在尝试创建Form1的单例实例。最好的方法是使用创建表单新实例的静态方法、属性或变量创建私有构造函数:

public class Form1 : Form
{
    private static Form1 instance;
    public static Form1 Instance
    {
        get 
        { 
           if(instance == null) { instance = new Form1(); }
           return instance;
        }
    }

    private Form1()
    {
        ......
    }
}
然后改变:

    Application.Run(new Form1());
致:

然后,您可以在任何其他类中使用Form1.Instance来访问主窗口。 现在,窗口句柄错误通常与未在主线程上创建的表单(Form1)相关联


遵循我提供的模式可能会解决您的问题。

如果您想在Form1和DSVDataSet之间通信,最好使用事件,而不是尝试将对表单的引用传递到DSVDataSet。如果您已经为此工作了2天,那么您肯定有时间创建一个。不管怎样,你必须这样做才能测试。但是,不要把整个项目中的所有源代码都发布出来。对不起,我是新来的。完成。我留下了dataGridView_RowPostPaint之类的内容,因为在使用F10跟踪错误之后,这些事件在Application.Run(new Form1())之后触发;如果要在Form1和DSVDataSet之间进行通信,最好使用事件,而不是尝试将对表单的引用传递到DSVDataSet。如果您已经为此工作了2天,那么您肯定有时间创建一个。不管怎样,你必须这样做才能测试。但是,不要把整个项目中的所有源代码都发布出来。对不起,我是新来的。完成。我留下了dataGridView_RowPostPaint之类的内容,因为在使用F10跟踪错误之后,这些事件在Application.Run(new Form1())之后触发;这个看起来很漂亮。我有另一个建议,我发现您的dataset对象包含对winform的引用!这是v