private void btnNext_Click(object sender, EventArgs e)
{
try
{
var emptyDelegate = new EmptyDelegate(Update);
emptyDelegate.BeginInvoke(null, null);
}
catch (Exception ex)
{
lblMessage.Text = ex.Message;
}
} void Update()
{
var i = lstUser.SelectedIndex;
while (i + 1 < lstUser.Items.Count - 1)
{
lstUser.SelectedIndex = i + 1;
System.Threading.Thread.Sleep(2000);
btnLogin_Click(null, null);
System.Threading.Thread.Sleep(2000);
btnBack_Click(null, null);
System.Threading.Thread.Sleep(2000);
i = i + 1;
}
}我想明一个线程,想来更新界面,一开始我是在单线程中的,但这样界面会卡死
现在我开多一个线程提示:线程间操作无效: 从不是创建控件“lstUser”的线程访问它。 谢谢正确写法应该是怎样,谢谢
解决方案 »
- ASP.NET 文件夹删除/更改权限问题
- C# 二次开发AutoCAD的SaveAs函数怎么写参数?
- 如何实现datagridview中单元格中添加复选框和text以及多个复选框的定制?
- 字符编码的问题
- C#winfrom开发,在窗体中打开Excel文件
- 关于反射
- 从Listview中拖拽一个Listview Item到一个Listbox中的一个Listboxitem上,如何得知鼠标是在哪个Listboxitem上释放的?
- 谁有空讲一下private void button1_Click(object sender, System.EventArgs e) 里的sender和e是干什么用的,msdn上讲解,我没看懂
- 关于非托管代码返回结构体指针的内容,该怎么调出来呢。~~~~~~~~~GGJJ们帮忙啊。~~~~~~
- 送给大家《MSDN》
- c#bindingSource1控件和datagridView控件的问题
- 高手进,连接数据库,WinForm分层显示,留言服务互动系统
btnLogin_Click(null, null);
}));
this.Invoke(new MethodInfo(delegate {
btnBack_Click(null, null);
}));
CheckForIllegalCrossThreadCalls = false;
要记得前面加入using System.Threading;
肯定行的!我有过这问题。
//在你的线程方法里的第一句写上
CheckForIllegalCrossThreadCalls = false;
void Update()
{
var i = (int)this.Invoke(new Func<int>(() =>
{
return lstUser.SelectedIndex;
})); while (i <= (int)this.Invoke(new Func<int>(() =>
{
return lstUser.Items.Count - 1;
})))
{
this.Invoke(new Action(() =>
{
lstUser.SelectedIndex = i;
})); System.Threading.Thread.Sleep(3000);
this.Invoke(new MethodInvoker(delegate
{
btnLogin_Click(null, null);
}));
System.Threading.Thread.Sleep(3000);
this.Invoke(new MethodInvoker(delegate
{
btnBonus_Click(null, null);
}));
System.Threading.Thread.Sleep(3000);
i = i + 1;
}
}这个是我改写的,但感觉写的不好
怎么写更好更科学呢
{
try
{
Thread th = new Thread(DoUpdate);
th.Start();
}
catch (Exception ex)
{
lblMessage.Text = ex.Message;
}
} void DoUpdate()
{
this.Invoke(new Action(Update));
} void Update()
{
var i = lstUser.SelectedIndex;
while (i + 1 < lstUser.Items.Count - 1)
{
lstUser.SelectedIndex = i + 1;
System.Threading.Thread.Sleep(2000);
btnLogin_Click(null, null);
System.Threading.Thread.Sleep(2000);
btnBack_Click(null, null);
System.Threading.Thread.Sleep(2000);
i = i + 1;
}
}
用这样的方式可能会好点。
private void btnNext_Click(object sender, EventArgs e)
{ try
{
var emptyDelegate = new EmptyDelegate(Update2);
emptyDelegate.BeginInvoke(null, null);
}
catch (Exception ex)
{
lblMessage.Text = ex.Message;
}
} void Update2()
{
if (this.InvokeRequired)
{
this.Invoke(new Action(Update2));
}
else
{
var i = lstUser.SelectedIndex; while (i <= lstUser.Items.Count - 1)
{
lstUser.SelectedIndex = i; System.Threading.Thread.Sleep(3000);
btnLogin_Click(null, null);
System.Threading.Thread.Sleep(3000);
btnBonus_Click(null, null); System.Threading.Thread.Sleep(3000);
i = i + 1;
}
}
}我参照网上的写法,但这样会卡死主线程
因为invoke后就是主线程了。
在invoke的函数里面Sleep或是做其他操作自然会阻塞主线程。只有在操作控件的时候一句一句的invoke向你上面那么写,如果btnLogin_Click里面还有Sleep的话依然阻塞主线程,还得写invoke
你可以在Form的构造函数中写: public Form()
{
InitializeComponent();
CheckForIllegalCrossThreadCalls = false;//不捕捉跨线程异常
}
然后直接开线程去调用方法就可以了: private void btnNext_Click(object sender, EventArgs e)
{
try
{
Thread th = new Thread(Update);
th.Start();
}
catch (Exception ex)
{
lblMessage.Text = ex.Message;
}
} void Update()
{
var i = lstUser.SelectedIndex;
while (i + 1 < lstUser.Items.Count - 1)
{
lstUser.SelectedIndex = i + 1;
System.Threading.Thread.Sleep(2000);
btnLogin_Click(null, null);
System.Threading.Thread.Sleep(2000);
btnBack_Click(null, null);
System.Threading.Thread.Sleep(2000);
i = i + 1;
}
}不过不太推荐使用这种方式。
简单的一个方法是用循环代替Thread.Sleep: private void btnNext_Click(object sender, EventArgs e)
{
try
{
Update(); // 在主线程上执行,就可以省略跨线程调要用control.Invoke这种形式
}
catch (Exception ex)
{
lblMessage.Text = ex.Message;
}
} void Update()
{
var i = lstUser.SelectedIndex;
while (i <= lstUser.Items.Count - 1)
{
lstUser.SelectedIndex = i;
Wait(2000);
btnLogin_Click(null, null);
Wait(2000);
btnBack_Click(null, null);
Wait(2000);
i = i + 1;
}
} void Wait(int timeout)
{
var tEnd = DateTime.Now + TimeSpan.FromMilliseconds(timeout);
// 让消息泵继续工作,直到结束时间
while (DateTime.Now < tEnd)
Application.DoEvents();
}上面这种方式只是比较简便,但不一定是好的做法。好的做法还是你在7楼写的那种,每次跨线程访问UI控件时都调用Invoke方法。
如果嫌麻烦的话,可以写个扩展方法类让简化调用语句: public static class ControlInvokeExtensions
{
public static TResult Invoke<TControl, TResult>(this TControl control, Func<TControl, TResult> func) where TControl : Control
{
return (TResult)control.Invoke(new Func<TResult>(()=>func(control)));
} public static void Invoke<TControl>(this TControl control, Action<TControl> action) where TControl : Control
{
control.Invoke(new MethodInvoker(() => action(control)));
}
}调用时就比较方便了,代码跟7楼是等效的: private void btnNext_Click(object sender, EventArgs e)
{
try
{
var emptyDelegate = new EmptyDelegate(Update);
emptyDelegate.BeginInvoke(null, null);
}
catch (Exception ex)
{
lblMessage.Text = ex.Message;
}
} void Update()
{
var i = lstUser.Invoke(x => x.SelectedIndex);
while (i <= lstUser.Invoke(x => x.Items.Count -1))
{
lstUser.Invoke(x => x.SelectedIndex = i);
System.Threading.Thread.Sleep(3000);
this.Invoke(x=>btnLogin_Click(null, null));
System.Threading.Thread.Sleep(3000);
this.Invoke(x=>btnBonus_Click(null, null));
System.Threading.Thread.Sleep(3000);
i = i + 1;
}
}
用 Invoke 函数
SetValueHandler svh;
private void Set()
{
svh = new SetValueHandler(SetControlValue);
svh.Invoke("label1", "test1");
svh.Invoke("label2", "test2");
} private void SetControlValue(string key, string value)
{
// Control control = this.Controls.Find(key, true)[0];
此处判断下 存不存在控件。
if (control.InvokeRequired)
{
control.Invoke(svh, new object[] { key, value });
}
else
{
control.Text = value;
}
}