how to wait c# webbrowser to complete document load in TPL - c#

Hi I have this two blocks of code
when I navigate the browser to a url and try to wait for it I get a deadlock I think=) any help suggestions are much appreciated!
I'm just adding some more text here so I am able to post the question :/ sorry
foreach (var davaType in davaTypes)
{
Parallel.ForEach(years, year =>
{
ts.Add(Task.Run(() =>
{
var th = new Thread(async () =>
{
await doWorkAsync(year, davaType, tarafType);
Application.Run();
});
th.IsBackground = true;
th.SetApartmentState(ApartmentState.STA);
th.Start();
}));
});
}
and
public static async Task doWorkAsync(int year, string davaType, string tarafType)
{
using (var browser = new MyBrowser())
{
Console.WriteLine("Im Created Here");
browser.Navigate("some url");
while (!browser.MyLoaded)
{
Console.WriteLine("waiting");
await Task.Delay(10);
}
Console.WriteLine("complete");
browser.Document?.GetElementById("DropDownList1")?.SetAttribute("value", davaType);
browser.Document.GetElementById("DropDownList3")?.SetAttribute("value", tarafType);
browser.Document.GetElementById("DropDownList4")?.SetAttribute("value", year.ToString());
browser.Document.GetElementById("Button1").MyClick();
await browser.WaitPageLoad(10);
Console.WriteLine("Im DoneHere");
}
}

You can use TaskCompletionSource to create a task and await for it after the page is loaded.
See: UWP WebView await navigate.

I have experienced this problem before and the solution I have applied is as follows;
private void waitTillLoad()
{
WebBrowserReadyState loadStatus = default(WebBrowserReadyState);
int waittime = 100000;
int counter = 0;
while (true)
{
loadStatus = webBrowser1.ReadyState;
Application.DoEvents();
if ((counter > waittime) || (loadStatus == WebBrowserReadyState.Uninitialized) || (loadStatus == WebBrowserReadyState.Loading) || (loadStatus == WebBrowserReadyState.Interactive))
{
break; // TODO: might not be correct. Was : Exit While
}
counter += 1;
}
counter = 0;
while (true)
{
loadStatus = webBrowser1.ReadyState;
Application.DoEvents();
if (loadStatus == WebBrowserReadyState.Complete)
{
break; // TODO: might not be correct. Was : Exit While
}
counter += 1;
}
}

You need to create only one separate thread for WinForms. Other threads will be created by individual browsers themselves. What you do need is handling their DocumentCompleted event. The first time the document completes you enter and submit data, the second time it completes you get the result. When all browsers finish, you exit the winforms thread and you're done.
Note: To launch bots from winforms thread, I made use of Idle event handler, just because it was the first solution that came to my mind. You may find better way.
using System;
using System.Collections.Generic;
using System.Threading;
using System.Windows.Forms;
namespace ConsoleBrowsers
{
class Program
{
static void Main(string[] args)
{
string url = "https://your.url.com";
string[] years = { "2001", "2002", "2003"};
string[] davas = { "1", "2", "3" };
string taraf = "1";
int completed = 0, succeeded = 0;
var bots = new List<Bot>();
var started = false;
var startBots = (EventHandler)delegate (object sender, EventArgs b)
{
if (started)
return;
started = true;
foreach (var dava in davas)
foreach (var year in years)
bots.Add(new Bot { Year = year, Dava = dava, Taraf = taraf, Url = url }.Run((bot, result) =>
{
Console.WriteLine($"{result}: {bot.Year}, {bot.Dava}");
succeeded += result ? 1 : 0;
if (++completed == years.Length * davas.Length)
Application.ExitThread();
}));
};
var forms = new Thread((ThreadStart) delegate {
Application.EnableVisualStyles();
Application.Idle += startBots;
Application.Run();
});
forms.SetApartmentState(ApartmentState.STA);
forms.Start();
forms.Join();
Console.WriteLine($"All bots finished, succeeded: {succeeded}, failed:{bots.Count - succeeded}.");
}
}
class Bot
{
public string Url, Dava, Taraf, Year;
public delegate void CompletionCallback(Bot sender, bool result);
WebBrowser browser;
CompletionCallback callback;
bool submitted;
public Bot Run(CompletionCallback callback)
{
this.callback = callback;
browser = new WebBrowser();
browser.ScriptErrorsSuppressed = true;
browser.DocumentCompleted += Browser_DocumentCompleted;
browser.Navigate(Url);
return this;
}
void Browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
if (browser.ReadyState == WebBrowserReadyState.Complete)
{
if (submitted)
{
callback(this, true);
return;
}
var ok = SetValue("DropDownList1", Dava);
ok &= SetValue("DropDownList3", Taraf);
ok &= SetValue("DropDownList4", Year);
if (ok)
ok &= Click("Button1");
if (!(submitted = ok))
callback(this, false);
}
}
bool SetValue(string id, string value, string name = "value")
{
var e = browser?.Document?.GetElementById(id);
e?.SetAttribute(name, value);
return e != null;
}
bool Click(string id)
{
var element = browser?.Document?.GetElementById(id);
element?.InvokeMember("click");
return element != null;
}
}
}

Related

How to Use a Boolean to Compare Action<int>

I have a method as below
public void RequestTPCost(Action<int> callback, Action<int> enemyCallback)
{
if (OnTPSelectionNeeded == null)
{
callback?.Invoke(0);
enemyCallback?.Invoke(0);
return;
}
if (TurnSystem.Instance.IsPlayerTurn())
{
//Player turn use shooting unit is selected and target is enemy
OnTPSelectionNeeded?.Invoke(this, new TPSelectionInfo()
{
callback = callback,
enemyCallback = enemyCallback,
TPAvailable = selectedUnit.GetTacticalPoints(),
enemyTPAvailable = targetUnit.GetTacticalPoints(),
});
}
else
{
OnTPSelectionNeeded?.Invoke(this, new TPSelectionInfo()
{
callback = callback,
enemyCallback = enemyCallback,
TPAvailable = targetUnit.GetTacticalPoints(),
enemyTPAvailable = selectedUnit.GetTacticalPoints()
});
}
}
and I use it elsewhere like this
private void UnitActionSystem_OnTPSelectionNeeded(object sender, UnitActionSystem.TPSelectionInfo e)
{
maxTP = e.TPAvailable;
maxEnemyTP = e.enemyTPAvailable;
callback = e.callback;
enemyCallback = e.enemyCallback;
TPAmount = 1;
enemyTPAmount = 0;
}
public void TPConfirm()
{
callback?.Invoke(TPAmount);
UnitActionSystem.Instance.GetSelectedUnit().
SpendTacticalPoints(TPAmount);
enemyTPAmount = Random.Range(0, (maxEnemyTP + 1));
enemyCallback?.Invoke(enemyTPAmount);
UnitActionSystem.Instance.GetTargetUnit().
SpendTacticalPoints(enemyTPAmount);
}
private void NextState()
{
switch (state)
{
case State.Aiming:
state = State.Choosing;
stateTimer = 0.1f;
UnitActionSystem.Instance.RequestTPCost(ConfirmTPCost,
ConfirmEnemyTPCost);
break;
case State.Choosing:
state = State.Shooting;
float shootingStateTime = 0.1f; //Not a frame
stateTimer = shootingStateTime;
break;
case State.Shooting:
state = State.Cooloff;
float coolOffStateTime = 0.5f;
stateTimer = coolOffStateTime;
Debug.Log("Shooting");
break;
case State.Cooloff:
ActionComplete();
break;
}
}
private void ConfirmTPCost(int value)
{
Debug.Log($"TP = {value}");
NextState();
}
private void ConfirmEnemyTPCost(int value)
{
Debug.Log($"EnemyTP = {value}");
//NextState();
}
Now I want to check if ConfirmTPCost < ConfirmEnemyTPCost but am not sure how to simply do that.
I have a roundabout way using events and more UnityActionSystem functions to call the instance and work through that but it seems very clunky and prone to breaking. Is there a better way to get these values and check which is more?
var cost = 0;
var enemyCost = 0;
UnitActionSystem.Instance.RequestTPCost(i => cost = i, i => enemyCost = i);
Debug.Log($"TP = {cost}");
Debug.Log($"EnemyTP = {enemyCost}");
if (cost < enemyCost)
{
//Do your stuff here
}
As said it sounds like there is actually some asynchronous parts involved.
So you either will want to properly implement async and await patterns and wait for both values to be available.
Or alternatively you could somewhat fake that behavior using e.g. nullables and a local function ike
int? cost = null;
int? enemyCost = null;
UnitActionSystem.Instance.RequestTPCost(i =>
{
cost = i;
if(enemyCost != null)
{
Validate();
}
}, i =>
{
enemyCost = i;
if(cost != null)
{
Validate();
}
});
// Can be a local function
// otherwise pass in the two int values via parameters
void Validate()
{
Debug.Log($"TP = {cost.Value}");
Debug.Log($"EnemyTP = {enemyCost.Value}");
if (cost.Value < enemyCost.Value)
{
//Do your stuff here
}
}
Idea here: No matter which of both callbacks fires first, it is ignored and only for the second one both values are already available and the actual handler method can be invoked

C# Called method is ignored with no error

I am developing a C# Windows Form program in which there are a number of called Methods (what in VB I called Subs). Excuse me if I'm using the wrong terms, new to C#. One of these methods is being ignored, CreateFolder(). When I set break points to debug and step through, the program gets to the called method and goes right through it like it like it was a blank line, no errors warning just goes through. The program is a bit large so I'm including what I hope is helpful. Sorry if too much. The method (sub) not being executed is CreateFolder(). Again, the debugger will stop on this line[CreateFolder()] in the ProcessEmail() routine but just goes on without going to the called code. Thoughts?
public partial class FrmProcessNew : Form
{
...bunch of global vaiables
private void FrmProcessNew_Load(object sender, EventArgs e)
{
chkBoxTesting.Checked = Convert.ToBoolean(ConfigurationManager.AppSettings["testing"]); //check if test to ignore bad credit cards
}
private void btnStart_Click(object sender, EventArgs e)
{
ProcessEmail(); //check for, process and then move new Outlook emails
}
public void ScrollBox(string box) //try to get scroll box right
{
if (box == "Text")
{
textBox1.SelectionStart = textBox1.Text.Length;
textBox1.ScrollToCaret();
}
if (box == "List")
{ listBox1.TopIndex = listBox1.Items.Count - 1; }
}
public static int GetLineNumber(COMException ex) //Get the line number if an error occurs - from stack overflow
{
var lineNumber = 0;
const string lineSearch = ":line ";
var index = ex.StackTrace.LastIndexOf(lineSearch);
if (index != -1)
{
var lineNumberText = ex.StackTrace.Substring(index + lineSearch.Length);
if (int.TryParse(lineNumberText, out lineNumber))
{
}
}
return lineNumber;
}
private void ProcessEmail() // Process the new email
{
while (inboxFolder.Items.Count == 0) //check for any email in Inbox
{
textBox1.Text = textBox1.Text + ("Inbox is empty - Listening for New emails!\r\n");
ScrollBox("Text");
textBox1.Update();
Delay(); //wait 5 seconds
}
CreateFolder(); //Create new destination folder to move processed emails to - NOT FORWARDING TO THIS SUB!!
try
{
foreach (Microsoft.Office.Interop.Outlook.MailItem mailItem in inboxFolder.Items)
{
....processing stuff
}
catch (System.Runtime.InteropServices.COMException ex)
{
....capture error
}
finally
{
....reset stuff
}
}
public void CreateFolder() //Create Outlook Folders to store processed emails = NEVER GETS TO THIS CODE!!!
{
DateTime fldDate = DateTime.Now;
if (fldDate.DayOfWeek != DayOfWeek.Friday) //must process with a Friday date, set date to next Friday
{
fldDate = NextFriday(fldDate);
}
String myYear = fldDate.ToString("yyyy");
string myMonth = fldDate.ToString("MM");
string myDay = fldDate.ToString("dd"); //day of the month
string fileDate = (myYear.ToString()) + "-" + (myMonth.ToString()) + "-" + myDay; // build the Date for Destination "file";
try
{olDestinationFolder = ns.Folders[mailBoxName].Folders[("done")].Folders[(fileDate)]; }
catch
{ olDestinationFolder = ns.Folders[mailBoxName].Folders[("done")].Folders.Add(fileDate); }
try
{ olBadCreditFolder = ns.Folders[mailBoxName].Folders[("done")].Folders[(fileDate)].Folders[("BadCredit")]; }
catch
{ olBadCreditFolder = ns.Folders[mailBoxName].Folders[("done")].Folders[(fileDate)].Folders.Add("BadCredit"); }
try
{ olNonOrderEmails = ns.Folders[mailBoxName].Folders[("done")].Folders[(fileDate)].Folders[("Non-OrderEmails")]; }
catch
{ olNonOrderEmails = ns.Folders[mailBoxName].Folders[("done")].Folders[(fileDate)].Folders.Add("Non-OrderEmails"); }
}
private void Delay() //If no emails then want 5 seconds and try again
{
textBox1.Text = textBox1.Text + ("Inbox is empty - Listening for New emails!\r\n");
ScrollBox("Text");
textBox1.Update();
// await Task.Delay(5000); //Wait 5 seconds for new email
var t = Task.Run(async delegate
{
await Task.Delay(TimeSpan.FromSeconds(5.5));
ScrollBox("Text");
return 42;
});
}
static DateTime NextFriday(DateTime fldDate) //Find the next FRIDAY date.
{
do
{
fldDate = fldDate.AddDays(1);
}
while (fldDate.DayOfWeek != DayOfWeek.Friday);
return fldDate;
}
}
}

Is that logic to reset the Lists and call Init() over again if there was an exception with the download/s?

And how can i report to the user that there was a problem and that it's trying over again ? And should i just do it like i'm doing it now reseting everything and calling Init() again or should i use some timer and wait some seconds before trying again ?
In the class i did:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Net;
using System.Xml;
using HtmlAgilityPack;
using System.ComponentModel;
namespace TestingDownloads
{
class ExtractImages
{
static WebClient client;
static string htmltoextract;
public static List<string> countriescodes = new List<string>();
public static List<string> countriesnames = new List<string>();
public static List<string> DatesAndTimes = new List<string>();
public static List<string> imagesUrls = new List<string>();
static string firstUrlPart = "http://www.sat24.com/image2.ashx?region=";
static string secondUrlPart = "&time=";
static string thirdUrlPart = "&ir=";
public class ProgressEventArgs : EventArgs
{
public int Percentage { get; set; }
public string StateText { get; set; }
}
public event EventHandler<ProgressEventArgs> ProgressChanged;
public void Init()
{
object obj = null;
int index = 0;
ExtractCountires();
foreach (string cc in countriescodes)
{
// raise event here
ProgressChanged?.Invoke(obj,new ProgressEventArgs{ Percentage = 100 * index / countriescodes.Count, StateText = cc });
ExtractDateAndTime("http://www.sat24.com/image2.ashx?region=" + cc);
index +=1;
}
ImagesLinks();
}
public void ExtractCountires()
{
try
{
htmltoextract = "http://sat24.com/en/?ir=true";//"http://sat24.com/en/";// + regions;
client = new WebClient();
client.DownloadFile(htmltoextract, #"c:\temp\sat24.html");
client.Dispose();
string tag1 = "<li><a href=\"/en/";
string tag2 = "</a></li>";
string s = System.IO.File.ReadAllText(#"c:\temp\sat24.html");
s = s.Substring(s.IndexOf(tag1));
s = s.Substring(0, s.LastIndexOf(tag2) + tag2.ToCharArray().Length);
s = s.Replace("\r", "").Replace("\n", "").Replace(" ", "");
string[] parts = s.Split(new string[] { tag1, tag2 }, StringSplitOptions.RemoveEmptyEntries);
string tag3 = "<li><ahref=\"/en/";
for (int i = 0; i < parts.Length; i++)
{
if (i == 17)
{
//break;
}
string l = "";
if (parts[i].Contains(tag3))
l = parts[i].Replace(tag3, "");
string z1 = l.Substring(0, l.IndexOf('"'));
if (z1.Contains("</ul></li><liclass="))
{
z1 = z1.Replace("</ul></li><liclass=", "af");
}
countriescodes.Add(z1);
countriescodes.GroupBy(n => n).Any(c => c.Count() > 1);
string z2 = parts[i].Substring(parts[i].LastIndexOf('>') + 1);
if (z2.Contains("&"))
{
z2 = z2.Replace("&", " & ");
}
countriesnames.Add(z2);
countriesnames.GroupBy(n => n).Any(c => c.Count() > 1);
}
}
catch (Exception e)
{
if (countriescodes.Count == 0)
{
countriescodes = new List<string>();
countriesnames = new List<string>();
DatesAndTimes = new List<string>();
imagesUrls = new List<string>();
Init();
}
}
}
public void ExtractDateAndTime(string baseAddress)
{
try
{
var wc = new WebClient();
wc.BaseAddress = baseAddress;
HtmlDocument doc = new HtmlDocument();
var temp = wc.DownloadData("/en");
doc.Load(new MemoryStream(temp));
var secTokenScript = doc.DocumentNode.Descendants()
.Where(e =>
String.Compare(e.Name, "script", true) == 0 &&
String.Compare(e.ParentNode.Name, "div", true) == 0 &&
e.InnerText.Length > 0 &&
e.InnerText.Trim().StartsWith("var region")
).FirstOrDefault().InnerText;
var securityToken = secTokenScript;
securityToken = securityToken.Substring(0, securityToken.IndexOf("arrayImageTimes.push"));
securityToken = secTokenScript.Substring(securityToken.Length).Replace("arrayImageTimes.push('", "").Replace("')", "");
var dates = securityToken.Trim().Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries);
var scriptDates = dates.Select(x => new ScriptDate { DateString = x });
foreach (var date in scriptDates)
{
DatesAndTimes.Add(date.DateString);
}
}
catch
{
countriescodes = new List<string>();
countriesnames = new List<string>();
DatesAndTimes = new List<string>();
imagesUrls = new List<string>();
this.Init();
}
}
public class ScriptDate
{
public string DateString { get; set; }
public int Year
{
get
{
return Convert.ToInt32(this.DateString.Substring(0, 4));
}
}
public int Month
{
get
{
return Convert.ToInt32(this.DateString.Substring(4, 2));
}
}
public int Day
{
get
{
return Convert.ToInt32(this.DateString.Substring(6, 2));
}
}
public int Hours
{
get
{
return Convert.ToInt32(this.DateString.Substring(8, 2));
}
}
public int Minutes
{
get
{
return Convert.ToInt32(this.DateString.Substring(10, 2));
}
}
}
public void ImagesLinks()
{
int cnt = 0;
foreach (string countryCode in countriescodes)
{
cnt++;
for (; cnt < DatesAndTimes.Count(); cnt++)
{
string imageUrl = firstUrlPart + countryCode + secondUrlPart + DatesAndTimes[cnt] + thirdUrlPart + "true";
imagesUrls.Add(imageUrl);
if (cnt % 10 == 0) break;
}
}
}
}
}
In both cases where i'm using try and catch if it's getting to the catch i'm trying over again the whole operation by reseting the Lists and calling Init() again.
Then in form1
ExtractImages ei = new ExtractImages();
public Form1()
{
InitializeComponent();
ProgressBar1.Minimum = 0;
ProgressBar1.Maximum = 100;
backgroundWorker1.RunWorkerAsync();
}
events of backgroundworker
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
if (backgroundWorker1.CancellationPending == true)
{
e.Cancel = true;
return; // this will fall to the finally and close everything
}
else
{
ei.ProgressChanged += (senders, eee) => backgroundWorker1.ReportProgress(eee.Percentage, eee.StateText);
ei.Init();
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
ProgressBar1.Value = e.ProgressPercentage;
label7.Text = e.UserState.ToString();
label8.Text = e.ProgressPercentage + "%";
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error == null)
{
ProgressBar1.Value = 100;
}
else
{
}
}
Another thing not sure if it's a problem. When there is no a problem with the class i see in form1 in label7 all the codes running from the first to the last.
But the progressBar1.Value and label8 both are getting only to 97% so i need in the completed event to add the progressBar1.Value = 100; is that fine or there is a problem with the reporting calculation in the class ?
For the 1st question:
Better catch the exception at client side and display the error msg, 'cause you probably need to control your program behavior accrodingly when sth went wrong.
Consider this: if the DOM struct of the page changed, according to you code, your program probably throw expections, with each exception catching, client.DownloadFile is excuted for one time, if this happens, u will need to know what go wrong, and if u donot change ur code behavior, such hi-freq client.DownloadFile excution will cause the firewall of the website block ur ip for a while.
Add a timer at client side is a good idea i think.
For the 2nd one:
did u define how to handle RunWorkerCompleted event?

WaitForDrawerClose blocks printing

I'm writing an application that uses Pos for .Net, and I'm noticing that if you call WaitForDrawerClose, then you won't be able to print receipts until it has returned.
This is not desirable behavior. Is there another way to wait for the cash drawer to close without blocking the printer?
I've looked into the OnDrawerStateChanged Event, but that is a protected member of CashDrawerBase, and I'm not entirely sure how to access it.
Here is my SSCCE:
static void Main(string[] args)
{
var posExplorer = new PosExplorer();
var waitTask = WaitForCloseAsync(posExplorer);
System.Threading.Thread.Sleep(500);
PrintText(posExplorer);
waitTask.Wait();
}
public static Task WaitForCloseAsync(PosExplorer posExplorer)
{
var result = Task.Factory.StartNew(() =>
{
Console.WriteLine("waiting");
var cashDrawer = GetCashDrawer(posExplorer);
cashDrawer.Open();
cashDrawer.Claim(1000);
cashDrawer.DeviceEnabled = true;
cashDrawer.WaitForDrawerClose(10000, 4000, 500, 5000);
cashDrawer.Release();
cashDrawer.Close();
Console.WriteLine("waited");
});
return result;
}
public static void PrintText(PosExplorer posExplorer)
{
Console.WriteLine("printing");
var printer = GetPosPrinter(posExplorer);
printer.Open();
printer.Claim(1000);
printer.DeviceEnabled = true;
var text = "abc\x1B|1lF";
printer.PrintNormal(PrinterStation.Receipt, text);
printer.Release();
printer.Close();
Console.WriteLine("printed");
}
public static CashDrawer GetCashDrawer(PosExplorer posExplorer)
{
var deviceInfo = posExplorer.GetDevices(DeviceCompatibilities.Opos)
.Cast<DeviceInfo>()
.Where(d => d.Type == "CashDrawer")
.ToList();
var device = deviceInfo.FirstOrDefault(d => d.Compatibility == DeviceCompatibilities.Opos);
if (device == null)
{
return null;
}
else
return (CashDrawer)posExplorer.CreateInstance(device);
}
private static PosPrinter GetPosPrinter(PosExplorer posExplorer)
{
var deviceInfo = posExplorer.GetDevices(DeviceCompatibilities.Opos)
.Cast<DeviceInfo>()
.Where(d => d.Type == "PosPrinter")
.ToList();
var device = deviceInfo.FirstOrDefault(d => d.Compatibility == DeviceCompatibilities.Opos);
if (device == null)
{
return null;
}
else
{
return (PosPrinter)posExplorer.CreateInstance(device);
}
}
so, what I did was essentially this: Instead of using (WaitForClose), I just poll it like this:
for (var i = 0; i < 15; i++)
{
cashDrawer = GetCashDrawer(posExplorer);
cashDrawer.Open();
cashDrawer.Claim(1000);
cashDrawer.DeviceEnabled = true;
if (!cashDrawer.DrawerOpened)
{
Console.WriteLine("waited");
return;
}
cashDrawer.Release();
cashDrawer.Close();
System.Threading.Thread.Sleep(1500);
}
Console.WriteLine("timed out");
It's not ideal, but it doesn't lock the printer up either, so It'll have to to for now.

Why don't execute task in ContinueWith?

I have async methods that returns an objects
public static IEnumerable<Users.User> GetUsers(IEnumerable<string> uids, Field fields)
{
Task<ResponseApi<Users.User>>[] tasks;
tasks = uids.Select(uid =>
{
var parameters = new NameValueCollection
{
{"uids", uid},
{"fields", FieldsUtils.ConvertFieldsToString(fields)}
};
return GetUserResponseApi(parameters);
}).ToArray();
Task.WaitAll(tasks);
foreach(Task<ResponseApi<Users.User>> task in tasks)
{
if(task.Result.Response != null)
{
yield return task.Result.Response;
}
}
}
And I want to update UI in other method,UI maybe don't update because method GetUserResponseApi don't return any value.
public static Task<ResponseApi<Users.User>> GetUserResponseApi(NameValueCollection parameters)
{
return CallMethodApi("users.get", parameters, CallType.HTTPS)
.ContinueWith(
r =>
{
//don't execute
var responseApi = new ResponseApi<Users.User>();
responseApi.Response = JsonConvert.DeserializeObject<Users.User>(r.Result["response"][0].ToString());
return responseApi;
});
}
private void BtnGetUsersClick(object sender, EventArgs e)
{
var random = new Random();
int max = 175028595;
var uids = new List<string>();
for(int i = 1; i <= 20; i++)
{
uids.Add((random.Next(max) + 1).ToString());
}
Task.Factory.StartNew(() =>
{
var users = VkontakteApi.GetUsers(uids, Field.Online);
foreach(var user in users)
{
richTextBox1.Text += string.Format("ID:{0} online:{1}", user.uid,
user.online);
}
}, CancellationToken.None, TaskCreationOptions.None, _uiContext);
}
How to resolve problem with ContinueWith in GetUserResponseApi?
UPDATE:
I think that problem in method GetUserResponseApi because block ContinueWith doesn't execute.
Use Application.Current.Dispatcher to dispatch calls to UI thread whenever you access UI objects.
Application.Current.Dispatcher.Invoke(() => {
try
{
richTextBox1.Text += string.Format("ID:{0} online:{1}", user.uid, user.online);
}
catch
{
//handle
}
), DispatcherPriority.Background);
Try using TaskScheduler.FromCurrentSynchronizationContext() method:
Task.Factory.StartNew(() =>
{
var users = VkontakteApi.GetUsers(uids, Field.Online);
foreach(var user in users)
{
richTextBox1.Text += string.Format("ID:{0} online:{1}", user.uid,
user.online);
}
}, CancellationToken.None,
TaskScheduler.FromCurrentSynchronizationContext());

Categories