如何从另一个类中运行的另一个线程更新UI
我目前正在用C#编写我的第一个程序,并且对这种语言非常陌生(到目前为止,仅用于C语言)。我做了很多研究,但是所有答案都太笼统了,我根本无法解决。
所以这是我的(很常见)问题:我有一个WPF应用程序,该应用程序从用户填写的几个文本框中获取输入,然后使用它们对它们进行大量计算。他们应该花费2-3分钟左右的时间,所以我想更新进度条和文本块,告诉我当前的状态是什么。另外,我还需要存储来自用户的UI输入并将其提供给线程,因此,我有一个第三类,用于创建对象并将该对象传递给后台线程。显然,我会在另一个线程中运行计算,因此UI不会冻结,但是我不知道如何更新UI,因为所有计算方法都是另一个类的一部分。经过大量的研究,我认为最好的方法是使用调度程序和TPL,而不是背景工作人员,
这是我程序的非常简单的结构:
public partial class MainWindow : Window
{
public MainWindow()
{
Initialize Component();
}
private void startCalc(object sender, RoutedEventArgs e)
{
inputValues input = new inputValues();
calcClass calculations = new calcClass();
try
{
input.pota = Convert.ToDouble(aVar.Text);
input.potb = Convert.ToDouble(bVar.Text);
input.potc = Convert.ToDouble(cVar.Text);
input.potd = Convert.ToDouble(dVar.Text);
input.potf = Convert.ToDouble(fVar.Text);
input.potA = Convert.ToDouble(AVar.Text);
input.potB = Convert.ToDouble(BVar.Text);
input.initStart = Convert.ToDouble(initStart.Text);
input.initEnd = Convert.ToDouble(initEnd.Text);
input.inita = Convert.ToDouble(inita.Text);
input.initb = Convert.ToDouble(initb.Text);
input.initc = Convert.ToDouble(initb.Text);
}
catch
{
MessageBox.Show("Some input values are not of the expected Type.", "Wrong Input", MessageBoxButton.OK, MessageBoxImage.Error);
}
Thread calcthread = new Thread(new ParameterizedThreadStart(calculations.testMethod);
calcthread.Start(input);
}
public class inputValues
{
public double pota, potb, potc, potd, potf, potA, potB;
public double initStart, initEnd, inita, initb, initc;
}
public class calcClass
{
public void testmethod(inputValues input)
{
Thread.CurrentThread.Priority = ThreadPriority.Lowest;
int i;
//the input object will be used somehow, but that doesn't matter for my problem
for (i = 0; i < 1000; i++)
{
Thread.Sleep(10);
}
}
}
如果有人有一个简单的解释如何从测试方法中更新用户界面,我将不胜感激。由于我是C#和面向对象编程的新手,因此我很可能不会理解过于复杂的答案,但我会尽力而为。
同样,如果某人总体上有一个更好的主意(也许使用backgroundworker或其他工具),我也很乐意看到它。
-
首先,您需要使用
Dispatcher.Invoke
来从另一个线程更改UI,并从另一个类进行更改,您可以使用事件。
然后,您可以在主类中注册该事件,然后将更改分发给UI,并在要通知UI的计算类中引发事件:class MainWindow : Window { private void startCalc() { //your code CalcClass calc = new CalcClass(); calc.ProgressUpdate += (s, e) => { Dispatcher.Invoke((Action)delegate() { /* update UI */ }); }; Thread calcthread = new Thread(new ParameterizedThreadStart(calc.testMethod)); calcthread.Start(input); } } class CalcClass { public event EventHandler ProgressUpdate; public void testMethod(object input) { //part 1 if(ProgressUpdate != null) ProgressUpdate(this, new YourEventArgs(status)); //part 2 } }
更新:
看来这仍然是一个经常访问的问题,我想用现在的方式(使用.NET 4.5)更新此答案-这有点长,因为我将展示一些不同的可能性:class MainWindow : Window { Task calcTask = null; void buttonStartCalc_Clicked(object sender, EventArgs e) { StartCalc(); } // #1 async void buttonDoCalc_Clicked(object sender, EventArgs e) // #2 { await CalcAsync(); // #2 } void StartCalc() { var calc = PrepareCalc(); calcTask = Task.Run(() => calc.TestMethod(input)); // #3 } Task CalcAsync() { var calc = PrepareCalc(); return Task.Run(() => calc.TestMethod(input)); // #4 } CalcClass PrepareCalc() { //your code var calc = new CalcClass(); calc.ProgressUpdate += (s, e) => Dispatcher.Invoke((Action)delegate() { // update UI }); return calc; } } class CalcClass { public event EventHandler<EventArgs<YourStatus>> ProgressUpdate; // #5 public TestMethod(InputValues input) { //part 1 ProgressUpdate.Raise(this, status); // #6 - status is of type YourStatus //part 2 } } static class EventExtensions { public static void Raise<T>(this EventHandler<EventArgs<T>> theEvent, object sender, T args) { if (theEvent != null) theEvent(sender, new EventArgs<T>(args)); } }
@ 1)如何启动“同步”计算并在后台运行它们
@ 2)如何“异步”和“等待”启动它:在这里,在方法返回之前执行并完成了计算,但是由于
async
/await
UI没有被阻塞(
顺便说一句,此类事件处理程序是的唯一有效用法)async void
因为事件处理程序必须返回void
-async Task
在所有其他情况下使用)@
3)Thread
现在使用代替新的Task
。为了以后能够检查其(成功)完成情况,我们将其保存在全局calcTask
成员中。在后台,这还会启动一个新线程并在该线程中运行操作,但是它更易于处理并具有其他一些好处。@ 4)在这里我们也开始动作,但是这次我们返回任务,因此“异步事件处理程序”可以“等待它”。我们还可以创建
async Task CalcAsync()
然后await Task.Run(() => calc.TestMethod(input)).ConfigureAwait(false);
(FYI:ConfigureAwait(false)
避免死锁,如果您使用async
/
,则应仔细阅读此内容,await
因为这里将对此进行大量解释),这将导致相同的工作流程,但Task.Run
唯一的“等待操作”,这是最后一个操作,我们可以简单地返回任务并保存一个上下文切换,从而节省了一些执行时间。@ 5)现在,我在这里使用“强类型通用事件”,以便我们可以轻松地传递和接收“状态对象”
@
6)在这里,我使用下面定义的扩展名(除了易于使用之外)解决了旧示例中可能出现的竞争情况。如果此时恰好在另一个线程中删除了事件处理程序,则可能发生了该事件null
在if
-check之后但在调用之前发生的情况。这不可能在这里发生,因为扩展获取了事件委托的“副本”,并且在相同情况下,处理程序仍在Raise
方法内注册。
-
javafx,从另一个线程更新ui
2021-01-31 关注 0 浏览124 1答案
-
从android中的另一个线程更新ui
2021-01-29 关注 0 浏览122 1答案
-
如何从另一个线程调用UI方法
2021-02-02 关注 0 浏览135 1答案
-
如何从另一个线程更新GUI?
2021-02-02 关注 0 浏览124 1答案
-
如何从另一个线程更新 GUI?
2022-03-22 关注 0 浏览85 1答案
-
从另一个线程更新JLabel
2021-01-29 关注 0 浏览94 1答案
-
如何从Java中的另一个线程更新SWT GUI
2021-01-30 关注 0 浏览68 1答案
-
从另一个线程更新SWT对象
2021-01-29 关注 0 浏览121 1答案
-
从另一个运行FTP下载的线程更新PyQt进度
2021-01-29 关注 0 浏览64 1答案
-
从另一个线程在主线程中运行代码
2022-04-11 关注 0 浏览31 1答案