将在另一个线程中创建的UserControl添加到表单

时间:2017-01-04 15:03:18

标签: c# .net multithreading winforms

我有一个名为MainAppForm(Thread1)的表单的应用程序。我有一个这种形式的面板,它将托管UserControls。

当用户单击该按钮时,我想创建另一个线程(Thread2),它将创建UserControl的一个实例,并调用Thread1上的方法将UserControl添加到第一段中提到的面板中。 / p>

这就是我从Thread2调用主Thread1的方法

public class SecondThread
{
    public void start()
    {
        ModuleWindow userControl = new ModuleWindow(new Module.ModuleLayer());

        Global.SetModuleWindowThreadSafe(userControl);
    }
}

我的方法将传入的用户控件添加到面板。

public static class Global
{
    private delegate void SetModuleWindowThreadSafeDelegate(UserControl userControl);
    public static void SetModuleWindowThreadSafe(UserControl userControl)
    {
        if (Global.mainAppForm.pnlMain.InvokeRequired)
        {
            Global.mainAppForm.pnlMain.Invoke(
                new SetModuleWindowThreadSafeDelegate(SetModuleWindowThreadSafe),
                userControl);
        }
        else
        {
            Global.mainAppForm.pnlMain.Controls.Add(userControl);
        }
    }
}

在我调用SetModuleWindowThreadSafe()方法后,它会引发

  

跨线程操作无效:控制'menuStrip1'从其创建的线程以外的线程访问。

注意:menuStrip1是UserControl的控件。

如何将在第二个线程中创建的UserControl添加到面板???

更新:

感谢您的回答。我相信他们在某些方面有所帮助,但不符合我的条件。原因是我的MainAppForm(AKTAP项目)和生成的UserControl(KKM项目)正在不同的项目甚至解决方案中创建。 KKM的项目输出是.dll,我在运行时使用反射加载这些dll文件。因此,MainAppForm不知道每个dll中正在生成什么类型​​的用户控件和控件。

我想要做的是按以下顺序:

1- AKTAP项目有一个接口,由KKM项目中的一个类实现 正在构建2- KKM项目并将dll文件放入指定目录 3- AKTAP开始运行并通过过滤1中提到的接口使用反射加载dll文件 4- AKTAP在KKM中调用一个方法,它将生成并返回用户控件 5- AKTAP将返回的usercontrol添加到MainAppForm。 (这是我得到上述例外的地方。)

2 个答案:

答案 0 :(得分:1)

  

如何将第二个帖子中创建的UserControl添加到面板?

你没有。您可以在UI线程中创建UserControl,而不是在某些后台线程中创建。

如果您要进行一些昂贵的CPU绑定计算,以便找出用户控件需要的数据,那么使用另一个线程来计算该数据然后拥有UI线程获取该数据并创建UI控件以显示它。

答案 1 :(得分:0)

Servy是正确的 - 你没有。

然而,你可以!意思是,这是可能的。

从线程传递数据很复杂,但System.ComponentModel.BackgroundWorker(WinForms的一部分)极大地简化了线程操作,并使这样的任务变得相当有趣。

这是一种使用两(2)个Windows窗体的通用技术,一个作为另一个的变量。两者都在相同的命名空间(相同的项目等)。

Form1中:

public partial class Form1 : Form
{

    private Button btnGetInteger;
    private Button btnGetMenuStrip;
    private Button btnGetString;
    private Form2 _form2;
    private Form2.ReturnType _getType;
    private Object _form2Argument;

    public Form1()
    {
        InitializeComponent();
        btnGetInteger = new Button();
        btnGetInteger.Click += Form2_GetInteger;
        btnGetMenuStrip = new Button();
        btnGetMenuStrip.Click += Form2_GetInteger;
        btnGetString = new Button();
        btnGetString.Click += Form2_GetString;
        Shown += (s, e) => { Form2_CreateMenuStrip(s, EventArgs.Empty); };
    }

    public void Form2_ThreadChanged(object sender, ProgressChangedEventArgs e)
    {
        var returned = (Form2.ReturnType)e.ProgressPercentage;
        switch (returned)
        {
            case Form2.ReturnType.MenuStrip:
                var menuStrip = (MenuStrip)e.UserState;
                this.Controls.Add(menuStrip);
                break;
            case Form2.ReturnType.Integer:
                var numberBack = (int)e.UserState;
                Text = String.Format("Form1 : (int){0}", numberBack);
                break;
            case Form2.ReturnType.String:
                var stringBack = e.UserState.ToString();
                Text = String.Format("Form1 : (String){0}", stringBack);
                break;
        }
    }

    public void Form2_ThreadCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        _form2Argument = null;
        if (e.Error != null)
        {
            String title;
            if (_form2 != null)
            {
                title = String.Format("{0}: {1}", _form2.Text, e.Error.GetType());
            } else
            {
                title = String.Format("Form2: {0}", e.Error.GetType());
            }
            MessageBox.Show(e.Error.Message, title, MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
        if (_form2 != null)
        {
            _form2.Close();
            _form2.Dispose();
            _form2 = null;
        }
        btnGetInteger.Enabled = true;
        btnGetMenuStrip.Enabled = true;
        btnGetString.Enabled = true;
    }

    private void Form2_CreateMenuStrip(object sender, EventArgs e)
    {
        if (_form2 == null)
        {
            _getType = Form2.ReturnType.MenuStrip;
            var item = new ToolStripMenuItem(Text);
            item.Click += Form2_GetInteger;
            _form2Argument = item;
            Form2_StartWork();
        }
    }

    private void Form2_GetInteger(object sender, EventArgs e)
    {
        if (_form2 == null)
        {
            _getType = Form2.ReturnType.Integer;
            Form2_StartWork();
        }
    }

    private void Form2_GetString(object sender, EventArgs e)
    {
        if (_form2 == null)
        {
            _getType = Form2.ReturnType.String;
            Form2_StartWork();
        }
    }

    private void Form2_StartWork()
    {
        btnGetInteger.Enabled = false;
        btnGetMenuStrip.Enabled = false;
        btnGetString.Enabled = false;
        _form2 = new Form2();
        _form2.Show(); // Show returns immediately
        _form2.StartThread(this, _form2Argument, _getType);

    }

}

Form2_ThreadChanged Form2_ThreadCompleted 都设置为PUBLIC,以便 Form2 的实例可以看到它们。

窗体2:

public partial class Form2 : Form
{
    private ReturnType _getType; // thread safe
    private BackgroundWorker _bwThread;

    public enum ReturnType { MenuStrip, String, Integer }

    public Form2() // Do Not Call this method
    {
        InitializeComponent();
    }

    public void StartThread(Form1 parent, Object argument, ReturnType getType)
    {
        _getType = getType;
        if (_bwThread == null)
        {
            _bwThread = new BackgroundWorker() {
                WorkerReportsProgress = true,
                WorkerSupportsCancellation = true
            };
            _bwThread.DoWork += ThreadWork;
            _bwThread.ProgressChanged += parent.Form2_ThreadChanged;
            _bwThread.RunWorkerCompleted += parent.Form2_ThreadCompleted;
        }
        if (!_bwThread.IsBusy)
        {
            _bwThread.RunWorkerAsync(argument);
        }
    }

    private void ThreadWork(object sender, DoWorkEventArgs e)
    {
        switch (_getType)
        {
            case ReturnType.MenuStrip:
                var menuStrip = new MenuStrip();
                if (e.Argument != null)
                {
                    var mi = (ToolStripMenuItem)e.Argument;
                    menuStrip.Items.Add(mi);
                }
                _bwThread.ReportProgress((int)_getType, menuStrip);
                break;
            case ReturnType.Integer:
                var numberBack = 1;
                _bwThread.ReportProgress((int)_getType, numberBack);
                break;
            case ReturnType.String:
                var stringBack = "Worker String";
                _bwThread.ReportProgress((int)_getType, stringBack);
                break;
        }
    }

}

如果你创建了一个名为Form1和Form2的带有2个空表单的新小项目,你可以进入代码并简单地将上面的所有内容粘贴到这两个表单中。

完成后,只需在所有方法(公共和私有)上放置断点,看看它们是如何工作的。