【问题标题】:Add a UserControl that is created in another thread to a form将在另一个线程中创建的 UserControl 添加到表单
【发布时间】:2017-01-04 15:03:18
【问题描述】:

我有一个名为 MainAppForm (Thread1) 的应用程序。我有一个这种形式的面板,它将承载用户控件。

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

这就是我从 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 将返回的用户控件添加到 MainAppForm。 (这就是我得到上述异常的地方。)

【问题讨论】:

  • 不要在单独的线程上创建控件。

标签: c# .net multithreading winforms


【解决方案1】:

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

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

如果您需要进行一些昂贵的 CPU 密集型计算来确定用户控件需要哪些数据,然后使用另一个线程计算该数据,然后UI 线程获取该数据并创建 UI 控件来显示它。

【讨论】:

    【解决方案2】:

    Servy 是正确的 - 你没有。

    但是,你可以!意思是,有可能。

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

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

    表格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_ThreadChangedForm2_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 的空表单,您可以进入代码并将上面的所有内容粘贴到这两个表单中。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-16
      相关资源
      最近更新 更多