How to get the result from an event handler from another class? - c#

I have to get the result from a web service. This is what I used:
EDIT:
private void Button_Click(object sender, RoutedEventArgs e)
{
webService.WebServiceHuscSoapClient a = new webService.WebServiceHuscSoapClient();
a.InsertNewUserCompleted += a_InsertNewUserCompleted;
a.InsertNewUserAsync("name", "phonenumber", "address");
}
void a_InsertNewUserCompleted(object sender, webService.InsertNewUserCompletedEventArgs e)
{
MessageBox.Show(e.Result.ToString());
}
Is there any way that I could put all this function and even handler to a class and when I want to get data from my webservice I would do something like this:
string json = MyWebService.GetData();

First, your call of DownloadStringAsync suggests passing in username and password, but it doesn't work like that. Check the documentation.
To (not really :-) ) answer your question: a far better approach these days is to use the new 'async/await' functionality available in C# 5. See this link for a comprehensive overview.
Your example then becomes trivial (no need to hook up a separate event handler anymore)
private async void Button_Click(object sender, RoutedEventArgs e)
{
WebClient webclient = new WebClient();
// TODO set up credentials
string result = await webclient.DownloadStringTaskAsync("http://your-url-here");
textBlock1.Text = str;
}
You could then still extract this in a separate, reusable (async) method:
private async Task<string> GetDataAsync()
{
WebClient webClient = new WebClient();
// TODO set up credentials
string result = await webclient.DownloadStringTaskAsync("http://your-url-here");
return result;
}
If you want to stick with the event-based approach, you could wrap the functionality in a separate class with it's own event, but that wouldn't bring you much gain IMO.

I figured it out from this article: How to use async-await with WCF in VS 2010 for WP7?
More: Async CTP - How can I use async/await to call a wcf service?
I wrote this on another class:
public static Task<int> InsertNewUser(string name, string phonenumber,string address) //can make it an extension method if you want.
{
TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
service.InsertNewUserCompleted += (object sender, WebService.InsertNewUserCompletedEventArgs e) => //change parameter list to fit the event's delegate
{
if (e.Error != null) tcs.SetResult(-1);
else
tcs.SetResult((int)e.Result);
};
service.InsertNewUserAsync(name, phonenumber,address);
return tcs.Task;
}
then I could call it from my class:
int su = await WebServiceHelper.SignUp("blabla", "0123465","huehuehue");

Related

Returning / setting a value within a thread

I'm trying to open an OpenFileDialog within C# (codebehind, on an asp.net page). Because the regular references and the system.windows.form ones have some conflicts, I'm having to use the OpenFileDialog box within a thread, as below:
protected void Button1_Click(object sender, EventArgs e)
{
Thread newThread = new Thread(new ThreadStart(BrowseForFile));
newThread.SetApartmentState(ApartmentState.STA);
newThread.Start();
}
static void BrowseForFile()
{
System.Windows.Forms.OpenFileDialog MyFile = new System.Windows.Forms.OpenFileDialog();
if (MyFile.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
}
}
The way the page works means that this has to be in C# - using asp.net's fileupload won't work.
Now, the OpenFileDialog appears fine, and I can get values from it, but I ideally need to pass values into the thread (the BrowseForFile) and have it work with other controls on the page, in order to set things up. However, I'm very new to using threads.
Can someone show me, basically, how to get an integer from the Button1_Click into the BrowseForFile, and how I would set a label on the page from BrowseForFile?
If you use a modern version of .net you could use async-await for this. This makes it much easier for you to communicate with your dialog and to pass data to the thread that performs your background work.
To be able to use async-await:
Declare your function async
Let your function return Task instead of void and Task<TResult> instead of TResult.
There is one exception: event handlers may return void
In your async function start your other threads using Task.Run
while the other thread is running you can do other things
if you need the result: call await Task.
In your case, you'll have to change your thread class into a procedure that contains the code in your thread class. This procedure may be in any class. It must be declared async and return Task instead of void:
Of course you'll have to change your thread class into an async procedure:
private async Task MyThreadProcedureAsync(string fileName)
{
// do something really slow with fileName and return void
}
protected async void Button1_Click(object sender, EventArgs e)
{
string fileName = this.BrowseForFile();
if (!String.IsNullOrEmpty(fileName))
{
var myTask = Task.Run( () => MyThreadProcedureAsync(fileName))
// if desired do other things.
// before returning make sure the task is ready:
await myTask;
// during this wait the UI keeps responsive
}
}
private string BrowseForFileName()
{
using (var dlg = new System.Windows.Forms.OpenFileDialog())
{
// if needed set some properties; show the dialog:
var dlgResult = dlg.ShowDialog(this);
if (dlgResult == System.Windows.Forms.DialogResult.OK)
{
return dlg.FileName;
}
else
{
return null;
}
}
}

EventHandler vs Timeout

I had write a little application on c# to reading some plc data by using ethernet protocol. Ethernet socket, open and close are stored inside a .dll library.
Now, i using this public method:
public static string readdata()
{
try
{
...
return (plcdata());
}
catch
{}
}
My doubt: if the plcdata() (that is a method of a .dll) waiting a few second (for istance slow comunication ...) my application may be frozen.
So, i try to add a EventHandler on string returned like this:
private static TextBox auxDataTextBox = new TextBox();
public static void goRead()
{
auxDataTextBox.TextChanged += new EventHandler(auxDataIncoming);
auxDataTextBox.Text = plcdata();
}
private static void auxDataIncoming(object sender, EventArgs e)
{
// Do something
}
In this case when the "plcdata()" changed, the auxDataIncoming will be raise.
It is correct? Or is better make a timeout control? Or make new thread?
Thanks a lot for yours opinion
Your change won't make a difference, it' still all running on the UI thread. To make plcdata() a non-blocking call you would need to fire it off on another thread e.g.
private static TextBox auxDataTextBox = new TextBox();
public static void goRead()
{
auxDataTextBox.TextChanged += new EventHandler(auxDataIncoming);
Task.Factory.StartNew(() => {
return plcData();
}).ContinueWith(task => {
auxDataTextBox.Text = task.Result;
}, null, TaskContinuationOptions.NotOnFaulted, TaskScheduler.FromCurrentSynchronizationContext());
}
private static void auxDataIncoming(object sender, EventArgs e)
{
// Do something
}
This will not unfreeze your application. The effect will be exactly the same. This is because you are still running the plcdata on your UI thread.
The whole event structure you set up does not make sense at all.
You should look into multithreading. A very easy way to do this is using a BackgroundWorker.

Using async/await with void method

in my Windows Phone 8 application, I have a LoadData() method in my file MainViewModel.cs.
This method load data from a WCF service with entity framework...
Then, in my pages, I call LoadData()
The LoadData() method :
public void LoadData()
{
client.GetMoviesCompleted += new EventHandler<ServiceReference1.GetMoviesCompletedEventArgs>(client_GetMoviesCompleted);
client.GetMoviesAsync();
client.GetTheatersCompleted += new EventHandler<ServiceReference1.GetTheatersCompletedEventArgs>(client_GetTheatersCompleted);
client.GetTheatersAsync();
this.IsDataLoaded = true;
}
With the methods :
private void client_GetMoviesCompleted(object sender, ServiceReference1.GetMoviesCompletedEventArgs e)
{
Movies = e.Result;
}
private void client_GetTheatersCompleted(object sender, ServiceReference1.GetTheatersCompletedEventArgs e)
{
Theaters = e.Result;
}
Then in my pages :
App.ViewModel.LoadData();
The problem is that it doesn't wait until the data is loaded.
Can you help me to use Async/Await the LoadData() method to wait until the data is loaded ?
Thanks
So we'll start with these two methods that convert your existing methods from an event-based model into a task based model. You'll need to modify them slightly to line up with your types as I don't quite have enough information to replicate them completely, but the remaining change should be small:
public static Task<Movie[]> WhenGetMovies(MyClient client)
{
var tcs = new TaskCompletionSource<Movie[]>();
Action<object, Movie[]> handler = null;
handler = (obj, args) =>
{
tcs.SetResult(args.Result);
client.GetMoviesCompleted -= handler;
};
client.GetMoviesCompleted += handler;
client.GetMoviesAsync();
return tcs.Task;
}
public static Task<Theater[]> WhenGetMovies(MyClient client)
{
var tcs = new TaskCompletionSource<Theater[]>();
Action<object, Theater[]> handler = null;
handler = (obj, args) =>
{
tcs.SetResult(args.Result);
client.GetTheatersCompleted -= handler;
};
client.GetTheatersCompleted += handler;
client.GetTheatersAsync();
return tcs.Task;
}
Now that we can get tasks that represent the completion of these async operations loading the data is easy:
public async Task LoadData()
{
var moviesTask = WhenGetMovies(client);
var theatersTask = WhenGetTheaters(client);
var movies = await moviesTask;
var theaters = await theatersTask;
}
The problem is, when you execute your LoadData() method, the runtime don't wait to continue the execution of your method.
You can simply do a think like this :
private bool _moviesLoaded;
private bool _theatersLoaded;
private void client_GetMoviesCompleted(object sender, ServiceReference1.GetMoviesCompletedEventArgs e)
{
Movies = e.Result;
_moviesLoaded = true;
TrySetDataIsLoaded();
}
private void client_GetTheatersCompleted(object sender, ServiceReference1.GetTheatersCompletedEventArgs e)
{
Theaters = e.Result;
_theatersLoaded = true;
TrySetDataIsLoaded();
}
private void TrySetDataIsLoaded()
{
if(_moviesLoaded && _theatersLoaded) this.IsDataLoaded = true;
}
If you want to use async and await, you can try to work with TaskCompletionSource
The bottom line is that you'll need to design and implement a "loading" state for your application. This is true whether you use event-based asynchronous programming (like your current code) or async/await. You should not synchronously block the UI until the loading is complete.
Personally, I like to (synchronously) initialize everything into the "loading" state, and when the asynchronous loading completes, have it update data-bound items on the View Model. The View then transitions to a "ready" state via data binding.
make above two variables (private bool _moviesLoaded;private bool _theatersLoaded;) as properties and set them in completed eventhandlers . and till the set is called use loader and when set is called disable this loader and now you can use this data for your work..

Calling webservice on windows phone

How to Calling the webservice without using the function service_GetItemInfoCompleted() in the below code?
Can it get the result without the function service_GetItemInfoCompleted().
Any idea?
private void btnFind_Click(object sender, RoutedEventArgs e)
{
Service1Client service = new Service1Client();
service.GetItemInfoAsync(txt1.Text);
service.GetItemInfoCompleted += new EventHandler<GetItemInfoCompletedEventArgs>(service_GetItemInfoCompleted);
}
void service_GetItemInfoCompleted(object sender, GetItemInfoCompletedEventArgs e)
{
txb1.Text = e.Result;
}
Example 2
public void running()
{
ServiceReference1.WebServiceSoapClient test = new ServiceReference1.WebServiceSoapClient();
test.ReadTotalOutstandingInvoiceCompleted += new EventHandler<ServiceReference1.ReadTotalOutstandingInvoiceCompletedEventArgs>(serviceClient);
test.ReadTotalOutstandingInvoiceAsync();
}
public void serviceClient(object sender, ReadTotalOutstandingInvoiceCompletedEventArgs e)
{
answer = int.parse(e.Result.ToString());
}
The above approach is the correct approach: Send the request to the service and when it returns, display the returned data in your textbox.
If you performed the same operation in one blocking call ...
private void btnFind_Click(object sender, RoutedEventArgs e)
{
Service1Client service = new Service1Client();
txtb1.Text = service.GetItemInfoAsync(txt1.Text);
}
... then your main UI thread would block until the data is returned from the service. If the service took several seconds to return, then your app's UI would 'hang' intermittently for several seconds, giving your users a very poor experience. This is a bad thing to do and something to avoid at all cost!
So we want to asynchronously call your service and wait for it to return without blocking threads. But rather than write the async code by hand, we can use the new async & await keywords in C# 5.0 to achieve the same goal as above but with less code & complexity:
private async void btnFind_Click(object sender, RoutedEventArgs e)
{
Service1Client service = new Service1Client();
string result = await service.GetItemInfoAsync(txt1.Text);
txtb1.Text = result;
}
Note the use of async and await in the code above. The async keyword tells the compiler that you want it to build a state-machine which is driven by await states. Each await keyword tells the compiler to add a new state to the state-machine. When the code is executed, the await code sets up the state-machine to pick-up at the next operation when the awaited-upon task (service.GetItemInfoAsync() in this case) completes.
HTH.

Start second method after first method is finished with thread functionality c#

I have a method which is rebuilding the product catalog of a webshop. This is neccessary after I change some product information. After the rebuilding method I would like to start a second method to generate full text index of the webshop. I can watch the status of the first method (RebuildCatalog). If the status is "RebuildFinished" then I would like to start the second method (GenerateFullTextIndex). I would like to use Threads functionality. Does someone can create an example of how to implementate this scenario?
I would like to use Threads functionality.
It really doesn't sound like you do. Starting one method after another finishes is as simple as:
var status = RebuildCatalog();
if (status == Status.RebuildFinished)
{
GenerateFullTextIndex();
}
No threading required. If you really think you need multiple threads, you should explain why you think they'll help. At what point do you need to perform multiple tasks concurrently?
Well, if you want to use a multiple threads and oranize your calls in chain so they are executed on another thread, but in sequence, and you are using .NET Framework 4.0>, you can use a Task Parallelism, like for example using Task::ContinueWith method.
Example (preudocode from MSDN):
Task<byte[]> getData = new Task<byte[]>(() => GetFileData());
Task<double[]> analyzeData = getData.ContinueWith(x => Analyze(x.Result));
Task<string> reportData = analyzeData.ContinueWith(y => Summarize(y.Result));
getData.Start();
//or...
Task<string> reportData2 = Task.Factory.StartNew(() => GetFileData())
.ContinueWith((x) => Analyze(x.Result))
.ContinueWith((y) => Summarize(y.Result));
Using events would seem to be simpler than watching the status.
In your rebuild catalog code fire a "finished" event on completion:
public event EventHandler<EventArgs> RebuildFinished;
private void Rebuild(...)
{
// Rebuild the catalog
this.RebuildFinished(this, new EventArgs(...));
}
Then handle it:
this.catalog.RebuildFinished += this.RebuildFinished;
private void RebuildFinished(object sender, EventArgs e)
{
// Rebuild the index
}
Now both of these can (and probably should) be using threads to ensure that the UI of your application stays responsive:
this.catalogThread = new Thread(new ThreadStart(this.catalog.Rebuild));
As I can assume from your question your rebuilding method probably takes up considerable time and that is why you want to run in a separate thread. Therefore I would suggest implementing Event based async pattern. When your rebuilding (async) method finishes it will throw finished event with AsyncCompletedEventArgs (which you can subclass to pass your result status) and from there you will start your second method.
BackgroundWorker bw1 = new BackgroundWorker();//To rebuild catalog.
BackgroundWorker bw2 = new BackgroundWorker();//To generate text.
public Form1()
{
InitializeComponent();
bw1.DoWork += bw1_DoWork;
bw1.RunWorkerCompleted += bw1_RunWorkerCompleted;
bw2.DoWork += bw2_DoWork;
bw2.RunWorkerCompleted += bw2_RunWorkerCompleted;
bw1.RunWorkerAsync();//Start new thread. - Rebuild catalog.
}
void bw1_DoWork(object sender, DoWorkEventArgs e)
{
//Rebuild catalog.
}
void bw1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
bw2.RunWorkerAsync();//Generate text.
}
void bw2_DoWork(object sender, DoWorkEventArgs e)
{
//Generate text.
}
void bw2_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//Whatever...
}

Categories