0

In a UWP C# app, need background (ie. worker) thread to use UI thread to display an image. But can't figure out how to compile Dispatcher.RunAsync().

using Foundation;
using System;
using UIKit;

using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Timers;
using System.Threading;
using System.Windows.Threading;                 <<<<<<<<<<  gets error
using Windows.UI.Core;                          <<<<<<<<<<  gets error

public async static void process_frame()
{

    await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    {
        // "the name dispatcher does not exist in current context"
        //UI code here:
        display_frame();
    });
}


public void display_frame()
{
    var data = NSData.FromArray(System_Hub_Socket.packet_frame_state.buffer);

    UIImageView_camera_frame.Image = UIImage.LoadFromData(data);
}

Latest method

public async static void process_frame( /* ax obsolete: byte[] camera_frame_buffer, int frame_size_bytes */  )
{
    await Task.Run( () => { viewcontroller.display_frame(); } );
}


// [3]
// Copies latest frame from camera to UIImageView on iPad.
// UI THREAD

public Task display_frame()
{
  var data = NSData.FromArray ( System_Hub_Socket.packet_frame_state.buffer);        
  <<<<<< ERROR 

  UIImageView_camera_frame.Image = UIImage.LoadFromData( data );

  return null;
}

Error from the latest method

enter image description here

Martin Zikmund
  • 38,440
  • 7
  • 70
  • 91
Doug Null
  • 7,989
  • 15
  • 69
  • 148
  • `display_frame` should be `public Task display_frame()` if you want it to be like a `void`, and if you need to return something, it should be `public Task` where T is the type returned from the method. Then you can do `await Task.Run(() => display_frame());` – Ryan Wilson Dec 04 '18 at 18:00
  • What should display_frame() return? I did "public Task display_frame()" but get compile error "not all code paths return a value". – Doug Null Dec 04 '18 at 18:17
  • so I returned: return null; – Doug Null Dec 04 '18 at 18:26
  • return Task.CompletedTask(); causes this error: "Non-invokable member "Task.CompletedTask' cannot be used like a method." – Doug Null Dec 04 '18 at 19:01
  • Sorry, I should have left the `()` off, it is a property not a method, do `return Task.CompletedTask;` Documentation (https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.completedtask?view=netframework-4.7.2) – Ryan Wilson Dec 04 '18 at 19:07
  • "Task.Run..." didn't work; seems to run display_frame() from non-UI thread. (please see ERROR FROM LATEST METHOD). – Doug Null Dec 04 '18 at 19:08
  • That's what Tasks typically do, they run off of the thread pool, what is it you are trying to accomplish? Maybe this just needs re-worked. If you want to get the image asynchronously and then show it with the UI thread, you could change your method to return `Task` await the image and then use that to show it with the UI thread inside of `process_frame()` – Ryan Wilson Dec 04 '18 at 19:09
  • Maybe this will help you (https://stackoverflow.com/questions/31412984/uikitthreadaccessexception-from-await-task-run-with-a-try-catch-finally-block) – Ryan Wilson Dec 04 '18 at 19:16
  • I found some class name such as `UIImage` `NSData`, it looks like xamrin.ios project, right? – Nico Zhu Dec 05 '18 at 05:45

1 Answers1

2

Looking at the using statements in you code:

using UIKit;
...
using Windows.UI.Core; 

This just can't happen. UIKit is a Xamarin.iOS, platform-specific namespace and Windows.UI.Core is Windows platform-specific namespace and in no way can those two be mixed in one file (except for shared project with #if directives that is, but that is not the case here).

Xamarin helps writing cross-platform apps, but you still cannot use platform specific APIs on OS on which they are not available. Windows has Dispatcher as a means of running code on the UI thread, but this concept is not available on iOS, which uses InvokeOnMainThread method instead.

So if you are writing code that is in the platform-specific iOS project, you must use iOS APIs. If you are writing code that is in the platfrom-specific UWP project, you must use UWP APIs - things like Dispatcher will work without problem there.

Finally, if you are writing code in a .NET Standard library, you cannot write any platform specific code directly and must use dependency injection to define an interface behind which you hide the use of platform specific APIs.

Martin Zikmund
  • 38,440
  • 7
  • 70
  • 91