我有一个名为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。 (这是我得到上述例外的地方。)
答案 0 :(得分:1)
如何将第二个帖子中创建的UserControl添加到面板?
你没有。您可以在UI线程中创建UserControl,而不是在某些后台线程中创建。
如果您要进行一些昂贵的CPU绑定计算,以便找出用户控件需要的数据,那么使用另一个线程来计算该数据然后拥有UI线程获取该数据并创建UI控件以显示它。
答案 1 :(得分:0)
Servy是正确的 - 你没有。
然而,你可以!意思是,这是可能的。
从线程传递数据很复杂,但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;
}
}
}
如果你创建了一个名为Form1和Form2的带有2个空表单的新小项目,你可以进入代码并简单地将上面的所有内容粘贴到这两个表单中。
完成后,只需在所有方法(公共和私有)上放置断点,看看它们是如何工作的。