我有一个设想。(Windows Forms, c#, .NET)
There is a main form which hosts some user control.
The user control does some heavy data operation, such that if I directly call the UserControl_Load method the UI become nonresponsive for the duration for load method execution.
To overcome this I load data on different thread (trying to change existing code as little as I can)
I used a background worker thread which will be loading the data and when done will notify the application that it has done its work.
Now came a real problem. All the UI (main form and its child usercontrols) was created on the primary main thread. In the LOAD method of the usercontrol I'm fetching data based on the values of some control (like textbox) on userControl.
伪代码看起来像这样:
代码1
UserContrl1_LoadDataMethod()
{
if (textbox1.text == "MyName") // This gives exception
{
//Load data corresponding to "MyName".
//Populate a globale variable List<string> which will be binded to grid at some later stage.
}
}
它给出的例外是
跨线程操作无效:从创建该控件的线程以外的线程访问的控件。
为了了解更多关于这一点,我做了一些谷歌搜索,并提出了一个建议,如使用以下代码
代码2
UserContrl1_LoadDataMethod()
{
if (InvokeRequired) // Line #1
{
this.Invoke(new MethodInvoker(UserContrl1_LoadDataMethod));
return;
}
if (textbox1.text == "MyName") // Now it won't give an exception
{
//Load data correspondin to "MyName"
//Populate a globale variable List<string> which will be binded to grid at some later stage
}
}
但我似乎还是回到了原点。再次申请
变得反应迟钝。这似乎是由于第1行if条件的执行。加载任务再次由父线程完成,而不是由我生成的第三个线程完成。
我不知道我认为这是对还是错。
我如何解决这个问题,以及执行第1行if块的效果是什么?
情况是这样的:我想根据控件的值将数据加载到全局变量中。我不想改变子线程中控件的值。我不会从子线程中做。
因此只有访问值,才能从数据库中获取相应的数据。
和之前的答案一样,
但是一个非常简短的添加,允许使用所有的Control属性而没有跨线程调用异常。
辅助方法
/// <summary>
/// Helper method to determin if invoke required, if so will rerun method on correct thread.
/// if not do nothing.
/// </summary>
/// <param name="c">Control that might require invoking</param>
/// <param name="a">action to preform on control thread if so.</param>
/// <returns>true if invoke required</returns>
public bool ControlInvokeRequired(Control c, Action a)
{
if (c.InvokeRequired) c.Invoke(new MethodInvoker(delegate
{
a();
}));
else return false;
return true;
}
示例使用
// usage on textbox
public void UpdateTextBox1(String text)
{
//Check if invoke requied if so return - as i will be recalled in correct thread
if (ControlInvokeRequired(textBox1, () => UpdateTextBox1(text))) return;
textBox1.Text = ellapsed;
}
//Or any control
public void UpdateControl(Color c, String s)
{
//Check if invoke requied if so return - as i will be recalled in correct thread
if (ControlInvokeRequired(myControl, () => UpdateControl(c, s))) return;
myControl.Text = s;
myControl.BackColor = c;
}
当我在xamarin工作室外的一个visual studio winforms原型项目中编程iOS-Phone单触摸应用程序控制器时,我发现了这个需求。比起xamarin studio,我更倾向于在VS中编程,我希望控制器能够与手机框架完全分离。通过这种方式,在Android和Windows Phone等其他框架上实现这个功能将更容易用于未来的使用。
我想要一种解决方案,使GUI可以响应事件,而无需处理每次单击按钮背后的跨线程切换代码。基本上让类控制器来处理,以保持客户端代码简单。你可能在GUI上有很多事件,如果你可以在类的一个地方处理它,会更干净。我不是一个多头专家,让我知道,如果这是有缺陷的。
public partial class Form1 : Form
{
private ExampleController.MyController controller;
public Form1()
{
InitializeComponent();
controller = new ExampleController.MyController((ISynchronizeInvoke) this);
controller.Finished += controller_Finished;
}
void controller_Finished(string returnValue)
{
label1.Text = returnValue;
}
private void button1_Click(object sender, EventArgs e)
{
controller.SubmitTask("Do It");
}
}
GUI表单不知道控制器正在运行异步任务。
public delegate void FinishedTasksHandler(string returnValue);
public class MyController
{
private ISynchronizeInvoke _syn;
public MyController(ISynchronizeInvoke syn) { _syn = syn; }
public event FinishedTasksHandler Finished;
public void SubmitTask(string someValue)
{
System.Threading.ThreadPool.QueueUserWorkItem(state => submitTask(someValue));
}
private void submitTask(string someValue)
{
someValue = someValue + " " + DateTime.Now.ToString();
System.Threading.Thread.Sleep(5000);
//Finished(someValue); This causes cross threading error if called like this.
if (Finished != null)
{
if (_syn.InvokeRequired)
{
_syn.Invoke(Finished, new object[] { someValue });
}
else
{
Finished(someValue);
}
}
}
}