In a very simple Xamarin android APP I scan a barcode with ZXing and then handle the result. The handle results takes a few seconds to process and during I want to show an ACR showLoading, but the activity indicator hides almost instantly when the scanner/camera is closed.
How can I keep the show loading open while it is handling the result?
Cleaned code:
async void onAddByScan(object sender, EventArgs e)
{
var options = new ZXing.Mobile.MobileBarcodeScanningOptions();
var scanner = new MobileBarcodeScanner();
ZXing.Result result = await scanner.Scan(options);
using (UserDialogs.Instance.Loading())
{
await HandleResultAsync(result);
}
}
async Task HandleResultAsync(ZXing.Result result)
{
//do something with the results
}
You could use InvokeOnMainThreadAsync method in the Xamarin.Essentials: MainThread class. The idea of this method is to not only execute a code on the UI/main thread, but to also to await it's code. This way you can have both async/await logic and main thread execution.
You could refer to the similar case https://stackoverflow.com/a/67483594/10768653.
Related
I'm trying to chain tasks, so as soon as the one finishes the next starts, but the UI doesn't update. I did a course in react and the one lesson is where you update the UI based on state changes in the application, and that is what I'm trying to replicate. Change the state of the application (basically I'll be running methods that run return a bool for validation), and then update the UI accordingly, I'm also using binding, but for some reason its not running as intended, I don't know if I follow the documentation incorrectly. What can I change or fix to make this work and is it practically correct to use more than one task in a single async Task<T> method
public async Task<string> Connect_To_Ip()
{
await Task.Run(() =>
{
details.State = "Connection To IP 127.0.01.258.....";
Task.Delay(5000).Wait();
}).ContinueWith(result => new Task(async () =>
{
await Task.Run(() =>
{
if (result.Status == TaskStatus.RanToCompletion)
{
details.State = "Validating Card Number......";
}
});
}), TaskContinuationOptions.OnlyOnRanToCompletion);
return details.State;
}
How I'm calling the original task
Task connect = Connect_To_Ip();
await connect;
When you use await then you don't need Task.ContinueWith. Everything that follows the awaited operation is a continuation. Since you want to validate on a background thread, you must post the changes back to the UI thread in order to update the UI elements, otherwise you will produce cross-thread exceptions.
This is because UI elements can't be updated from a background thread, except the update occurs via INotifyPropertyChanged and data binding.
One way to do this is to use the Dispatcher to invoke UI manipulations on the UI thread or use the Progress<T> class, which will always execute the registered callback on the UI thread.
Your fixed and simplified code could look like this example:
public async Task ValidateAsync()
{
// Register the callback that updates the UI with the 'progressReporter'.
// Progress<T> must be instantiated on the UI thread it is associated with
var progressReporter = new Progress<string>(message => details.State = message);
// Execute the operation on a background thread
await Task.Run(() => ConnectToIp(progressReporter));
// Continuation starts here, after await
}
public async Task ConnectToIp(IProgress<string> progressReporter)
{
progressReporter.Report("Connection To IP 127.0.01.258.....");
await Task.Delay(TimeSpan.FromSeconds(5));
// Continuation starts here, after await
progressReporter.Report("Validating Card Number......");
}
It is recommended to use async APIs when possible instead of using background threads. For example, to connect to a server without blocking the UI you can use
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync("http://www.contoso.com/");
Many IO classes provide an async API.
Furthermore, I recommend to take a look at the INotifyDataErrorInfo interface. It is the recommended way to implement property validation and allows to provide UI error feedback in a very easy way.
I did this in Windows Forms (I had a test Windows Forms project open), but it should be about the same in WPF. I dropped a button, a label and a text box on the form. Then I wrote this code:
private async void button1_Click(object sender, EventArgs e)
{
var result = await ValidateTextBox();
if (result != null)
{
label1.Text = result;
return;
}
var intResult = await ReadTextBox();
label1.Text = intResult.ToString();
await IncrementTextBox();
intResult = await ReadTextBox();
label1.Text = intResult.ToString();
}
private async Task<string> ValidateTextBox()
{
await Task.Delay(2000);
if (!int.TryParse(textBox1.Text, out _)) {
return "Not Valid";
}
//otherwise
return null;
}
private async Task<int> ReadTextBox()
{
await Task.Delay(3000);
if (!int.TryParse(textBox1.Text, out var result))
{
throw new Exception("Don't do that");
}
return result;
}
private async Task IncrementTextBox()
{
await Task.Delay(3000);
if (!int.TryParse(textBox1.Text, out var result))
{
throw new Exception("Don't do that");
}
textBox1.Text = (result + 1).ToString();
}
If you type something that's not an int into the text box and press the button, a few seconds go by, and then Not Valid shows up in the label.
If there is a number there, then there is a pause and the number shows up in the label. Then another pause and the text box number will increment by 1. Finally after another pause, the label will show the incremented value.
Note that this all runs on a single thread. But, in spite of all the delays, the UI remains responsive the whole time.
Put breakpoints at the start of each function and on the lines after each of the awaits in the button click handler. Step through (not into) the whole thing and you'll see how the awaits create continuations
I have to call an API when my WPF Applications is being closed.
I'm using Application_Exit event because I have to do this even closing all the windows, being shut down from Task Manager or Alt + F4.
I've tried several ways, but all the options has some issues (some of them are these):
OPTION A: Async event + await
private async void Application_Exit(object sender, ExitEventArgs e)
{
if (MonitorInstance.Instance.User != null)
{
Log.Information($"Disconnect starting");
var result = await _apiClient.Disconnect();
Log.Information($"Disconnect result: {result}");
}
}
This option just calls the HTTP CONNECT (which sometimes succeed and others is being cancelled) but not my PATCH request.
OPTION B: Blocking Thread with Result of Task.
private void Application_Exit(object sender, ExitEventArgs e)
{
if (MonitorInstance.Instance.User != null)
{
Log.Information($"Disconnect starting");
var result = _apiClient.Disconnect().Result;
Log.Information($"Disconnect result: {result}");
}
}
This effectively calls the API but the process continues running. Visual Studio continues showing that the App is running and this is not desired.
I think it's worth to mention that I'm calling the API (which uses SSL) using HttpClient which is being created in the Disconnect method and I'm using .NET Core 3.1.
My questions:
Is Application_Exit the proper event to do this?
How to program the Application_Exit event to fulfill all the scenarios?
Should I change the implementation for my call to do it directly without the HttpClient?
This a classic case of deadlock. Your code is running under a synchronization context, your await calls inside of _apiClient.Disconnect() try to resume on the UI thread, but the UI thread is blocked waiting synchronously on the task because of .Result.
There are multiple ways to get around that, but for your precise case I don't think it's useful to get fancy. Just use Task.Run to run your call in a safer context:
private void Application_Exit(object sender, ExitEventArgs e)
{
if (MonitorInstance.Instance.User != null)
{
Log.Information($"Disconnect starting");
var result = Task.Run(apiClient.Disconnect).Result;
Log.Information($"Disconnect result: {result}");
}
}
I have an async codeblock running on the pageload.
The codes run smoothy until you reach capturevalue method where we create a new task.On executing that code block the await just freezes and then the control doesnt come back seems like the code just went to deadlock
protected void Page_Load(object sender, EventArgs e)
{
try
{
var textvalue = GetTextValueFromTask();
txtbox.Text = textvalue.Result;
string ss = "";
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
private async Task<string> GetTextValueFromTask()
{
string strReturn = await CaptureValue();
return strReturn;
}
private async Task<string> CaptureValue()
{
Thread.Sleep(5000);
Task<string> T = Task.Factory.StartNew<string>(() => "hi");
return await T;
}
Then I made a small change in Capturevalue method.
private async Task<string> CaptureValue()
{
Thread.Sleep(5000);
Task<string> T = Task.Factory.StartNew<string>(() => "hi");
string ss = T.Result;
return await T;
}
Once I made that change it started working normal.What difference did it make on just fetching the result initially. Please help me Iam a newbee to async
The difference is that second time it doesn't happen any "await" because you waited the task yourself, so await doesn't do anything.
I think you missed the await keyword the first time, here:
var textvalue = await GetTextValueFromTask();
Without it your method GetTextValueFromTask runs synchronously, then it comes into CaptureValue method where await occurs. But the default behaviour of the await is that it tries to capture synchronization context where it was called and to continue the rest of the method in that context, in your example it is WPF synchronization context, which does not allow more than one thread to execute at once. But the continuation cannot proceed, because context is already used by await mechanism.
So, one more time. There is one thread (UI thread), that executes your code up to the last await, which is return await T;, then it yields back to the caller - GetTextValueFromTask, and again to the Page_Load when it gets blocked, because initially you called GetTextValueFromTask synchronously (without await). After that, your operation T completes, and your code tries to continue executing using the initial synchronization context, the WPF one. But it can't, because it is already waiting in the Page_Load.
The link in the comments describes the situation in more detail.
Also consider not using Thread.Sleep in async/await scenarios, because it kills all the "asynchronous" nature of the code. Further reading: link.
Another general piece of advice, which is not directly applicable to your source code, is not to use Task.Factory.StartNew, but rather use Task.Run. Explanation here.
Please use Task.Run() instead of Task.Factory.StartNew()
var T = Task.Run(() => "hi");
It's up to Task.Run to decide how to handle this task.
Also please use .ConfigureAwait(false) in your await calls that do not requires the continuation being done in the awaiter thread context.
I have a web request (HttpRequest) which triggers a third library scanning method on my server that has an event handler attached to it:
scanner.OnScanComplete += scanner_OnScanComplete;
The web request will invoke scanner.Scan(files) but how can I force (or hook) the request to wait and get the results from scanner_OnScanComplete when the scan process is complete so it can return data to clients without having to send another web request to get this data?
void DoWork(HttpRequst request, var files)
{
var scanner = new Scanner()
scanner.OnScanComplete += scanner_OnScanComplete;
scan(files)
}
void scanner_OnScanComplete(object sender, EventArgs e)
{
var scanCompleted = true;
//Return scanCompleted somehow to the DoWork thread above
}
Do you have to use a HttpHandler or can you use other api's?
If you can use MVC4 or later then you can use an asynchronous Action Method to do this easily. Look here for an example of how to use them.
In addition to using an async Action Method you may need a way to await the event from the scanner. Using a Task Completion source as in this answer may be a good way to do that.
One way to do what you want is to store the completion of the task in a boolean member.
The boolean shall be marked volatile to avoid threading issues.
The risk of the approach is to lead to timeouts on client side if the scan processing is too long.
private volatile bool _finished;
void DoWork(HttpRequst request, var files)
{
var scanner = new Scanner();
scanner.OnScanComplete += scanner_OnScanComplete;
_finished= false;
scan(files)
while (!_finished) // wait for the scan completion
System.Threading.Thread.Sleep(1000); // avoid consuming 100% cpu
var scanData = Dothescanwork();
//Return scanData somehow to the DoWork thread above
}
void scanner_OnScanComplete(object sender, EventArgs e)
{
_finished= true;
}
I've a scenario:
MyApp calls cameraCapture
that fires a callbackFunction
after the callbackFunction (I have a photo captured) completes, I do more stuff.
So I have to wait for callbackFunction to complete before executing another function. How could i do this?
Here my code:
private static readonly Plustek_Camera.PFNCK_EVENT staticFnCamera = fnPFNCK_EVENT;
public static bool fnPFNCK_EVENT(int iEvent, int iParam, IntPtr pUserData)
{
//capture picture and save to folder
}
//I implement callback start camera and fire a callback staticFnCamera
var _status = CameraCtrl.Start(CameraCtrl.ScanMode, CameraCtrl.Resolution, CameraCtrl.ImageFormat, CameraCtrl.Alignment, staticFnCamera);
//waiting for staticFnCamera complete make sure image produced
ReadPassPortText();
If I understand correctly, you have some camera control that provides an asynchronous API to start capturing an image, but you want to wait synchronously for that operation to complete.
If so, there are lots of different ways to accomplish what you're trying to do. One such way would be to use a TaskCompletionSource:
TaskCompletionSource<bool> source = new TaskCompletionSource<bool>();
var _status = CameraCtrl.Start(CameraCtrl.ScanMode, CameraCtrl.Resolution,
CameraCtrl.ImageFormat, CameraCtrl.Alignment,
(iEvent, iParam, pUserData) =>
{
staticFnCamera(iEvent, iParam, pUserData);
source.SetResult(true);
});
//waiting for staticFnCamera complete make sure image produced
await source.Task;
ReadPassPortText();
Note that the above uses await, which is valid only in an async method. You haven't provided enough context to show exactly how that would work in your code, but I strongly recommend following the above. That will avoid blocking the currently running thread; the async method will return at that point, letting the thread continue to run, and will be resumed at the ReadPassPortText(); statement when the operation completes.
If for some reason you simply cannot use the await in your method, you can instead simply do source.Task.Wait();. This will, of course, block the currently executing thread at that statement.
The above requires .NET 4.5. There are other approaches that work with earlier versions of .NET, but you would need to be specific about your requirements to make it worth trying to describe those.
Edit:
Since you are using .NET 4.0, and presumably Visual Studio 2010, the above won't work for you "out-of-the-box". One option is to download the Async CTP for Visual Studio, which will give you the C# 5.0 compiler that would enable the above. But if that's not feasible for you, another option is to just do what the compiler would do on your behalf, by replacing the last two lines above with the following:
source.Task.ContinueWith(task => ReadPassPortText(),
TaskScheduler.FromCurrentSynchronizationContext());
That would attach the continuation delegate that call ReadPassPortText() to the Task object from the TaskCompletionSource, specifying the current synchronization context as the source of the scheduler to use to actually run the continuation.
The method would return after calling ContinueWith() (just as it would in the await version, except that here it's written out explicitly instead of the compiler doing it for you). When the Task object is set to the completed state, the previously-registered continuation will be executed.
Note that your original question isn't very clear about the context. If the code is running in the UI thread, then using FromCurrentSynchronizationContext() is important and will ensure that the continuation is executed in the UI thread as well. Otherwise, you can probably get away without specifying a scheduler in the call to ContinueWith().
This demonstrates an async-await pattern that you can use. As Peter Duniho points out in the comments, you will have to adapt the pattern to the API that you're using. Try playing with it here at this fiddle to understand how these things work.
using System;
using System.Threading.Tasks;
public class MyApp
{
public static void Main()
{
Console.WriteLine("1. MyApp calls camera capture.");
CameraCaptureAsync().Wait();
}
public async static Task CameraCaptureAsync()
{
Console.WriteLine("2. That calls callbackFunction");
var task = CallbackFunction();
Console.WriteLine("4. In the meantime.");
Console.WriteLine("5. Do some other stuff. ");
await task;
Console.WriteLine("7. Process the " + task.Result);
DoMoreStuff();
}
public async static Task<string> CallbackFunction()
{
Console.WriteLine("3. Which takes a picture.");
await Task.Delay(100);
Console.WriteLine("6. After the callback functions completes");
return "Photograph";
}
public static void DoMoreStuff()
{
Console.WriteLine("8. Do more stuff.");
}
}
After try some implement callback waiting, i try to resolve by adding another form for capturing images (frmSecond).
frmFirst call frmSecond and waiting in 5 to 7 seconds to make sure capture completed.
after that processing ReadPassPortText()
frmFirst Code:
frmReadPassport frmReadPass = new frmReadPassport();
frmReadPass.ShowDialog();
ReadPassPortText();
frmSecondCode
private CAMERACTRL CameraCtrl = null;
//Add static for call from camera start , make sure this alive
private static Plustek_Camera.PFNCK_EVENT staticFnCamera ;
public frmReadPassport()
{
InitializeComponent();
staticFnCamera = fnPFNCK_EVENT;
}
Timer formClose = new Timer();
private void frmReadPassport_Load(object sender, EventArgs e)
{
CaptureImages();
formClose.Interval = 7000; // 7 sec
formClose.Tick += new EventHandler(formClose_Tick);
formClose.Start();
}
private void formClose_Tick(object sender, EventArgs e)
{
//free camera first
// check if camera start then stop
ReleaseResourceCamera();
staticFnCamera =null;
formClose.Stop();
formClose.Tick -= new EventHandler(formClose_Tick);
this.Dispose();
this.Close();
}
private void CaptureImages()
{
CameraCtrl = new CAMERACTRL();
CameraCtrl.LoadCameraDll();
CameraCtrl.GetDeviceList();
String temp = CameraCtrl.GetParameter();
CameraCtrl.Start(CameraCtrl.ScanMode,CameraCtrl.Resolution,CameraCtrl.ImageFormat, CameraCtrl.Alignment, staticFnCamera);
}
public static bool fnPFNCK_EVENT(int iEvent, int iParam, IntPtr UserData)
{
captureImage();
return true;
}
}