我正在处理WPF Prism
申请。我有一个DelgateCommand
,它负责填充由UI线程异步拥有的ObservableCollection
using async and await
。该集合依次绑定到图表。
为了使multiple threads
能够访问集合,我启用了synchronization
,如下所示:
BindingOperations.EnableCollectionSynchronization(ChartBoundCollection, _lock);
命令处理程序逻辑如下:
private async void ShowPatientVisitsVsDays()
{
IsChartBeingPopulated = true;
this.ChartSubTitle = "Requests Vs Days";
this.SeriesTitle = "Days";
ChartBoundCollection.Clear();
await ShowRequestsVsDaysAsync();
IsChartBeingPopulated = false;
}
填充可观察集合的逻辑如下:
private async Task ShowRequestsVsDaysAsync()
{
await Task.Run(() =>
{
if (PatientVisits.Count() > 0)
{
var days = PatientVisits.Select(p => p.VisitDate.Value.Date).Distinct();
foreach (var i in days)
{
var dayVisitCount = PatientVisits.Where(p => p.VisitDate.Value.Date == i).Count();
ChartBoundCollection.Add(new PatientVisitVsXAxis() { XAxis = i.ToShortDateString(), NumberOfPatientVisits = dayVisitCount });
}
}
});
}
我面临的问题是,在设置了await的异步方法完成后,我设置IsChartBeingPopulated = false
的延续不会被执行。
await ShowRequestsVsDaysAsync();
因此,即使在异步方法之前也设置了IsChartBeingPopulated 完成了。
命令处理程序ShowPatientVisitsVsDays()由单击调用 视图上的按钮。该按钮绑定到以下内容 命令:
ShowPatientVisitsVsDaysCommand = new DelegateCommand(ShowPatientVisitsVsDays);
IsChartBeingPopulated
用于设置属于'扩展WPF工具包的IsBusy DependencyProperty
控件的BusyIndiator
。
我们的想法是在绑定集合中填充图表数据时显示BusyIndicator
。
<xctk:BusyIndicator IsBusy="{Binding Path=IsChartBeingPopulated}" >
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<chart:ClusteredColumnChart Grid.Row="0" ChartTitle="Patient Visits History" ChartSubTitle="{Binding Path=ChartSubTitle}">
<chart:ClusteredColumnChart.Series>
<chart:ChartSeries SeriesTitle="{Binding Path=SeriesTitle}" DisplayMember="XAxis" ValueMember="NumberOfPatientVisits" ItemsSource="{Binding Path=ChartBoundCollection}" />
</chart:ClusteredColumnChart.Series>
</chart:ClusteredColumnChart>
</Grid>
</xctk:BusyIndicator>
不确定问题是什么。有人知道造成这种情况的原因是什么吗?
答案 0 :(得分:2)
您没有同步对集合的访问权限。 BindingOperations.EnableCollectionSynchronization
不会神奇地使收集线程安全。它只能确保数据绑定引擎在不进行锁定的情况下不会枚举集合。
添加和清除收集时,您仍需要锁定_lock
对象。
有关EnableCollectionSynchronization
的详细信息,请参阅here。
答案 1 :(得分:0)
你可以做这样的事情;
private async void ShowPatientVisitsVsDays()
{
IsChartBeingPopulated = true;
this.ChartSubTitle = "Requests Vs Days";
this.SeriesTitle = "Days";
new ChartBoundCollection().Clear();
IsChartBeingPopulated = await ShowRequestsVsDaysAsync();//here we are waiting till the async method is finished.
}
private async Task<bool> ShowRequestsVsDaysAsync()
{
return await Task.Run(() =>
{
if (PatientVisits.Any())//replace Count with Any to avoid unwanted enumerations.
{
var days = PatientVisits.Select(p => p.VisitDate.Value.Date).Distinct();
foreach (var i in days)
{
var dayVisitCount = PatientVisits.Count(p => p.VisitDate.Value.Date == i);
chartBoundCollection.Add(new PatientVisitVsXAxis() { XAxis = i.ToShortDateString(), NumberOfPatientVisits = dayVisitCount });
}
}
Thread.Sleep(5000);//this is for testing. Sleep the thread for 5secs. Now your busyIndicator must be visible for 5secs minimum.
return false;//return false, so that we can use it to populate IsChartBeingPopulated
});
}
更新:我认为您对异步等待有一些疑问。下面的代码将帮助您清除它们。
创建一个新的控制台应用,并将此代码放在Program
类中。
根据您在下面的评论,ShowPatientVisitsVsDays中的文本应该在打印“ShowRequestsVsDaysAsync”方法之前打印。这样做有用吗?测试一下,让我们知道。
static void Main(string[] args)
{
Console.WriteLine("Main started");
ShowPatientVisitsVsDays();
Console.ReadLine();
}
private static async void ShowPatientVisitsVsDays()
{
await ShowRequestsVsDaysAsync();
Console.WriteLine("ShowPatientVisitsVsDays() method is going to SLEEP");
Thread.Sleep(2000);
Console.WriteLine("ShowPatientVisitsVsDays() method finished");
}
private static async Task ShowRequestsVsDaysAsync()
{
await Task.Run(() =>
{
Console.WriteLine("ASYNC ShowRequestsVsDaysAsync() is going to SLEEP.");
Thread.Sleep(5000);
Console.WriteLine("ASYNC ShowRequestsVsDaysAsync finished.");
});
}
}