通常这是我花费几个小时浏览google和stackoverflow的东西,但是我遇到了如何解决这个问题的问题。
我希望有一种简单的方法可以实现这一点,因为我目前的方法感觉很糟糕" hackish"
我需要做什么,如果跟踪几个数据源何时完成加载,并且只有当所有数据完成时才加载新视图(这是WPF mvvm)。现在,数据通过称为Repository的静态类加载,每个人创建一个线程并确保它们只能一次发生一次加载操作(以避免多个线程试图加载到同一个集合中),这些类中的每一个都会触发一个名为加载完成后加载完成。
我有一个位置可以加载大部分数据(第一次,还有其他位置可以重新加载数据)我计划挂钩到每个存储库的OnLoaded事件,并跟踪哪个已经返回,当一个返回时,将其标记为已加载并检查是否有任何剩余。如果没有任何内容仍然加载新视图,则不执行任何操作。
这样的事情:
ShipmentRepository.LoadingComplete += ShipmentRepository_LoadingComplete;
ContainerRepository.LoadingComplete += ContainerRepository_LoadingComplete;
void ContainerRepository_LoadingComplete(object sender, EventArgs e)
{
_containerLoaded = true;
CheckLoaded();
}
void ShipmentRepository_LoadingComplete(object sender, EventArgs e)
{
_shipmentsLoaded = true;
CheckLoaded();
}
private void CheckLoaded()
{
if(_shipmentsLoaded && _containersLoaded && _packagesLoaded)
{
LoadView();
}
}
然而,正如我所提到的那样,这种感觉有些笨拙和虚伪,我希望有一种更清洁的方法。
答案 0 :(得分:1)
您可以使用Reactive Extensions并将Observable.FromEventPattern
与Observable.Zip
方法结合使用来实现此目的。您应该可以执行以下操作:
var shipmentRepositoryLoadingComplete = Observable.FromEventPattern<EventHandler,EventArgs>(h => ShipmentRepository.LoadingComplete += h, h => ShipmentRepository.LoadingComplete -= h);
var containerRepositoryLoadingComplete = Observable.FromEventPattern<EventHandler, EventArgs>(h => ContainerRepository.LoadingComplete += h, h => ContainerRepository.LoadingComplete -= h);
然后你对这样的观察者进行了分类:
var subscription = Observable.Zip(shipmentRepositoryLoadingComplete, containerRepositoryLoadingComplete)
.Subscribe(l => LoadView()));
子顺序需要保持活跃,因此您可以将其保存为私有变量。调用两个完整事件时,应调用LoadView
方法。这是我用来测试此方法的工作控制台示例。
using System;
using System.Reactive.Linq;
namespace RxEventCombine
{
class Program
{
public event EventHandler event1;
public event EventHandler event2;
public event EventHandler event3;
public Program()
{
event1 += Event1Completed;
event2 += Event2Completed;
event3 += Event3Completed;
}
public void Event1Completed(object sender, EventArgs args)
{
Console.WriteLine("Event 1 completed");
}
public void Event2Completed(object sender, EventArgs args)
{
Console.WriteLine("Event 2 completed");
}
public void Event3Completed(object sender, EventArgs args)
{
Console.WriteLine("Event 3 completed");
}
static void Main(string[] args)
{
var program = new Program();
var event1Observable = Observable.FromEventPattern<EventHandler,EventArgs>(h => program.event1 += h, h => program.event1 -= h);
var event2Observable = Observable.FromEventPattern<EventHandler, EventArgs>(h => program.event2 += h, h => program.event2 -= h);
var event3Observable = Observable.FromEventPattern<EventHandler, EventArgs>(h => program.event3 += h, h => program.event3 -= h);
using (var subscription = Observable.Zip(event1Observable, event2Observable, event3Observable)
.Subscribe(l => Console.WriteLine("All events completed")))
{
Console.WriteLine("Invoke event 1");
program.event1.Invoke(null, null);
Console.WriteLine("Invoke event 2");
program.event2.Invoke(null, null);
Console.WriteLine("Invoke event 3");
program.event3.Invoke(null, null);
}
Console.ReadKey();
}
}
}
<强>输出强>
Invoke event 1
Event 1 completed
Invoke event 2
Event 2 completed
Invoke event 3
Event 3 completed
All events completed
答案 1 :(得分:0)
如果您将货件,容器和包装载为异步任务,那么您有几个选项。正如其他人建议您可以使用WhenAll
或Join()
等待所有线程完成后再继续。但是,如果您的线程必须保持活动状态并且线程在完成加载后不会停止,您可以使用System.Threading.CountdownEvent
,如下所示:
修改强>
添加了我如何设置线程和处理事件。还将示例从静态Program
移动到实例,更接近您的情况。同样,如果您在加载数据后不需要在线程中执行任何操作,只需完全跳过CountdownEvent
并等待所有线程完成。更简单,不需要事件,可以使用Join()
或在这种情况下Task.WaitAll()
实现。
class Program
{
static void Main(string[] args)
{
var myWpfObject = new MyWpfObject();
}
}
public class MyWpfObject
{
CountdownEvent countdownEvent;
public MyWpfObject()
{
ShipmentRepository ShipmentRepository = new ShipmentRepository();
ContainerRepository ContainerRepository = new ContainerRepository();
PackageRepository PackageRepository = new PackageRepository();
ShipmentRepository.LoadingComplete += Repository_LoadingComplete;
ContainerRepository.LoadingComplete += Repository_LoadingComplete;
PackageRepository.LoadingComplete += Repository_LoadingComplete;
Task[] loadingTasks = new Task[] {
new Task(ShipmentRepository.Load),
new Task(ContainerRepository.Load),
new Task(PackageRepository.Load)
};
countdownEvent = new CountdownEvent(loadingTasks.Length);
foreach (var task in loadingTasks)
task.Start();
// Wait till everything is loaded.
countdownEvent.Wait();
Console.WriteLine("Everything Loaded");
//Wait till aditional tasks are completed.
Task.WaitAll(loadingTasks);
Console.WriteLine("Everything Completed");
Console.ReadKey();
}
public void Repository_LoadingComplete(object sender, EventArgs e)
{
countdownEvent.Signal();
}
}
一个模拟的Repository类:
public class ShipmentRepository
{
public ShipmentRepository()
{
}
public void Load()
{
//Simulate work
Thread.Sleep(1000);
if (LoadingComplete != null)
LoadingComplete(this, new EventArgs());
Console.WriteLine("Finished loading shipments");
DoAditionalWork();
}
private void DoAditionalWork()
{
//Do aditional work after everything is loaded
Thread.Sleep(5000);
Console.WriteLine("Finished aditional shipment work");
}
public event EventHandler LoadingComplete;
}
答案 2 :(得分:0)
另一种方法:添加属性LoadingCompleted。对于每个实例,您启动一个线程将该对象返回到列表。在每个loadcompleted上将属性设置为true,并在您通过列表捕获加载完成循环的位置(myList.Any(x =&gt; LoadingCompleted == false))以确定是否所有内容都已完成。
不是最正确的方法。但阅读你的情景可能会有所帮助。