Calling a async method multiple times - silverlight

I am calling a async method having a single parameter,
It will return me the result according to parameter.
I am calling that method more than one time with different parameter value, but in Completed event i am getting the same value for all.
client.ListAllLookupValuesByTypeCompleted += client_ListAllAddressFormatCompleted;
client.ListAllLookupValuesByTypeAsync("AddressFormat");
client.ListAllLookupValuesByTypeCompleted += client_ListAllPhoneFormatCompleted;
client.ListAllLookupValuesByTypeAsync("PhoneFormat");
void client_ListAllAddressFormatCompleted(object sender, ListAllLookupValuesByTypeCompletedEventArgs e)
{
cmbAddressFormat.ItemsSource = e.Result;
}
void client_ListAllPhoneFormatCompleted(object sender, ListAllLookupValuesByTypeCompletedEventArgs e)
{
cmbPhonePrintFormat.ItemsSource = e.Result;
}
any suggetions.
Thanks.

Got the Answer
Your method may return a different value based on the first parameter, but both handlers will be called at the same time every time, regardless of what you send it. If this is a standard webservice reference, then you should see an object userState parameter available for you and this can be used to determine what to do.
client.ListAllLookupValuesByTypeCompleted += client_ListAllLookupValuesCompleted;
client.ListAllLookupValuesByTypeAsync("AddressFormat", true);
client.ListAllLookupValuesByTypeAsync("PhoneFormat", false);
void client_ListAllLookupValuesCompleted(object sender, ListAllLookupValuesByTypeCompletedEventArgs e)
{
// e.UserState will either be false or true
if ((bool)e.UserState)
cmbAddressFormat.ItemsSource = e.Result;
else
cmbPhonePrintFormat.ItemsSource = e.Result;
}

You should either only add the completed event handler once and pass back the type of data retrieved in the ListAllLookupValuesByTypeCompletedEventArgs object:
client.ListAllLookupValuesByTypeCompleted += client_ListFormatCompleted;
client.ListAllLookupValuesByTypeAsync("AddressFormat");
client.ListAllLookupValuesByTypeAsync("PhoneFormat");
void client_ListFormatCompleted(object sender, ListAllLookupValuesByTypeCompletedEventArgs e)
{
if (e.Type == ResultType.AddressFormat)
{
cmbAddressFormat.ItemsSource = e.Result;
}
else
{
cmbPhonePrintFormat.ItemsSource = e.Result;
}
}
or have two separate events one for each data type:
client.ListAddressLookupValuesCompleted += client_ListAddressFormatCompleted;
client.ListAddressLookupValuesAsync();
client.ListPhoneLookupValuesCompleted += client_ListPhoneFormatCompleted;
client.ListPhoneLookupValuesByTypeAsync();
void client_ListAddressFormatCompleted(object sender, ListAddressValuesCompletedEventArgs e)
{
cmbAddressFormat.ItemsSource = e.Result;
}
void client_ListPhoneFormatCompleted(object sender, ListPhoneValuesCompletedEventArgs e)
{
cmbPhonePrintFormat.ItemsSource = e.Result;
}
In this case you'll need to refactor your server side code to match.

without source code it is a little hard to guess the problem but I think the problem is, that you add the same completed handler several times. Like this:
for(int i =0; i < 10; i++)
{
ws.callCompleted += CallCompletedHandler;
ws.callAsync(i);
}
void CallCompletedHandler(object sender, EventArgs args)
{
handle result
}
Do you remove the handler in the completed event?
void CallCompletedHandler(object sender, EventArgs args)
{
ws.callCompleted -= CallCompletedHandler;
handle result
}
This could solve your problem.
Here are some other ideas:
As you call is async it can be that you completed handler is called up to ten times for each call (cause you added it 10 times). You can use the UserState Parameter (http://msdn.microsoft.com/en-us/library/wewwczdw(v=vs.80).aspx) so you can match your completed handler with your call.
for(int i =0; i < 10; i++)
{
ws.callCompleted += CallCompletedHandler;
ws.callAsync(i, i); //Second param is user state
}
void CallCompletedHandler(object sender, EventArgs args)
{
if(args.UserState == //Your check here;)
{
ws.callCompleted -= CallCompletedHandler; //Remove the handler
handle result
}
}
The problem here is, that you need some sort of class variable to track your UserStates.
If you write it like this, you don't have to do this.
for(int i =0; i < 10; i++)
{
CallWebservice(i);
}
void CallWebservice(int i)
{
EventHandler myHandler= null;
myHandler = (s, args) => {
if(args.UserState == i){
ws.callCompleted -= myHandler; //Remiov
Handleresult
};
ws.callCompleted += myHandler; //Add the handler
ws.callAsync(i, i); //Call the ws
}
}
If you have any further questions, jsut leave a comment. If you could provide some source code, I think we can help you more.
BR,
TJ

Related

Updating UI from a background thread which is called in a loop in main UI when the thread finishes

I have a WinForms application that is calling a business class method that performs some heavy duty action taking about 5 seconds for each call. The main form calls this method in a loop. This loop can run from 10 times to maybe up to 10 thousand times.
The WinForms application sends a parameter to the business class and has an area to display the time taken for each method call and what the value returned by the method. How do I inform my main window and update a text area in the main winform with what the method has returned for each call?
Currently the data comes all at once after all the threads have finished. Is there a way to update the UI for all the iterations of the loop once the each call is done? I don't mind if it is done sequentially also.
The FORM
HeavyDutyClass hd;
public Form1()
{
InitializeComponent();
hd = new HeavyDutyClass();
}
//BUTTON CLICK
private void Start_Click(object sender, EventArgs e)
{
int filecount = 5000; //BAD - opening 5000 threads! Any other approach?
hd.FileProcessed += new EventHandler(hd_FileProcessed);
var threads = new Thread[filecount];
for (int i = 0; i < filecount; i++)
{
threads[i] = new Thread(() => { hd.LongRunningMethod(); });
threads[i].Start();
}
}
//BUSINESS CLASS EVENT THAT FIRES WHEN BUSINESS METHOD COMPELTES
void hd_FileProcessed(object sender, EventArgs e)
{
if (dgv.InvokeRequired)
{
dgv.Invoke((MethodInvoker)delegate { UpdateGrid(); });
}
}
private void UpdateGrid()
{
dgv.Rows.Add(1);
int i = dgv.Rows.Count;
dgv.Rows [ i-1].Selected = true;
dgv.FirstDisplayedScrollingRowIndex = i - 1;
}
The business HeavyDuty class
public event EventHandler FileProcessed;
public HeavyDutyClass()
{
}
protected virtual void OnMyEvent(EventArgs e)
{
if (FileProcessed != null)
{
FileProcessed(this, e);
}
}
public bool LongRunningMethod()
{
for (double i = 0; i < 199990000; i++)
{
//time consuming loop
}
OnMyEvent(EventArgs.Empty);
return true;
}
Add a Winforms Project, Drop a Label Control on the Form , Copy-Paste this code and Hit F5
[EDIT]: Updated with the business class comment from the user
NB: My form class is named Form3. You may have to change your Program.cs or vice-versa.
using System.ComponentModel;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public class BusinessClass
{
public int MyFunction(int input)
{
return input+10;
}
}
public partial class Form3 : Form
{
private BackgroundWorker _worker;
BusinessClass _biz = new BusinessClass();
public Form3()
{
InitializeComponent();
InitWorker();
}
private void InitWorker()
{
if (_worker != null)
{
_worker.Dispose();
}
_worker = new BackgroundWorker
{
WorkerReportsProgress = true,
WorkerSupportsCancellation = true
};
_worker.DoWork += DoWork;
_worker.RunWorkerCompleted += RunWorkerCompleted;
_worker.ProgressChanged += ProgressChanged;
_worker.RunWorkerAsync();
}
void DoWork(object sender, DoWorkEventArgs e)
{
int highestPercentageReached = 0;
if (_worker.CancellationPending)
{
e.Cancel = true;
}
else
{
double i = 0.0d;
int junk = 0;
for (i = 0; i <= 199990000; i++)
{
int result = _biz.MyFunction(junk);
junk++;
// Report progress as a percentage of the total task.
var percentComplete = (int)(i / 199990000 * 100);
if (percentComplete > highestPercentageReached)
{
highestPercentageReached = percentComplete;
// note I can pass the business class result also and display the same in the LABEL
_worker.ReportProgress(percentComplete, result);
_worker.CancelAsync();
}
}
}
}
void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
// Display some message to the user that task has been
// cancelled
}
else if (e.Error != null)
{
// Do something with the error
}
}
void ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label1.Text = string.Format("Result {0}: Percent {1}",e.UserState, e.ProgressPercentage);
}
}
}
With this you can achieve Cancel functionality also very easily.
Observe that during initialisation, I set the WorkerSupportsCancellation = true & then I check for _worker.CancellationPending in the DoWork. So, if you want to cancel the process by a Cancel Button click, then you will write this code in the button handler- _worker.CancelAsync();

WPF & Multi-threading questions

I'm working on building a multi-threaded UI. I would like long processes to be handled by the BackgroundWorker class, and have a small timer on the UI to keep track of how long the process is taking. It's my first time building such a UI, so I'm reading up on related resources on the web. My test code is thus:
private BackgroundWorker worker;
private Stopwatch swatch = new Stopwatch();
private delegate void simpleDelegate();
System.Timers.Timer timer = new System.Timers.Timer(1000);
string lblHelpPrevText = "";
private void btnStart_Click(object sender, RoutedEventArgs e)
{
try
{
worker = new BackgroundWorker(); //Create new background worker thread
worker.DoWork += new DoWorkEventHandler(BG_test1);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(BG_test1end);
worker.RunWorkerAsync();
simpleDelegate del = new simpleDelegate(clockTicker);
AsyncCallback callBack = new AsyncCallback(clockEnd);
IAsyncResult ar = del.BeginInvoke(callBack, null);
lblHelpText.Text = "Processing...";
}
finally
{
worker.Dispose(); //clear resources
}
}
private void clockTicker()
{
//Grab Text
simpleDelegate delLblHelpText = delegate()
{ lblHelpPrevText = this.lblHelpText.Text; };
this.Dispatcher.BeginInvoke(DispatcherPriority.Send, delLblHelpText);
//Start clock
timer.Elapsed += new ElapsedEventHandler(clockTick);
timer.Enabled = true;
swatch.Start();
}
private void clockTick(object sender, ElapsedEventArgs e)
{
simpleDelegate delUpdateHelpTxt = delegate()
{ this.lblHelpText.Text = String.Format("({0:00}:{1:00}) {2}", swatch.Elapsed.Minutes, swatch.Elapsed.Seconds, lblHelpPrevText); };
this.Dispatcher.BeginInvoke(DispatcherPriority.Send, delUpdateHelpTxt);
}
private void BG_test1(object sender, DoWorkEventArgs e)
{
//this.lblHelpText.Text = "Processing for 10 seconds...";
Thread.Sleep(15000);
}
private void BG_test1end(object sender, RunWorkerCompletedEventArgs e)
{
this.lblHelpText.Text = "Process done.";
this.timer.Enabled = false;
this.swatch.Stop();
this.swatch.Reset();
}
static void clockEnd(IAsyncResult ar)
{
simpleDelegate X = (simpleDelegate)((AsyncResult)ar).AsyncDelegate;
X.EndInvoke(ar);
}
The idea is when the button is clicked, we take the status text from a Label (e.g. "Processing...") then append the time onto it every second. I could not access the UI elements from the Timer class as it's on a different thread, so I had to use delegates to get and set the text.
It works, but is there a better way to handle this? The code seems much for such a basic operation. I'm also not fully understanding the EndInvoke bit at the bottom. I obtained the snippet of code from this thread Should One Always Call EndInvoke a Delegate inside AsyncCallback?
I understand the idea of EndInvoke is to receive the result of BeginInvoke. But is this the correct way to use it in this situation? I'm simply worried about any resource leaks but when debugging the callback appears to execute before my timer starts working.
Don't use a separate timer to read the progress of your BackgroundWorker and update the UI. Instead, make the BackgroundWorker itself "publish" its progress to the UI directly or indirectly.
This can be done pretty much anyway you want to, but there's a built-in provision exactly for this case: the BackgroundWorker.ProgressChanged event.
private void BG_test1(object sender, DoWorkEventArgs e)
{
for(var i = 0; i < 15; ++i) {
Thread.Sleep(1000);
// you will need to get a ref to `worker`
// simplest would be to make it a field in your class
worker.ReportProgress(100 / 15 * (i + 1));
}
}
This way you can simply attach your own handler to ProgressChanged and update the UI using BeginInvoke from there. The timer and everything related to it can (and should) go.
You can use timer to update UI. It is normal practice. Just instead of System.Timer.Timer I suggest use System.Windows.Threading.DispatcherTimer. The DispatcherTimer runs on the same thread as the Dispatcher. Also, instead of BackgroundWorker you can use ThreadPool.
Here is my sample:
object syncObj = new object();
Stopwatch swatch = new Stopwatch();
DispatcherTimer updateTimer; // Assume timer was initialized in constructor.
void btnStart_Click(object sender, RoutedEventArgs e) {
lock (syncObj) {
ThreadPool.QueueUserWorkItem(MyAsyncRoutine);
swatch.Start();
updateTimer.Start();
}
}
void updateTimer_Tick(object sender, EventArgs e) {
// We can access UI elements from this place.
lblHelpText.Text = String.Format("({0:00}:{1:00}) Processing...", swatch.Elapsed.Minutes, swatch.Elapsed.Seconds);
}
void MyAsyncRoutine(object state) {
Thread.Sleep(5000);
lock (syncObj)
Dispatcher.Invoke(new Action(() => {
swatch.Stop();
updateTimer.Stop();
lblHelpText.Text = "Process done.";
}), null);
}
private void button1_Click(object sender, EventArgs e)
{
string strFullFilePath = #"D:\Print.pdf";
ProcessStartInfo ps = new ProcessStartInfo();
ps.UseShellExecute = true;
ps.Verb = "print";
ps.CreateNoWindow = true;
ps.WindowStyle = ProcessWindowStyle.Hidden;
ps.FileName = strFullFilePath;
Process.Start(ps);
Process proc = Process.Start(ps);
KillthisProcess("AcroRd32");
}
public void KillthisProcess(string name)
{
foreach (Process prntProcess in Process.GetProcesses())
{
if (prntProcess.ProcessName.StartsWith(name))
{
prntProcess.WaitForExit(10000);
prntProcess.Kill();
}
}
}

call a Async Method multiple times problem in silverlight

Hi i am calling a Async method with different parameter value multiple times giving same result in completed event.
client.ListAllLookupValuesByTypeCompleted += client_ListAllAddressFormatCompleted;
client.ListAllLookupValuesByTypeAsync("AddressFormat");
client.ListAllLookupValuesByTypeCompleted += client_ListAllPhoneFormatCompleted;
client.ListAllLookupValuesByTypeAsync("PhoneFormat");
void client_ListAllAddressFormatCompleted(object sender, ListAllLookupValuesByTypeCompletedEventArgs e)
{
cmbAddressFormat.ItemsSource = e.Result;
}
void client_ListAllPhoneFormatCompleted(object sender, ListAllLookupValuesByTypeCompletedEventArgs e)
{
cmbPhonePrintFormat.ItemsSource = e.Result;
}
please help me.
Thanks.
You can create new instance of client.
...
var client = new XyzClient();
client.ListAllLookupValuesByTypeCompleted += client_ListAllAddressFormatCompleted;
client.ListAllLookupValuesByTypeAsync("AddressFormat");
client = new XyzClient();
client.ListAllLookupValuesByTypeCompleted += client_ListAllPhoneFormatCompleted;
client.ListAllLookupValuesByTypeAsync("PhoneFormat");
...
void client_ListAllAddressFormatCompleted(object sender, ListAllLookupValuesByTypeCompletedEventArgs e)
{
cmbAddressFormat.ItemsSource = e.Result;
}
void client_ListAllPhoneFormatCompleted(object sender, ListAllLookupValuesByTypeCompletedEventArgs e)
{
cmbPhonePrintFormat.ItemsSource = e.Result;
}
Another solution would be to make the second call in the handler of the first one (probably creating new client instance anyway).

Detecting input keystroke during WPF processing

Greetings,
I want to write code that executes within an event handler, inside a WPF Windows application, that can detect a keypress, specifically an "Escape" character keypress, within a processing loop. This will allow the user to escape from processing. I realize this may be accomplished with some kind of multi-threaded approach, but the problem seems so simple I wondered if it might be accomplished as follows:
// Attempt 1: See if Keyboard static IsKeyDown method detects key presses while executing.
// Note that this was not successful. The Keyboard states do not appear to be updated during processing.
bool iskeypressed = false;
while (!iskeypressed)
{
System.Threading.Thread.Sleep(1000);
if (Keyboard.IsKeyDown(Key.Enter))
iskeypressed = true;
}
So, on to attempt #2. I saw some articles and samples using the Pinvoke "GetKeyboardState" method. I'm not sure I used the method correctly, but here is my attempt. It is a bit clumsy to refer to a Windows.Forms enumeration in a WPF application, but it seems like it could work.
// Attempt 2: Use Pinvoke GetKeyboardState method.
// So far, I've been unsuccessful with this as well, but I'm not sure my usage is correct.
bool iskeypressed = false;
while (!iskeypressed)
{
System.Threading.Thread.Sleep(1000);
if (isEscapePressed())
iskeypressed = true;
}
}
[DllImport("user32.dll")] public static extern int GetKeyboardState(byte[] lpKeyState);
private bool isEscapePressed()
{
byte[] keyboardState = new byte[255];
int keystate = GetKeyboardState(keyboardState);
if (keyboardState[(int)System.Windows.Forms.Keys.Escape] == 128)
return true;
else
return false;
}
But unfortunately, I'm not seeing any change in the keyboard states as this executes. I also played around a little with calls to the Dispatcher to see if I could get the keyboard information to refresh during processing, but I have not been successful with any technique.
I'm out of ideas. Can someone propose something? Thank you in advance for your assistance.
David
Something like this:
private bool IsCancelled { get; set; }
private void OnButtonClick(object sender, EventArgs e)
{
Action doWorkDelegate = DoWork;
doWorkDelegate.BeginInvoke(null, null);
}
protected override void OnKeyDown(KeyEventArgs e) {
if (e.Key == Key.Escape) {
IsCancelled = true;
e.Handled = true;
} else {
base.OnKeyDown(e);
}
}
private void DoWork()
{
IsCancelled = false;
while (!IsCancelled)
{
System.Threading.Thread.Sleep(1000);
}
}
The important point is that the method that does the work is executed in a separate thread so the main thread can process user input (key strokes).
You can not detect a key event while you are blocking WPF by executing a very long loop. You must use a multithreaded approach or you have to split the loop.
Using a BackgroundWorker is an easy way to let WPF continue handling the frontend while executing the loop.
private BackgroundWorker bw;
private void Button_Click(object sender, RoutedEventArgs e)
{
if (bw != null)
return;
bw = new BackgroundWorker();
bw.WorkerSupportsCancellation = true;
bw.WorkerReportsProgress = true;
bw.DoWork += (senderBw, eBw) =>
{
for (int i = 0; i < 100; i++)
{
Thread.Sleep(1000);
bw.ReportProgress(i);
if (eBw.Cancel)
return;
}
};
bw.ProgressChanged += (senderBw, eBw) =>
{
//TODO set progressbar to eBw.ProgressPercentage
};
bw.RunWorkerCompleted += (senderBw, eBw) =>
{
this.bw = null;
//TODO frontend stuff (hide progressbar etc)
};
bw.RunWorkerAsync();
}
private void MainWindow_KeyDown(object sender, KeyEventArgs e)
{
if (this.bw != null && this.bw.IsBusy && e.Key == Key.Escape)
this.bw.CancelAsync();
}

Cannot Get Threading right on wpf UI

I am building a proof of concept application before it gets rollout to the real one.
Scenario
I should be able to stop processing in the middle of it.
Toolbar 2 buttons "Start" & "Stop"
User press start and it process a long running task.
User decides out of the blue to stop the task.
I cannot seem to get threading right!! I cannot press stop as it's waiting for the long running task as if the long running task is actually running on UI thread and not as intented on background thread.
What Am I doing wrong can you spot it? Thanks for your help
public partial class TestView : UserControl
{
private readonly BackgroundWorker _worker;
public TestView
{
InitializeComponent();
_worker = new BackgroundWorker();
_worker.RunWorkerCompleted += RunWorkerCompleted;
_worker.DoWork+=DoWork;
_worker.WorkerReportsProgress = true;
_worker.ProgressChanged+=_worker_ProgressChanged;
_worker.WorkerSupportsCancellation = true;
}
static void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
MessageBox.Show("The task has been cancelled");
}
else if (e.Error != null)
{
MessageBox.Show("Error. Details: " + e.Error);
}
else
{
MessageBox.Show("The task has been completed. Results: " + e.Result);
}
}
private delegate void SimpleDelegate();
void DoWork(object sender, DoWorkEventArgs e)
{
for (var i = 0; i < 1000000; i++)
{
_worker.ReportProgress(i, DateTime.Now);
// SimpleDelegate simpleDelegate = () => txtResult.AppendText("Test" + System.Environment.NewLine);
//Dispatcher.BeginInvoke(DispatcherPriority.Normal, simpleDelegate);
}
MessageBox.Show("I have done it all");
}
private void _worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
DateTime time = Convert.ToDateTime(e.UserState);
txtResult.AppendText(time.ToLongTimeString());
txtResult.AppendText(Environment.NewLine);
}
private void BtnStart_Click(object sender, RoutedEventArgs e)
{
_worker.RunWorkerAsync();
}
private void BtnStop_Click(object sender, RoutedEventArgs e)
{
_worker.CancelAsync();
MessageBox.Show("Process has been stopped!");
}
}
You run a very tight loop inside of DoWork and continuously push Invoked ProgressUpdates to the Main Thread. That will make it sluggish.
But the real problem is that DoWork has to cooperate in Cancellation:
void DoWork(object sender, DoWorkEventArgs e)
{
for (var i = 0; i < 1000000; i++)
{
if (_worker.CancelationPending)
{
e.Cancel = true;
break; // or: return to skip the messagebox
}
_worker.ReportProgress(i, DateTime.Now);
}
MessageBox.Show("I have done it all"); // remove or make depend on Cancelled
}

Resources