我想通过ObservableCollection
方法将数据从文件导入Add
。而且我还希望在加载文件期间,ProgressBar的属性IsIndeterminate应该为true
。
因此,我将有两个线程:
ObservableCollection
。但是我有两个问题。
ObservableCollection
在未创建线程中不起作用。OR
Dispatcher
,它将阻塞第一个线程,并且ProgressBar无法正常工作。问题:
C#:
public ObservableCollection<MyClass> Collection { get; set; }
public bool ProgressIsIndeterminate { get; set; }
CommandImportBaseFile = new Command(async (o) => { await DoImportFileAsync(); });
private async Task DoImportBaseFileAsync()
{
OpenFileDialog serviceDialogs = new OpenFileDialog();
if (serviceDialogs.ShowDialog() == true)
{
string message = null;
ProgressIsIndeterminate = true;
try
{
var task = Task.Run(() =>
{
if (!System.IO.File.Exists(serviceDialogs.FileName)) throw new Exception("File is not found - " + serviceDialogs.FileName + ".");
MyClass data = new MyClass();
BinaryFormatter binFormat = new BinaryFormatter();
using (Stream fStream = new FileStream(serviceDialogs.FileName, FileMode.Open, FileAccess.Read, FileShare.None))
{
data = (MyClass)binFormat.Deserialize(fStream);
}
foreach (var d in data)
{
Collection.Add(d);
}
});
await task;
}
catch (Exception e)
{
message = e.Message;
}
if (String.IsNullOrEmpty(message))
{
message = "Completed";
}
MessageBox.Show(message);
ProgressIsIndeterminate = false;
}
}
或者我使用Dispatcher
foreach (var d in data)
{
App.Current.Dispatcher.BeginInvoke((Action)delegate()
{
Collection.Add(d);
});
}
XAML:
<ProgressBar
Width="100"
Height="12"
IsIndeterminate="{Binding ProgressIsIndeterminate}"/>
更新:
C#:
public class MyClass
{
public string Name { get; set; }
public int Age { get; set; }
public MyClass()
{
Name = String.Empty;
Age = 0;
}
}
答案 0 :(得分:4)
您要将长期运行的工作与更新UI分开。您的异步代码不应该负责执行和更新UI。例如,您应该使用IProgress<T>
来通知UI有关更改。
以下是示例:
public class MainWindowViewModel : INotifyPropertyChanged
{
private double _progressValue;
public double ProgressValue {
get => _progressValue;
set
{
_progressValue = value;
NotifyPropertyChanged();
}
}
public ObservableCollection<string> Values { get; set; } = new ObservableCollection<string>();
private ICommand _onLoadCommand;
public ICommand OnLoadCommand => _onLoadCommand ??
(_onLoadCommand = new RelayCommand(_ => true, p => OnLoad()));
private async void OnLoad()
{
var progress = new Progress<Tuple<string, double>>();
progress.ProgressChanged += OnWorkProgress;
await DoWork(progress);
}
private void OnWorkProgress(object sender, Tuple<string, double> e)
{
Values.Add(e.Item1);
NotifyPropertyChanged(nameof(Values));
ProgressValue = e.Item2 * 100;
}
private async Task DoWork(IProgress<Tuple<string, double>> progress)
{
const int elementsCount = 500;
for (int index = 0; index < elementsCount; index++)
{
var result = "Value_" + index;
await Task.Delay(10);
progress.Report(Tuple.Create(result, (double)index / elementsCount));
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName]string propertyName = null) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
并查看:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding OnLoadCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<StackPanel>
<ProgressBar Minimum="0" Maximum="100" Value="{Binding ProgressValue}" Height="30"></ProgressBar>
<Separator HorizontalAlignment="Center"></Separator>
<ListView ItemsSource="{Binding Values}" Height="250"></ListView>
</StackPanel>
这是它的工作方式:
OnLoad
是Window的事件处理程序,在window时被调用
负载。这是开始使用异步代码的好地方。Progress<T>
实例,并将其用作
UI和ThreadPool线程之间进行通信。ProgressChanged
,这是一个
用于我们的交流await DoWork(progress);
并通过
引用progress
。progress.Report
完成工作。OnWorkProgress
在UI线程上显示报告。编辑:
如果要运行此程序,则需要System.Windows.Interactivity.WPF
nuget程序包。
EDIT2:
这是为CPU绑定工作的方式:
private async void OnLoad()
{
var progress = new Progress<Tuple<string, double>>();
progress.ProgressChanged += OnWorkProgress;
await Task.Run(() => DoWork(progress));
}
private void OnWorkProgress(object sender, Tuple<string, double> e)
{
Values.Add(e.Item1);
NotifyPropertyChanged(nameof(Values));
ProgressValue = e.Item2 * 100;
}
private void DoWork(IProgress<Tuple<string, double>> progress)
{
const int elementsCount = 500;
for (int index = 0; index < elementsCount; index++)
{
var result = "Value_" + index;
Thread.Sleep(10); // don't do this. Do CPU intensive work here...
progress.Report(Tuple.Create(result, (double)index / elementsCount));
}
}
答案 1 :(得分:-2)
使用后台线程,并使用runcompleted和progress方法更新进度条。 ObservableCollection需要开始调用而不是调用来更新更改。希望这会阻止UI或您的线程