我有带按钮的窗口,触发冗长的处理。我把处理放在一个单独的线程中,但是 - 令我惊讶的是 - 无论如何它都会冻结GUI。没有控制刷新,我甚至无法移动窗口。
所以问题是如何启动线程,因此它不会干扰GUI,即GUI总是最新的(在处理时我更改数据和GUI显示它的一些部分)?
这就是我当前开始线程的方式:
var thread = new Thread(doLearn);
thread.IsBackground = true;
thread.Start();
强:
处理是一个带有数学运算的大循环,甚至没有分配内存,在UI端我有控件绑定(WPF)到数据,比如主循环的当前迭代次数。每次主循环“滴答”时都应该刷新它。循环的计数器是一个属性,它会在每次更改时触发OnPropertyChanged(经典的WPF绑定)。
好的,所以乔恩击中头部(谁是惊喜?;-D) - 谢谢!问题来自更换计数器。当我使用计数器时,本地计数器GUI被刷新 - 我的意思是我可以移动窗口,但是......我看不到计数器的显示。
我在这里 - 一个带有这种数据绑定的WPF GUI
<TextBlock Text="{Binding Path=Counter"/>
我当然有Counter属性,每次更改都会发送事件PropertyChanged。其中一个听众肯定是GUI。
所以,Jon的回答是有效的“答案”,但是从好的设计POV来看并不完全,因为如果GUI部分必须拉出有关Counter的信息并且每次(比方说)更新显示3秒,为什么会有人使用数据绑定?对我来说,这种方法使数据绑定理念无效。
理论上,我可以将GUI调度程序传递给处理线程,并在GUI线程中执行所有发送,并且它可以工作(我没有尝试过)但是这意味着非GUI部分的紧密耦合GUI部分。
到目前为止,我不知道如何以“正确”的方式做到这一点。到目前为止,最好的猜测是创建TimerDispatcher,但不是在GUI端,而是在处理库内,并立即更新Counter值,但不时地进行所有发送(尽管我还没有尝试)。
小注释:我实际上有更多属性绑定,比如IsRunning,它在处理的开始和结束时都会被更改。这些更改会正确影响显示 - 但计数器更改会在3-4秒内触发大约3000个通知。所以看起来像是干扰问题。我做了另一个测试 - 我部分杀死了数据绑定,因此发送了通知,但GUI没有“接收”它们 - 但正在听它们。在这种情况下,GUI也被冻结。
所以,我仍在听取所有建议 - 谢谢你提前分享。
传奇继续在这里:
How to do the processing and keep GUI refreshed using databinding?
答案 0 :(得分:3)
有许多方法可以在UI线程上运行函数,但最简单且通常最合适的方法是查看BackgroundWorker组件。可以找到许多体面的教程。例如,here。
答案 1 :(得分:3)
应该没问题。可能会冻结你的UI的事情:
Join
?如果你能想出一个显示问题的简短但完整的程序,我相信我们可以帮忙修复它......但它肯定应该没问题。
编辑:好的,现在你已经添加了这个:循环的计数器是一个属性,它会在每次更改时触发OnPropertyChanged(经典的WPF绑定)。
所以你要从非UI线程更新属性?我希望这会导致问题,因为它会从错误的线程触发UI更改。
我建议您采取以下方法:
Dispatcher.BeginInvoke
DispatcherTimer
将“工作人员计数器”中的值复制到UI线程中的“UI计数器”,基本上是轮询它。答案 2 :(得分:0)
我把处理单独放在一起 线程,但 - 令我惊讶的是 - 它 无论如何都会使GUI冻结。
我真的不想告诉你,但是你没有把它放到一个单独的线程中。那个simlpe。
这里有一张海报,前面有一个类似的问题,并且在调用代码时出错,他在线程开始之前基本完成了所有处理,线程jsut返回结果。
答案 3 :(得分:0)
我遇到了同样的情况,并通过两种方式解决了......
使用其他类中的线程,并通过在其构造函数或任何方法中创建Thread来在主应用程序中调用它。
如果你想在同一个类中执行它,那么创建一个调用你的函数的Thread,该函数应该调用Delegate。
参见示例:
public partial class Form1 : Form
{
private delegate void TickerDelegate();
TickerDelegate tickerDelegate1;
public Form1()
{
InitializeComponent();
}
//first solution
// This button event call other class having Thread
private void button1_Click(object sender, EventArgs e)
{
f = new FormFileUpdate("Auto File Updater", this);
f.Visible = true;
this.Visible = false;
}
// Second Solution
private void BtnWatch_Click(object sender, EventArgs e)
{
tickerDelegate1 = new TickerDelegate(SetLeftTicker);
Thread th = new Thread(new ThreadStart(DigitalTimer));
th.IsBackground = true;
th.Start();
}
private void SetLeftTicker()
{
label2.Text=DateTime.Now.ToLongTimeString();
}
public void DigitalTimer()
{
while (true)
{
label2.BeginInvoke(tickerDelegate1, new object[] {});
Thread.Sleep(1000);
}
}
}