mlt
mlt

Reputation: 31

Setup an event loop for a UWP app

How do I setup an event loop (main loop) in a UWP app?

My goal is to have an app that has a main page with a continuously updating calculation, that then continuously updates an image on the page. While that is constantly happening, the user can click a button to change the calculation behavior, or to go to a second page and edit related data.

The user can then click back to the main page and see the image and calculation restart and continuously update. (The calculation is complex, so it should go as fast as possible and use up as much app time as possible).

If there is a way to accomplish this without an event loop I would like to know that also, but so far I have not found a way.
With an event loop, I can simply update the calculation and UI every time through the loop. But I have not found any way to do so. This question asked nearly the same thing, but was never directly answered and had a different use case anyway.

The closest I have found to a solution is to grab the CoreDispatcher from the CoreWindow and use the RunIdleAsync() method to create a loop:

public MainPage()
{
    this.InitializeComponent();

    Windows.UI.Core.CoreWindow appwindow = Windows.UI.Core.CoreWindow.GetForCurrentThread();
    Windows.UI.Core.CoreDispatcher appdispatcher = appwindow.Dispatcher;
    //create a continuously running idle task (the app loop)
    appdispatcher.RunIdleAsync( (dummyt) =>
   {
        //do the event loop here
        .
        .
        .
        if (appdispatcher.ShouldYield()) //necessary to prevent blocking UI
        {
           appdispatcher.ProcessEvents(Windows.UI.Core.CoreProcessEventsOption.ProcessAllIfPresent);
        }

   });
}    

The main problem with this is that you can't switch between pages (you get a system exception from dispatching events within an already dispatched event).
Second, this is very messy and requires maintaining extra state in the event loop. Besides, why should I have to go through these contortions just to have some calculations happening while the app is waiting for user input?

Is there a way to do this (besides switching to a C++ DirectX app)?

Upvotes: 3

Views: 1582

Answers (2)

VMAtm
VMAtm

Reputation: 28355

If we're talking about some event loop, or stream, .Net has a great library named Rx, or Reactive Extensions, which may be helpful for you. You can set up a simple flow, something like this:

var source = Observable
    // fire event every second
    .Interval(TimeSpan.FromSeconds(1), Scheduler.DispatcherScheduler)
    // add timestamp for event
    .Timestamp()
    // gather system information to calculate
    .Select(GetSystemInfo);

Note that the events right now are on UI thread, as you need to access the controls. Now you have two options: use Rx for background processing too or use TPL Dataflow' TransformBlock for processing your system information into new image (it can be Observer and Observable at a time). After that you need to get back to UI thread.

First option:

var source = Observable
    // fire event every second
    .Interval(TimeSpan.FromSeconds(1), DispatcherScheduler.Current)
    // add timestamp for event
    .Timestamp()
    // gather system information to calculate
    .Select(GetSystemInfo)

    // expensive calculations are done in background
    .Select(x => x.ObserveOn(DefaultScheduler.Instance))
    .Select(x => Expensive(x))

    .Select(x => x.ObserveOn(DispatcherScheduler.Current))
    .Select(x => UpdateUI(x));

You probably should split this chain into several observers and observables, still the idea is the same, more information here: Rx Design Guidelines.

Second option:

var action = new TransformBlock<SystemInfo, ImageDelta>(CalculateDelta,
    new ExecutionDataflowBlockOptions
    {
        // we can start as many item processing as processor count
        MaxDegreeOfParallelism = Environment.ProcessorCount,
    });

IDisposable subscription = source.Subscribe(action.AsObserver());

var uiObserver = action.AsObservable()
    .Select(x => x.ObserveOn(DispatcherScheduler.Current))
    .Select(x => UpdateUI(x));

I want to note that UWP and MVVM pattern do provide a possibility to work with binding between UI and ObservableCollection, which will help you to notify user in most natural way.

Upvotes: 0

BradleyDotNET
BradleyDotNET

Reputation: 61349

I don't know about setting up your own event loop, but there is no reason to do so.

What you are talking about sounds like a great case for Tasks. You would start a calculation Task whenever your user did something, having it report its progress via standard C# events if you need mid-operation updates. Those updates would modify properties in your view model which the binding system would then pick up.

You could also make your calculation code cancellable so changes can abort a previous calculation.

All of this involves pretty standard UWP concepts; no need for a special event loop. That you are even considering that makes me think you need to study MVVM and multi-threading/tasks; you are still thinking in a very "Win-Forms" kind of way.

Upvotes: 3

Related Questions