CallBack of Function not reached - c#

I have a MVVVM app with a view model that uses Hammock .
I call my Get2 function in the code behind my main page like this:
private void List2_SelectionChanged_1(object sender, SelectionChangedEventArgs e)
{
if (List2.SelectedItem != null)
{
((MainPageViewModel)DataContext).Get2();
NavigationService.Navigate(new Uri("/Page3.xaml", UriKind.Relative));
}
}
Here is my Get2 Function:
public void Get2()
{
[...]
restClient.BeginRequest(restRequest, Get2CallBack);
}
private void GetListStatusesCallBack(RestRequest Request, RestResponse Response, object Obj)
{
[...]
}
But what happens at the end of my Get2() function is that instead of reaching the callback function just after , it goes back to my MainPage code behind, executs the NavigationService.Navigate(new Uri("/Page3.xaml", UriKind.Relative));
, quits the List2_SelectionChanged_1 and then reaches the CallBack function finally .
How come my CallBack Function is not reached just after Get2() ?
I would like the CallBack to be reached before the Navigation Event,

Your method should be a Synchronous call. The behavior that you are describing requires the use of a Synchronous call.
However, looking at your code, the call looks to be async (BeginRequest).
Perhaps if you could post more details about the variable restClient (Datatype, intended usage etc.) it would be helpful.
Alternatively, you could try having this line in the call back method
NavigationService.Navigate(new Uri("/Page3.xaml", UriKind.Relative));

Related

Button.PerformClick() doesn't wait an async event hanlder

I have a problem in a Windows Forms application where I use PerformClick to call an async event handler. It seems that the event handler doesn't await but just returns immediately. I have created this simple application to show the problem (it's just a form with 3 buttons, easy to test yourself):
string message = "Not Started";
private async void button1_Click(object sender, EventArgs e)
{
await MyMethodAsync();
}
private void button2_Click(object sender, EventArgs e)
{
button1.PerformClick();
MessageBox.Show(message);
}
private void button3_Click(object sender, EventArgs e)
{
MessageBox.Show(message);
}
private async Task MyMethodAsync()
{
message = "Started";
await Task.Delay(2000);
message = "Finished";
}
The interesting problem here is, what do you think message shows, when I click Button2?
Surprisingly, it shows "Started", not "Finished", as you would expect. In other Words, it doesn't await MyMethod(), it just starts the Task, then continues.
Edit:
In this simple code I can make it Work by calling await Method() directly from Button2 event handler, like this:
private async void button2_Click(object sender, EventArgs e)
{
await MyMethodAsync();
MessageBox.Show(message);
}
Now, it waits 2 seconds and displays 'Finished'.
What is going on here? Why doesn't it work when using PerformClick?
Conclusion:
Ok, now I get it, the conclusion is:
Never call PerformClick if the eventhandler is async. It will not await!
Never call an async eventhandler directly. It will not await!
What's left is the lack of documentation on this:
Button.PerformClick should have a Warning on the doc page:
Button.PerformClick
"Calling PerformClick will not await async eventhandlers."
Calling an async void method (or eventhandler) should give a compiler Warning: "You're calling an async void method, it will not be awaited!"
You seem to have some misconceptions about how async/await and/or PerformClick() work. To illustrate the problem, consider the following code:
Note: the compiler will give us a warning but let's ignore that for the sake of testing.
private async Task MyMethodAsync()
{
await Task.Delay(2000);
message = "Finished"; // The execution of this line will be delayed by 2 seconds.
}
private void button2_Click(object sender, EventArgs e)
{
message = "Started";
MyMethodAsync(); // Since this method is not awaited,
MessageBox.Show(message); // the execution of this line will NOT be delayed.
}
Now, what do you expect the MessageBox to show? You'd probably say "started".1 Why? Because we didn't await the MyMethodAsync() method; the code inside that method runs asynchronously but we didn't wait for it to complete, we just continued to the next line where the value of message isn't yet changed.
If you understand that behavior so far, the rest should be easy. So, let's change the above code a little bit:
private async void button1_Click(object sender, EventArgs e)
{
await Task.Delay(2000);
message = "Finished"; // The execution of this line will be delayed by 2 seconds.
}
private void button2_Click(object sender, EventArgs e)
{
message = "Started";
button1_Click(null, null); // Since this "method" is not awaited,
MessageBox.Show(message); // the execution of this line will NOT be delayed.
}
Now, all I did was that I moved the code that was inside the async method MyMethodAsync() into the async event handler button1_Click, and then I called that event handler using button1_Click(null, null). Is there a difference between this and the first code? No, it's essentially the same thing; in both cases, I called an async method without awaiting it.2
If you agree with me so far, you probably already understand why your code doesn't work as expected. The code above (in the second case) is nearly identical to yours. The difference is that I used button1_Click(null, null) instead of button1.PerfomClick(), which essentially does the same thing.3
The solution:
If you want to wait for the code in button1_Click to be finished, you need to move everything inside button1_Click (as long as it's asynchronous code) into an async method and then await it in both button1_Click and button2_Click. This is exactly what you did in your "Edit" section but be aware that button2_Click will need to have an async signature as well.
1 If you thought the answer was something else, then you might want to check this article which explains the warning.
2 The only difference is that in the first case, we could solve the problem by awaiting the method, however, in the second case, we can't do that because the "method" is not awaitable because the return type is void even though it has an async signature.
3Actually, there are some differences between the two (e.g., the validation logic in PerformClick()), but those differences don't affect the end result in our current situation.

How to refresh textBlock Text after updating its text?

code snippet linkI am developing windows 8 application in C#. I have 2 textBlocks in my GUI.
I get text input from user and assign it to textBlock1 using textBlock1.Text = user input;.
After that I call a method that gets some data from API in JSON and part of data it populated in textBlock2 using textBlock2.Text = "specific data";
but text of both text blocks is changed at same time that I don't want. according to code sequence text of textBlock1 should be changed earlier as textblock2 text change happens after call to API that take about 10 sec.
How can I first change text of textBlock1?
help me out as i'm new to windows development.
The API call locks the UI. If you want to update textblock1 before textblock 2 you have to make the call on a different thread.
Something like this should work.
//make the method async.
private async void button_click(object sender, RoutedEventArgs e)
{
//...
//assuming userMessage is textblock2.
userMessage.Text = await MakeAPICallAsync();
//...
}
//where MakeAPICallAsync() is another function defined as
private async Task<string> MakeAPICallAsync()
{
//API call;
return result;
}
In your method, you use
HttpResponseMessage response = client.GetAsync(userInput).Result;
string result = response.Content.ReadAsStringAsync().Result;
If you call an Async method, you should use the await statement, if the code should wait for the method call, like this:
HttpResponseMessage response = await client.GetAsync(userInput).ConfigureAwait(false);
string result = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
But for that, you must change your method to async:
private async void button_Click(object sender, RoutedEventArgs e)
Perhaps, it solves your problem.
Offtopic: Because you are new, I recommend visit this page and read them, to understand Stack Overflow better.
Update:
I forgot ".ConfigureAwait(false)"

In-window popups: How to block UI thread?

I'm currently implementing a MessageDialog control. It is there to replace MessageBox entirely and is displayed as an "in-window popup" (correct UX term needed).
Currently, its constructor is private and there is a method Show, just like in MessageBox.Show. However, MessageBox blocks the UI thread and returns a result.
What my MessageDialog control currently does is having a Action<MessageDialogResult> callback parameter which gets called when a button is clicked.
Utilizing MessageDialog
// class MessageDialog
public static void MessageDialog.Confirmation(Window owner, string message, Action<MessageDialogResult> callback);
// When used by other controls
MessageDialog.Confirmation(WindowMain.Singleton, true, (result) =>
{
if (result.Button == MessageDialogButton.Yes)
{
//...
}
});
However, having a callback instead of a blocking method call like in MessageBox.Show yields absolutely no benefits for me. It makes things rather complicated. What I'm rather trying to achieve is something like...
if (MessageDialog.Confirmation(WindowMain.Singleton, true).Button == MessageDialogButton.Yes)
{
//...
}
... which is much cleaner in my opinion.
The current code behind is basically
Create instance of MessageDialog and populate content with text
Add it to the children of Window.Content.Children
On button click, call callback(result) and remove from Window.Content.Children
The question: What I would like to achieve is having a blocking method call instead of one that triggers a callback.
Even though the accepted answer seems to work, I propose a better solution using TaskCompletionSource. This is exactly what await was made for - it's still basically just a callback (won't block the thread), but your code looks a lot simpler when using it.
TaskCompletionSource<DialogResult> taskSource;
Task<DialogResult> ShowAsync()
{
return taskSource.Task;
}
public void OkButton_OnClick(EventArgs e, object sender)
{
taskSource.SetResult(DialogResult.OK);
}
public void CancelButton_OnClick(EventArgs e, object sender)
{
taskSource.SetResult(DialogResult.Cancel);
}
You then have to await the call: await Dialog.ShowAsync()
How about something like this:
//DialogControl.cs
bool _closed = false;
DialogResult _result;
DialogResult ShowModal()
{
this.Show();
while(!_closed) Application.DoEvents(); //Infinite loop
return _result;
}
public void OkButton_OnClick(EventArgs e, object sender)
{
_result = DialogResult.OK;
_closed = true;
}
public void CancelButton_OnClick(EventArgs e, object sender)
{
_result = DialogResult.Cancel;
_closed = true;
}

implement callback in user defined libraries using C#

Hi i have some question related to call back feature in libraries created by user in c#
i have created a winform application named "Sample"
i have also created a class library named "Library"
Sample contains only one form that has a button say "CALL"
i have implemented all the coding part in a library
when i click on the call button on form then a method "ACTIVATE CALL" of the library is called.
this method performs some work on a thread.
What i want is when thread work if finished then "CALLBACK" method placed in my winform must be called.
To achieve this i have passed "this" reference of the form to the library
i collected "this" as obj "Object" type in formal arguement in library.
can anybody suggest me how to call callback method?
i tried this:
if(obj.GetType()== typeOf(what to specify here))
{
obj.callback();
}
hope somebody can provide me help.
note: both library and sample are different projects
how to achieve callback feature?
Define your library method with a callback.
public void ACTIVATE(object arg, object arg, Action callback)
{
// Do what you have to do here.
callback.Invoke();
}
Then, in your Sample WinForms client you can call something like this.
public void MethodInSample()
{
Library lib = new Library();
Action callback = () => { DoSomethingHere };
Lib.ACTIVATE(1,1,callback);
}
If you want the callback to return some parameters, you could use a Func<> instead and define the arguments that way.
Alternatively, you could use an event. The BackgroundWorker object is a good example of this. This class has a method called RunWorkerAsync(), which causes some work to be done on a background thread. There is then an event called RunWorkerCompleted which you listen on to indicate when the background thread has completed.
Both methods are valid I think, but the second has the benefit of allowing more than one party to listen for completion of the work.
Have the user of the ActivateCall function supply a callback so in you library:
function void ActivateCall(Action callback){
//Do Stuff
if (null != callback){
callback();
}
}
and then in your main form:
function button1_Click(object sender, EventArgs e){
library.ActivateCall(DoStuff);
}
There are a number of things to look out for though since you say you are doing stuff in a separate thread within the library call. If you are altering the GUI at all in the callback you will need to make sure you do the work in the GUI thread. You will also need to make sure you run the callback once all the work in the thread has been completed (I suspect).
To make sure your callback is run in the GUI thread (if required) do something like this:
function button1_Click(object sender, EventArgs e){
library.ActivateCall(DoStuff());
}
function void DoStuff(){
if (InvokeRequired(){
Invoke(DoStuff);
return;
}
//Do stuff here....
}
Finally i achieved this using Delegate+Event
*****************Sample class**************************
call()
{
//activate method of library is called
libraryObject.stop += new LibraryClass.callback(setCallbackMethod);
libraryObject.activate();
}
public void setCallbackMethod(String str)
{
// most important to be back on main thread
this.Invoke((MethodInvoker)delegate
{
btn.Enabled = true;
});
}
*******************Library***************************
public delegate void callback(String str);
public event callback stop;
activate()
{
//instantiates a thread/timer
aTimer = new System.Timers.Timer();
aTimer.Elapsed += new ElapsedEventHandler(CheckForMessage);
aTimer.Interval = 1000;
aTimer.Start();
}
public void CheckForMessage(object source, ElapsedEventArgs e)
{
//performs some work
//calls callback method of ui thread in sample code
if (stop != null)
{
stop("COMPLETED");
}
}

How can I call an async method of a webservice that returns void?

The webservice that I am calling from my application has two methods.
XmlNode getCase(string parameter) // synchronous
void getCaseAsync(string parameter) //async
I can simply call method 1 and store the results in an xmlnode like this,
XmlNode node=webservice.getCase("test");
but I can not figure out how to get the result back from the async method returning void. I tried this but get an erorr:
IAsyncResult result = webservice.getCaseAsync(("test");
Any ideas?
Yes Brian you are right there is a "completed" event,that I already have implemented in my Form consturcor class like this,
webService.getCaseCompleted += new webService.getCaseCompletedEventHandler(webService_getCaseCompleted);
void webService_getCaseCompleted(object sender,webService.getCaseCompletedEventArgs e)
{
webService.GetCaseAsync("test");
}
I also have a button on my form which I want to run the code from there.I tried this,
private void button1_Click(object sender, EventArgs e)
{
webService_getCaseCompleted(this, null);
}
But I get error that "e" is Null.How should I run this methode?
Thanks,
Dave
Assuming you are talking about a WCF proxy, it would go like this:
My IService would look something like this:
[ServiceContract]
public interface IService
{
[OperationContract]
void DoSomething();
}
In your client, your code would look like this:
var serviceProxy = new MyService.Service1Client();
serviceProxy.DoSomethingCompleted += DoSomethingComplete;
serviceProxy.DoSomethingAsync();
And your asynchronous callback would look like this:
private void DoSomethingComplete(object sender, AsyncCompletedEventArgs e)
{
// Handle the result
}
In the completed handler, you can check to see if the service call succeeded (e.Cancelled == false and e.Error == null).
Remember. An asynchronous call will not give you a result immediately. It will tell you when it is complete.
Hope this helps?
If method return type is void it means it doesn't return anything. If it doesn't return anything then you cannot expect any result from it, that's why your assignment is invalid. You should just call such methods like that:
webservice.getCaseAsync("test");
If you want it to return something modify the method return type to something other then void.

Categories