C#在异步/等待代码中实现dispatcher
我有一个wpf应用程序,其中一个功能是复制文件。它功能正常,但有时会冻结UI,因此我尝试向其添加异步/等待函数 代码如下:C#在异步/等待代码中实现dispatcher,c#,wpf,async-await,invoke,C#,Wpf,Async Await,Invoke,我有一个wpf应用程序,其中一个功能是复制文件。它功能正常,但有时会冻结UI,因此我尝试向其添加异步/等待函数 代码如下: if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) { string[] files = filePickedTextBox.Text.Split('\n'); await Task.Run(() =>
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
string[] files = filePickedTextBox.Text.Split('\n');
await Task.Run(() =>
{
foreach (var file in files)
{
try
{
AddValueLoadingBar(20, true);
File.Copy(file, Path.Combine(dialog.SelectedPath, Path.GetFileName(file)));
newDirTextBox.Text = Path.Combine(dialog.SelectedPath, Path.GetFileName(file));
}
catch(Exception ex)
{
newDirTextBox.Text = ex.ToString();
}
finally
{
AddValueLoadingBar(100, true);
}
}
});
我曾尝试在多个位置添加dispatcher.invoke,但总是得到一个错误,即当它需要与文本框对话时,它请求来自不同线程的请求
我已经做了几个小时,在网上搜索了许多stackoverflow线程和其他文档,但我不知道如何实现这一点
我也知道我的进度条实现很糟糕,但我现在并不担心,因为我无法在没有冻结的情况下运行它
任何帮助都会很好
Thx这里是您使用该类的地方
您知道对话框构造函数将在主线程上运行。因此,在创建对话框时,将主线程的调度程序记录为成员变量
然后,在任务线程内,确保通过该调度程序将任何更新更改调度到windows线程
using System.Windows;
class MyTestDialog
{
readonly Dispatcher _uiThreadDispatcher = Dispatcher.CurrentDispatcher;
public async Task DoSomethingInBackround()
{
await Task.Run(
() =>
{
//
// Do a bunch of stuff here
//
_uiThreadDispatcher.BeginInvoke(
new Action(
() => newDirTextBox.Text = "Some progress text";
)
);
//
// Do some more stuff
//
}
);
}
}
在
任务中。运行如果不调用对UI线程的回调,则无法访问或更新任何UI元素。您的每一条语句似乎都在与UI交互。您需要将所有UI工作提取到代码中,然后再将繁重的工作推到任务中
现在,我建议使用微软的反应式框架(aka Rx)-NuGetSystem.Reactive.Windows.Threading
(对于WPF位),并使用System.Reactive.Linq添加代码>-然后您可以执行以下操作:
IDisposable subscription =
files
.Select(f => new
{
source = f,
destination = Path.Combine(dialog.SelectedPath, Path.GetFileName(f))
})
.ToObservable()
.SelectMany(x => Observable.Start(() =>
{
File.Copy(x.source, x.destination);
return x.destination;
}))
.Select((d, n) => new { destination = d, progress = 100 * (n + 1) / files.Length })
.ObserveOnDispatcher()
.Subscribe(
x =>
{
AddValueLoadingBar(x.progress, true);
newDirTextBox.Text = x.destination;
},
ex => newDirTextBox.Text = ex.ToString(),
() => AddValueLoadingBar(100, true));
这完成了将实际文件复制发送到后台线程所需的所有操作,但仍然会将所有更新封送到UI。这不是winform,因此不使用backgroundworker。编辑主要帖子以陈述其WPF。这是否回答了您的问题。您应该使用BeginInvoke
而不是Invoke
,因为后者可能导致线程deadlock@MattU-BackgroundWorker
不再是做这种事情的方式。现在有很多更好的选项。@user15919119-为什么要在WPF中使用System.Windows.Forms
对话框?我会更改它。谢谢!与引入并发性的.SelectMany
不同,.Select(…).Merge(1)
可能更接近OP试图做的事情。在这种情况下,可能需要将Observable.Start
包装成Observable.Defer
。@TheodorZoulias-这可能是一个合理的选择。