简体   繁体   English

将在另一个线程中创建的UserControl添加到窗体

[英]Add a UserControl that is created in another thread to a form

I have an application that has a form named as MainAppForm (Thread1). 我有一个名为MainAppForm(Thread1)的窗体的应用程序。 I have a panel in this form which will host UserControls. 我有这种形式的面板,它将托管UserControls。

When a user clicks the button, I want to create another thread (Thread2) which will create an instance of the UserControl and call a method that is on the Thread1 to add UserControl to the panel in mentioned in the first paragraph. 当用户单击按钮时,我想创建另一个线程(Thread2),该线程将创建UserControl的实例并调用Thread1上的方法以将UserControl添加到第一段中提到的面板中。

This is how I call main Thread1 from Thread2 这就是我从Thread2调用主Thread1的方式

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

        Global.SetModuleWindowThreadSafe(userControl);
    }
}

My method that will add the passed in user control to the panel. 我的方法会将传入的用户控件添加到面板中。

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);
        }
    }
}

After I do the call in the SetModuleWindowThreadSafe() method it raises 在SetModuleWindowThreadSafe()方法中进行调用后,它引发

Cross-thread operation not valid: Control 'menuStrip1' accessed from a thread other than the thread it was created on. 跨线程操作无效:从创建该线程的线程以外的线程访问控件“ menuStrip1”。

Note: menuStrip1 is a control on UserControl. 注意:menuStrip1是UserControl上的控件。

How can I add the UserControl that is created in the second thread to the panel??? 如何将第二个线程中创建的UserControl添加到面板中?

UPDATED: 更新:

Thanks for the answers. 感谢您的回答。 I am sure they're helpful in some ways but not in my condition. 我确信他们在某些方面会有所帮助,但对我而言并非如此。 The reason is my MainAppForm(AKTAP project) and the generated UserControl's(KKM project) are being created in different projets even solutions. 原因是我的MainAppForm(AKTAP项目)和生成的UserControl(KKM项目)正在不同的Projets甚至解决方案中创建。 The project output of KKM is a .dll and I am loading those dll files on runtime using reflections. KKM的项目输出是一个.dll,我正在使用反射在运行时加载这些dll文件。 So MainAppForm does not know what type of usercontrols and controls are being generated in each dll. 因此MainAppForm不知道每个dll中正在生成哪种类型的用户控件和控件。

What I want to do is in the following order: 我要按以下顺序进行操作:

1- AKTAP project has an interface which is implemented by a class in KKM project. 1- AKTAP项目具有一个接口,该接口由KKM项目中的类实现。
2- KKM project is being built and puts dll files to a specified directory. 2- KKM项目正在构建中,并将dll文件放入指定的目录。
3- AKTAP starts to run and loads dll files using reflections by filtering the interface mentioned in 1. 3- AKTAP通过过滤第1节中提到的接口开始运行并使用反射加载dll文件。
4- AKTAP calls a method in KKM hich will generate and return the usercontrol. 4- AKTAP在KKM hich中调用方法将生成并返回用户控件。
5- AKTAP adds the returned usercontrol to the MainAppForm. 5-AKTAP将返回的用户控件添加到MainAppForm。 (And this is where I get the exception above.) (这是我在上面得到异常的地方。)

How can I add the UserControl that is created in the second thread to the panel? 如何将第二个线程中创建的UserControl添加到面板中?

You don't. 你不知道 You create the UserControl in the UI thread, rather than in some background thread. 您可以在UI线程中而不是在某些后台线程中创建UserControl。

If you have some expensive CPU bound computation to do in order to figure out what data the user control will need then use another thread to compute that data and then have the UI thread take that data and create the UI controls to display it. 如果要进行一些昂贵的CPU绑定计算以找出用户控件将需要的数据,请使用另一个线程来计算该数据 ,然后让UI线程获取该数据并创建UI控件以显示该数据。

Servy is correct - you don't. 服务是正确的-您不正确。

However, you can! 但是,您可以! Meaning, it is possible. 意思是有可能的。

Passing data from a thread is complicated, but the System.ComponentModel.BackgroundWorker (part of WinForms) greatly simplifies threading operations and makes tasks like this rather fun to do. 从线程传递数据很复杂,但是System.ComponentModel.BackgroundWorker (WinForms的一部分)极大地简化了线程操作,并使诸如此类的任务变得很有趣。

Here is a generic technique that uses two (2) Windows Forms, one as a variable inside the other. 这是一种使用两(2)个Windows窗体的通用技术,一个窗体作为另一个窗体中的变量。 Both are in the same namespace (same project, etc). 两者都在同一个名称空间中(相同的项目等)。

Form1: 表格1:

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 and Form2_ThreadCompleted are both set to PUBLIC so that they can be visible by the instance of Form2 . Form2_ThreadChangedForm2_ThreadCompleted都设置为PUBLIC,以便它们可以被Form2实例看到。

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

}

If you make a new, small project with 2 empty forms in it called Form1 and Form2, you can go into the code and simply paste everything from above into those two forms. 如果您创建一个新的小型项目,其中有2个空窗体,分别称为Form1和Form2,则可以进入代码,然后简单地将上面的所有内容粘贴到这两个窗体中。

With that done, just put breakpoints on all of the methods (both public and private) to see how they work. 完成此操作后,只需在所有方法(公共方法和私有方法)上设置断点,以查看其工作方式。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM