Unable to update label prior to function call - c#

I need to update a simple label control prior to calling a function in another class but the label only updates after the function call completes. The code is below.
lblCalling.Text = "Calling...";
bool res = pf_auth.pf_authenticate(pfAuthParams, out otp, out callStatus, out errorId);

NO, I don't think so. Rather, in your code the label gets updated as it supposed to but most probably the following method takes long time and you are running in STA (single threaded) mode and so your main UI thread is block; not allowing you to see the update probably.
Run the method pf_auth.pf_authenticate() in separate thread probably

Try this:
public await void DoWork()
{
await Task.Run(() =>
{
lblCalling.Text = "Calling...";
});
bool res = pf_auth.pf_authenticate(pfAuthParams, out otp, out callStatus, out errorId);
}

Related

If statement getting passed over

The if statement in my code isn't working, it always goes straight to the else, I know for sure the value IS 255 but it still won't go to if, I even tried testing it in the else and it reported back that it IS 255 so I don't know why it keeps doing this, im just really confused now, can someone help?
public Form1()
{
InitializeComponent();
serialPort1.Open();
string lastLine = string.Empty;
Task.Run(() =>
{
while (true)
{
string tailValue = lastLine;
lastLine = serialPort1.ReadLine();
string line = lastLine;
label1.BeginInvoke(new Action(() =>
{
label1.Text = string.IsNullOrEmpty(line) || string.Equals(tailValue, line)
? label1.Text
: $"{line}";
}));
if (label1.Text == "255")
{
Console.WriteLine(label1.Text);
System.Diagnostics.Process.Start("http://www.google.com");
Task.Delay(10000).Wait();
}
Task.Delay(1000).Wait();
}
});
}
What's happening here is that you are executing asynchronous code, but not waiting for the result to be calculated before trying to use it.
Here's the documentation for BeginInvoke. Note the description:
Executes the specified delegate asynchronously on the thread that the
control's underlying handle was created on.
So when the call to BeginInvoke happens, the next statement (in your case it's the if statement) is executed without waiting for your Action to complete.
So what is the value of label1 before you execute Action? (it's probably not 255)
One way of fixing this would be to use Invoke instead of BeginInvoke - you're already encasing this in Task.Run, so why are you trying to start another asynchronous call, especially for what looks like a simple calculation that the very next method depends on.
The reason Invoke works is because unlike BeginInvoke, it's a synchronous call. The calling thread blocks until Invoke finishes.

WPF: display a progress dialog only when its background task takes longer than a specified interval

I am executing a potentially long running operation in the background thread of a modal dialog. The problem is that, when the operation takes a short time, the dialog is shown and closed almost instantaneously, which annoys the users.
I would like to show the dialog only if the operation takes longer than, say, 2s.
The dialog is a WPF Window and the long running operation code is in the ViewModel. The ViewModel creates a Task that runs the operation in the background.
Here is a relevant snippet:
public Task StartAction() {
var mainTask = Task.Factory.StartNew(InternalAction);
MainTask = mainTask;
mainTask.ContinueWith(_ => { IsFinished = true; });
return mainTask;
}
InternalAction is the potentially long running operation.
This is how I am trying to introduce the delay. I am using Sriram Sakthivel's suggestions from a different answer, but the code is not exactly the same:
var viewModel = ... // Creates the ViewModel
var dialogWindow = ... // Creates the Window and starts the operation by calling viewModel.StartAction();
var delayTask = Task.Delay(2000);
if (viewModel.MainTask != null) {
Task.WaitAny(delayTask, viewModel.MainTask);
}
if (viewModel.IsFinished) {
return;
}
ShowDialog(dialogWindow); // this code calls dialogWindow.ShowDialog() eventually
I am not using await because I do not want to yield control to the caller (COM) because the caller expects the result to be ready when it gets the control back.
I have been experimenting with different timeouts, e.g., 5000ms, and I do not see any difference in the behavior. The dialog windows still "blink" (are shown and closed immediately). I am sure I am doing something wrong, but I cannot understand my mistake.
You're waiting on MainTask, but MainTask isn't the task that sets IsFinished. You may be returning from WaitAny after InternalAction completes but before the IsFinished = true continuation completes.
Try setting MainTask to the continuation rather than its antecedent:
public Task StartAction() {
var mainTask = Task.Factory.StartNew(InternalAction);
var continuation = mainTask.ContinueWith(_ => { IsFinished = true; });
MainTask = continuation;
return mainTask;
}
Note that continuation cannot begin until mainTask has completed, so with this change you'll be waiting on mainTask and continuation.
Note, however, that if IsFinished is being read from the UI thread, you'll want to also set it from the UI thread. That, or make it backed by a volatile field.
There used to be a 3rd party Library called "Busy Indicator". Maybe you could enable it to only appear if the busy condition is met for a certain time? (https://github.com/xceedsoftware/wpftoolkit/wiki/Xceed-Toolkit-Plus-for-WPF).
Basically it comes down to the ViewModel exposing a "busy" property (or any property that can be converted into a boolean value representing "busy"). And the View reacting to the change on a delay (if any).
I am not sure if XAML itself can do that, as you need to show a window. A bit of code behind might be nesseary here. How about you register a custom ChangeNotification handler that starts a timer, with the timer re-checking if the condition is still met in the "tick" event?
Here is some code, made largely from memory:
//custom ChangeNofiticationHander
busyChangeHanlder(object sender, PropertyChangedEventArgs e){
if(e.PropertyName == "BusyBoolean"){
if(BusyBoolean)
//Start the timer
else
//Stop the timer
}
}
timerTickHandler(object sender, TimerTickEventArgs e){
if(BusyBoolean){
//create and Dispaly the Dialog here
}
}
var mainTask = Task.Delay(5000); // your long running task
if(Task.WaitAny(mainTask, Task.Delay(2000)) == 1){ // if the delay enden first, show dialog
showDialog();
await mainTask;
closeDialog();
}
await mainTask; // this will just skip, if mainTask is already done
Try this approach - it will only show dialog window, if the operation takes longer that 2s. You can also wrap all that in another task, then the caller can await the whole thing with no difference whether the dialog was shown or not.

Change label text and start threads in the same void

I have a big problem and hope you guys can help me...
I need to start a Thread called ListenOnSocket, no problem so far..
Thread ListenOnSocket = new Thread(newThreadStart(Form1.ListenOnSocket));
ListenOnSocket.Start();
But when I want to change the label from within ListenOnSocket, I get an object reference is required for the non-static field, method or property.
So normally you would passe the label on by doing this
public static void ListenOnSocket(Label lblstatus)
{
//i want to change the label from here.
lblstatus.text = "Hello";
}
but then I get
No overload for ListenOnSocket matches delegate System.Threading.Threadstart
in my threadstart.
Can anyone please help, I am really stuck, sorry if there is not much to go on I am quite new to C#.
You can use Lambda Expression to pass parameter.
Thread ListenOnSocket = new Thread( () => { Form1.ListenOnSocket(yourParameter); } );
ListenOnSocket.Start();
But you will get the CrossThreadException when the ListenOnSocket method execute. So you need to use BeginInvoke to set label text.
So search the CrossThreadException and why you will get it.
Note: I do not write the sample code for this, because searching is more beneficial.
You need to marshal this back to the UI thread:
public static void ListenOnSocket(Label lblstatus)
{
this.BeginInvoke(new Action(() =>
{
//i want to change the label from here.
lblstatus.text = "Hello";
});
}
It looks like you might actually want a ParameterizedThreadStart here.
You would pass the Label in as its parameter.
Control changes also need to be performed on the UI thread.
public void DoSomething()
{
// Actually a ParameterizedThreadStart, but you don't need to state this explicitly
//var listenOnSocket = new Thread(new ParameterizedThreadStart(ListenOnSocket));
var listenOnSocket = new Thread(ListenOnSocket);
// Pass the label as the ParameterizedThreadStart parameter
// TestLabel is a label within the form
listenOnSocket.Start(TestLabel);
}
private void ListenOnSocket(object statusLabelObject) // Parameter must be of type Object
{
var statusLabel = statusLabelObject as Label;
if (statusLabel == null)
throw new ArgumentException(#"Parameter must be of type Label", "statusLabelObject");
// Changes to controls must be performed on the UI thread.
BeginInvoke((Action)(() => statusLabel.Text = #"text"));
}
They key gotcha here is it's not valid to update a UI element (like a label) from a background thread.
If you have a long running task then you probably don't want to run that on the UI thread as it will block.
Assuming that you're creating a thread because you have something that runs for too long to run on the UI thread, it might be worth looking into way of marshalling calls from background threads onto the UI thread.
For more information on how to do this see How to update the GUI from another thread in C#? if you're looking to update the status from a long running task, you might want to look into background worker: MSDN: How to Use Background Worker which is a helper class designed to help with long running background tasks.

Updating a Control's property (label.Text) from another thread

In my windows application I want to update a label's Text property from another thread when some button is clicked:
Here is the code of my button click event handler:
StatusLabel.Text = "Started";
Task.Factory
.StartNew(() =>
{
… // long-running code
StatusLabel.Text = "Done";
}, CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext())
.ContinueWith(tsk =>
{
MessageBox.Show("something broke");
var flattened = tsk.Exception.Flatten();
// note: Don't actually handle exceptions this way, m'kay?
flattened.Handle(ex => { MessageBox.Show("Error:" + ex.Message); return true; });
}, TaskContinuationOptions.OnlyOnFaulted);
When I click the button the above code is executed. I am not seeing the StatusLabel.Text = "Started"; at once. It seems it waits for // long-running code and then it is executed.
What I want is to see the "Started" in the label as soon as the button is clicked, and when the long-running task is done, I want to see "Done" on the label.
There are two reasons why this is happening.
First, you are telling the task to run on the GUI thread, by specifying TaskScheduler.FromCurrentSynchronizationContext() as a parameter. This means that your processing is not happening on a background thread, but on a GUI thread. Second is that changing a control's property only invalidates it, meaning that it will only be redrawn once the GUI thread has done processing other jobs.
In other words, you set the value to "Started" (and the label only invalidates itself), and then immediately queue the "background" task to the GUI thread, keeping it busy from painting controls. Your form will appear "hanged" during this time, and you will probably be unable to even move it around.
The simplest way to do a background job in Windows Forms is to use a BackgroundWorker. If you, however, really want to use a Task, then use the simple task factory method which doesn't accept a sync context, and then make sure that all UI interactions from that background thread are invoked on a GUI thread:
StatusLabel.Text = "Started";
// this is the simple Task.Factory.StartNew(Action) overload
Task.Factory.StartNew(() =>
{
// do some lengthy processing
Thread.Sleep(1000);
// when done, invoke the update on a gui thread
StatusLabel.Invoke(new Action(() => StatusLabel.Text = "Done"));
});
Alternatively, you may simplify the whole thing by moving GUI thread sync logic into a separate method:
// this method can be invoked from any thread
private void UpdateStatusLabel(string msg)
{
if (StatusLabel.InvokeRequired)
{
StatusLabel.Invoke(new Action<string>(UpdateStatusLabel), msg);
return;
}
StatusLabel.Text = msg;
}
And then simply call the method from wherever you wish:
private void button1_Click(object sender, EventArgs e)
{
UpdateStatusLabel("Started");
Task.Factory.StartNew(() =>
{
// do some lengthy processing
Thread.Sleep(10000);
// no need to invoke here
UpdateStatusLabel("Done");
});
}
If I understand, the button click happens in the UI thread, so no problem to set the label text to "Started" from there. Then you start the long running code from a different thread. Call Invoke method from a different thread to update UI elements after the long running code finishes:
Invoke((Action) (() => StatusLabel.Text = "Done"));
private async void Button_Clicked(object sender, EventArgs e)
{
Device.BeginInvokeOnMainThread(() =>
{
// UI updates ( label text change, button enable/disable )
});
await task;
await Task.Run(()=> {
// Synchronous methods
});
}
These can be arranged any how based on requirements.
All run serially. Like here first UI update then task completes and then other synchronous methods will be run.
If those synchronous methods have UI updates again the same way has to be followed like this method. Since those updates are inside a sync method which is called by this async method. All connected synchronous methods should be treated like async method only without "await". Because they are called from Task.Run of this method so they get converted to async method

Dispatcher.Invoke from a new thread is locking my UI

i'm using wpf, there's a button on my ui.
when the user clicks it, i have a for loop that runs a new method, on a new thread using autoresetevent.
in that method on that new thread, i'm using a label, let's call it lblStatus. i want to update that label on this thread that's not on the ui. using wpf, i have to use Dispatcher.Invoke.
here's a sample of my code:
Thread thread= new Thread(StartLooking);
thread.Start();
_waitHandle.WaitOne();
private void StartLooking(object value)
{
if (lblStatus.Dispatcher.Thread == Thread.CurrentThread)
{
lblStatus.Content = "Scanning>...";
}
else
{
lblStatus.Dispatcher.Invoke(DispatcherPriority.Background, new Action(() => lblStatus.Content = "Scanning>>>>>"));
}
_waitHandle.Set();
}
the program just stops here. it doesn't change the content of the label, it returns to my ui, but blocks it.
i've tried
lblStatus.Dispatcher.Invoke(DispatcherPriority.Normal, new LblStatusThreadCheck(lblStatusThreadCheck), "Scanning...");
as well, but that isn't working also. any ideas?
The problem is that you're making it impossible for this to execute, since you're using Invoke.
Dispatcher.Invoke will not return until the UI thread processes. However, you've blocked the UI thread by calling _waitHandle.WaitOne();, and don't set the wait handle until AFTER this processes. The two effectively cause a dead lock.
If you switch this to use BeginInvoke instead, the UI will queue the element, the wait handle will set, THEN the label will update. It will work and not block, however.
Since the two previous posts already cover the problem in your code, just a suggestion: instead of
if (lblStatus.Dispatcher.Thread == Thread.CurrentThread)
try using
if (!lblStatus.CheckAccess())
It's cleaner and has the exact intent you want. Just read about it here.
You probably want to use BeginInvoke instead. Invoke will block the thread that called it until the UI thread has run the Action, and since you're setting the priority to Background, this could take some time.
Best solution I have found for .net 4.5+ is using SynchronizationContext Post
Example (Task.Run's can be as many as you want in parallel accessing UI):
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
var context = SynchronizationContext.Current;
Task.Run(() =>
{
var i = 0;
while (true)
{
context.Post((tmp) =>
{
uiText.Text = $"{i}";
}), this);
Thread.Sleep(1000);
i++;
}
});
}

Categories

Resources