从线程更新表单时表单中的空白组件
Blank components in a Form while updating it from a thread
我制作了一个在屏幕上向左移动的表单,但是表单上的所有组件都是空白的。
我把动作的代码放在评论里了,一切正常,所以问题出在我的动作代码上,但我不知道是什么问题。
System.Threading.Thread thread;
private void Form1_Load(object sender, EventArgs e)
{
thread = new System.Threading.Thread(loop);
thread.Start();
}
void loop()
{
this.BeginInvoke((Action)delegate () {
int x = 0;
int y = 0;
int MoveRate = 1;
Point TopLeft = this.Location;
Point TopRight = new Point (this.Location.X + this.Width, this.Location.Y);
while (true)
{
x = x + MoveRate;
this.Location = new Point(x, 150);
System.Threading.Thread.Sleep(10);
}
});
}
这应该会使表单向左移动,但是表单上的组件是空白的。
让我们看看loop()
方法:
void loop()
{
this.BeginInvoke((Action)delegate () {
int x = 0;
int y = 0;
int MoveRate = 1;
Point TopLeft = this.Location;
Point TopRight = new Point (this.Location.X + this.Width, this.Location.Y);
while (true)
{
x = x + MoveRate;
this.Location = new Point(x, 150);
System.Threading.Thread.Sleep(10);
}
});
}
此代码立即在主 UI 线程上调用委托。委托运行一个永不退出的 while(true)
循环。循环一开始执行,UI 线程就被完全控制,没有希望响应其他事件消息,包括绘制事件。
试试这个:
void loop()
{
int x = 0;
int MoveRate = 1;
while(true)
{
x += MoveRate;
this.BeginInvoke((Action)delegate () { this.Location = new Point(x, 150); });
System.Threading.Thread.Sleep(16);
}
}
它们都是相同的代码(除了没有做任何事情的东西),但现在经过安排以便在循环内调用委托。 UI 线程只被短暂阻塞,然后控制 returns 回到循环线程,循环线程会 Sleep 一会儿,然后再打扰 UI。调用非常简单,我可以将其重写为单行。
请注意,我还增加了睡眠时间,因为这样仍然可以让您每秒播放 60 帧。
同一过程的 Async
变体,以测试不同的东西。
(你的线程没有按预期工作的主要原因已经解释过了。如果你使用一个线程然后在闭环中调用 UI 线程,它或多或少就像没有你的代码 运行 完全在不同的线程中:表单没有时间更新自身或其控件)。
当窗体滚动到当前屏幕边界之外时,此方法将终止滚动过程。满足此条件时,退出 while 循环,Task 结束,将 Form 移动到屏幕中央。
任务在 Shown
事件中启动。我认为它比 Load
事件更合适(表格已准备好在此处呈现)。
请注意,此任务或线程均未对 Form.FormClosing
事件添加任何检查,以取消异步过程:如果在执行滚动时关闭窗体,则很可能会出现异常(表格已被处理,因此不再处理)。
private async Task Scroller(int ScreenWidth)
{
int x = 0;
int MoveRate = 2;
while (true)
{
x += MoveRate;
this.BeginInvoke(new MethodInvoker(() => { this.Location = new Point(x, 150);}));
await Task.Delay(10);
if (x > ScreenWidth) break;
};
}
private async void Form_Shown(object sender, EventArgs e)
{
int ScreenWidth = Screen.FromHandle(this.Handle).Bounds.Width;
await this.Scroller(ScreenWidth);
this.Location = new Point((ScreenWidth - this.Width) / 2 , 150);
}
我制作了一个在屏幕上向左移动的表单,但是表单上的所有组件都是空白的。
我把动作的代码放在评论里了,一切正常,所以问题出在我的动作代码上,但我不知道是什么问题。
System.Threading.Thread thread;
private void Form1_Load(object sender, EventArgs e)
{
thread = new System.Threading.Thread(loop);
thread.Start();
}
void loop()
{
this.BeginInvoke((Action)delegate () {
int x = 0;
int y = 0;
int MoveRate = 1;
Point TopLeft = this.Location;
Point TopRight = new Point (this.Location.X + this.Width, this.Location.Y);
while (true)
{
x = x + MoveRate;
this.Location = new Point(x, 150);
System.Threading.Thread.Sleep(10);
}
});
}
这应该会使表单向左移动,但是表单上的组件是空白的。
让我们看看loop()
方法:
void loop()
{
this.BeginInvoke((Action)delegate () {
int x = 0;
int y = 0;
int MoveRate = 1;
Point TopLeft = this.Location;
Point TopRight = new Point (this.Location.X + this.Width, this.Location.Y);
while (true)
{
x = x + MoveRate;
this.Location = new Point(x, 150);
System.Threading.Thread.Sleep(10);
}
});
}
此代码立即在主 UI 线程上调用委托。委托运行一个永不退出的 while(true)
循环。循环一开始执行,UI 线程就被完全控制,没有希望响应其他事件消息,包括绘制事件。
试试这个:
void loop()
{
int x = 0;
int MoveRate = 1;
while(true)
{
x += MoveRate;
this.BeginInvoke((Action)delegate () { this.Location = new Point(x, 150); });
System.Threading.Thread.Sleep(16);
}
}
它们都是相同的代码(除了没有做任何事情的东西),但现在经过安排以便在循环内调用委托。 UI 线程只被短暂阻塞,然后控制 returns 回到循环线程,循环线程会 Sleep 一会儿,然后再打扰 UI。调用非常简单,我可以将其重写为单行。
请注意,我还增加了睡眠时间,因为这样仍然可以让您每秒播放 60 帧。
同一过程的 Async
变体,以测试不同的东西。
(你的线程没有按预期工作的主要原因已经解释过了。如果你使用一个线程然后在闭环中调用 UI 线程,它或多或少就像没有你的代码 运行 完全在不同的线程中:表单没有时间更新自身或其控件)。
当窗体滚动到当前屏幕边界之外时,此方法将终止滚动过程。满足此条件时,退出 while 循环,Task 结束,将 Form 移动到屏幕中央。
任务在 Shown
事件中启动。我认为它比 Load
事件更合适(表格已准备好在此处呈现)。
请注意,此任务或线程均未对 Form.FormClosing
事件添加任何检查,以取消异步过程:如果在执行滚动时关闭窗体,则很可能会出现异常(表格已被处理,因此不再处理)。
private async Task Scroller(int ScreenWidth)
{
int x = 0;
int MoveRate = 2;
while (true)
{
x += MoveRate;
this.BeginInvoke(new MethodInvoker(() => { this.Location = new Point(x, 150);}));
await Task.Delay(10);
if (x > ScreenWidth) break;
};
}
private async void Form_Shown(object sender, EventArgs e)
{
int ScreenWidth = Screen.FromHandle(this.Handle).Bounds.Width;
await this.Scroller(ScreenWidth);
this.Location = new Point((ScreenWidth - this.Width) / 2 , 150);
}