跟踪x个事件被触发的时间

时间:2014-07-11 12:19:15

标签: c# events

通常这是我花费几个小时浏览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();
    }
}

然而,正如我所提到的那样,这种感觉有些笨拙和虚伪,我希望有一种更清洁的方法。

3 个答案:

答案 0 :(得分:1)

您可以使用Reactive Extensions并将Observable.FromEventPatternObservable.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)

如果您将货件,容器和包装载为异步任务,那么您有几个选项。正如其他人建议您可以使用WhenAllJoin()等待所有线程完成后再继续。但是,如果您的线程必须保持活动状态并且线程在完成加载后不会停止,您可以使用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))以确定是否所有内容都已完成。

不是最正确的方法。但阅读你的情景可能会有所帮助。