[英]Add a UserControl that is created in another thread to a form
我有一个名为MainAppForm(Thread1)的窗体的应用程序。 我有这种形式的面板,它将托管UserControls。
当用户单击按钮时,我想创建另一个线程(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项目)正在不同的Projets甚至解决方案中创建。 KKM的项目输出是一个.dll,我正在使用反射在运行时加载这些dll文件。 因此MainAppForm不知道每个dll中正在生成哪种类型的用户控件和控件。
我要按以下顺序进行操作:
1- AKTAP项目具有一个接口,该接口由KKM项目中的类实现。
2- KKM项目正在构建中,并将dll文件放入指定的目录。
3- AKTAP通过过滤第1节中提到的接口开始运行并使用反射加载dll文件。
4- AKTAP在KKM hich中调用方法将生成并返回用户控件。
5-AKTAP将返回的用户控件添加到MainAppForm。 (这是我在上面得到异常的地方。)
如何将第二个线程中创建的UserControl添加到面板中?
你不知道 您可以在UI线程中而不是在某些后台线程中创建UserControl。
如果要进行一些昂贵的CPU绑定计算以找出用户控件将需要的数据,请使用另一个线程来计算该数据 ,然后让UI线程获取该数据并创建UI控件以显示该数据。
服务是正确的-您不正确。
但是,您可以! 意思是有可能的。
从线程传递数据很复杂,但是System.ComponentModel.BackgroundWorker (WinForms的一部分)极大地简化了线程操作,并使诸如此类的任务变得很有趣。
这是一种使用两(2)个Windows窗体的通用技术,一个窗体作为另一个窗体中的变量。 两者都在同一个名称空间中(相同的项目等)。
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实例看到。
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;
}
}
}
如果您创建一个新的小型项目,其中有2个空窗体,分别称为Form1和Form2,则可以进入代码,然后简单地将上面的所有内容粘贴到这两个窗体中。
完成此操作后,只需在所有方法(公共方法和私有方法)上设置断点,以查看其工作方式。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.