WP7 WebClient DownloadStringAsync and Map - windows-phone-7

I'm using WebClient object in to poll some data from server.
It's working good and it's updating text block fine. Till I don't use map on same Page. When I add a map, only one request get completed and data is retrieved only once.
This is the code for getting messages:
public MessagesPage()
{
InitializeComponent();
new System.Threading.Timer(messagePolling, null, 0, 5000); // every 5 seconds
}
void messagePolling(object state)
{
getMessages(Const.GET_MESSAGES_URL + uuid);
}
private void getMessages(string uri)
{
WebClient webClient = new WebClient();
webClient.DownloadStringAsync(new Uri(uri));
webClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(messagesResponseCompleted);
}
void messagesResponseCompleted(object sender, DownloadStringCompletedEventArgs e)
{
lock (this)
{
try
{
string s = e.Result;
if (s.Length > 0)
{
List<Message> messagesResult = JSONHelper.Deserialize<List<Message>>(s);
foreach (Message m in messagesResult)
{
tbMessages.Text += m.message + "\n";
}
}
else
{
tbMessages.Text += "No new messages #: " + System.DateTime.Now + "\n";
}
}
catch (System.Net.WebException we)
{
MessageBox.Show(we.Message);
}
}
}
Anyone?

The WebClient response is processed on the UI thread - so you don't need the lock that you have in your event handler.
For your particular problem - is this just occurring in the emulator? I've seen quite a few timer issues with the emulator - but never anything similar on the real phone.
As an aside, I believe in general it's better to use HttpWebRequest rather than WebClient - see the explanation here of webclient using the UI thread Silverlight Background Thread using WebClient - for your particular code I don't think this will be a problem.

If using
System.Windows.Threading.DispatcherTimer myDispatcherTimer = new System.Windows.Threading.DispatcherTimer();
myDispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 5000);
myDispatcherTimer.Tick += new EventHandler(messagePolling);
myDispatcherTimer.Start();
instead of
new System.Threading.Timer(messagePolling, null, 0, 5000); // every 5 seconds
is working fine =)

Related

cancel taskcompletionsource which calls a void method from an API with timeout xamarin forms

I have this non-async Task> which just requests:
TaskCompletionSource<ObservableCollection<ItemDto>> tcs = new TaskCompletionSource<ObservableCollection<ItemDto>>();
ObservableCollection<ItemDto> results = new ObservableCollection<ItemDto>();
try
{
BasicHttpBinding binding = new BasicHttpBinding();
binding.OpenTimeout = new TimeSpan(0, 0, 30);
binding.CloseTimeout = new TimeSpan(0, 0, 30);
binding.SendTimeout = new TimeSpan(0, 0, 30);
binding.ReceiveTimeout = new TimeSpan(0, 0, 30);
MobileClient clientMobile = new MobileClient(binding, new EndpointAddress(_endpointUrl));
clientMobile.FindItemsCompleted += (object sender, FindItemsCompletedEventArgs e) =>
{
if (e.Error != null)
{
_error = e.Error.Message;
tcs.TrySetException(e.Error);
}
else if (e.Cancelled)
{
_error = "Cancelled";
tcs.TrySetCanceled();
}
if (string.IsNullOrWhiteSpace(_error) && e.Result.Count() > 0)
{
results = SetItemList(e.Result);
tcs.TrySetResult(results);
}
clientMobile.CloseAsync();
};
clientMobile.FindItemsAsync(SetSearchParam(searchString, 100));
}
catch (Exception)
{
results = new ObservableCollection<ItemDto>();
tcs.TrySetResult(results);
}
return tcs.Task;
Yes, I know, nothing special, it's just that this
clientMobile.FindItemsAsync(SetSearchParam(searchString, 100))
is a call to a void method, which in turn calls another void method which sets a few params in order to then call an async method which itself calls an async method which performs an async operation to return the list of Items.
Problem is, I have no control whatsoever of anything beyond the scope of this Task above, because everything I just explained is part of an API, in which I'm not allowed to touch, and of which I can make no comment, regarding the way it works, as the policy is for me to adapt my work to it... -_-
So, in order to do that, I must kill this call to the FindItemsAsync, as soon as a total of 1 minute has passed... I tried setting the above timespans to a minute each (first worked, now some changes have been made and no go), I tried reducing to half the time, and no go...
Here's the code which is calling this Task:
public void LoadItemList(string searchString)
{
_itemList = new ObservableCollection<ItemDto>();
// Calls the Task LoadList.
var result = LoadList(searchString).Result;
if (result != null && result != new ObservableCollection<ItemDto>())
{
_itemList = result;
}
else
{
_isTaskCompleted = false;
}
_isListEmpty = (_itemList != new ObservableCollection<ItemDto>()) ? false : true;
}
and below is the code which calls the caller of this task... (what a mess -_-):
void Init(string searchString = "")
{
Device.BeginInvokeOnMainThread(async () =>
{
if (!LoadingStackLayout.IsVisible && !LoadingActivityIndicator.IsRunning)
{
ToggleDisplayLoadingListView(true);
}
await Task.Run(() => _listVM.LoadItemList(searchString));
ToggleDisplayLoadingListView();
if (!string.IsNullOrWhiteSpace(_listVM.Error))
{
await DisplayAlert("Error", _listVM.Error, "OK");
}
else if (_listVM.AdList != null && !_listVM.IsListEmpty)
{
ItemListView.IsVisible = true;
ItemListView.ItemsSource = _listVM.ItemList;
}
else if (!_listVM.IsTaskCompleted || _listVM.IsListEmpty)
{
await DisplayAlert("", "At the moment it is not possible to show results for your search.", "OK");
}
else if (_listVM.ItemList.Count == 0)
{
await DisplayAlert("", "At the moment there are no results for your search.", "OK");
}
});
}
At the moment I'm trying to implement the MVVM arch...
Really, thank you so much for your help on this matter, it's been great, and I really apologize for all this inconvenience...
EDIT
Sorry because I didn't explain my objective clearly; it is: I need to fetch a list of Items accessing an API that just communicates with me via a void method FindItemsAsync. I have 60 seconds to fetch all those items. If something goes wrong, or if timeout, I have to cancel the process and inform the user something went wrong.
That doesn't happen. It never cancels. Either gets me the Items, or stays loading forever, dispite my hardest tries... I'm new to tasks and most of this stuff, hence my constant issues...
You can call CloseAsync when your cancellation token expires.
//Creates an object which cancels itself after 5000 ms
var cancel = new CancellationTokenSource(5000);
//Give "cancel.Token" as a submethod parameter
public void SomeMethod(CancellationToken cancelToken)
{
...
//Then use the CancellationToken to force close the connection once you created it
cancelToken.Register(()=> clientMobile.CloseAsync());
}
It will cut down the connection.

DownloadStringCompleted event does not fire

I am developing a windows phone application. The webclient does not fire as I expected. The related code is as below:
public PArticle(PocketListItem aPli)
{
this.pli = aPli;
using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
{
if (!isf.FileExists(aPli.ID + ".json"))
{
WebClient client = new WebClient();
client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(client_DownloadStringCompleted);
client.DownloadStringAsync(new Uri(pli.Url));
}
else
{
string json = RetrieveDataFromLocalStorage(aPli.ID + ".json");
PocketArticle pa = JsonConvert.DeserializeObject<PocketArticle>(json);
this.text = pa.text;
}
}
}
void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
var readability = Readability.Create(e.Result);
this.text = readability.Content;
}
I know it's a synchronous/asynchronous problem. But I have no idea about how to handle it.
Thanks in advance.
I have tested the WebClient part of your code with 2 different URLs http://riktamtech.com and http://google.com. In both cases, the DownloadStringCompleted event is raised. I observed it by placing a break point.
So I suggest you to test again with break points.

BackKey not working when a Thread is running. Why?

PageA Navigated to PageB
There is a thread is running for HttpWebRequest.
Back Key is invalid when the Thread is running.
PageB Code:
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
Debug.WriteLine("OnNavigatedTo");
//base.OnNavigatedTo(e);
DoWork();
}
void DoWork()
{
t = new Thread(new ThreadStart(() =>
{
request = WebRequest.Create("http://www.google.com") as HttpWebRequest;
request.BeginGetResponse(new AsyncCallback(AsyncBack), request);
}));
t.IsBackground = true;
t.Start();
}
void AsyncBack(IAsyncResult ias)
{
HttpWebRequest req = (HttpWebRequest)ias.AsyncState;
using (HttpWebResponse res = req.EndGetResponse(ias) as HttpWebResponse)
{
this.Dispatcher.BeginInvoke(() =>
{
this.PageTitle.Text = res.ContentLength.ToString();
long length = res.ContentLength;
for (long i = 0; i < length; i++)
{
//here imitate a long time for working
Debug.WriteLine(i);
if (i == length)
{
break;
}
}
Debug.WriteLine(res.ContentLength);
});
}
}
the Back Key is invalid until Method AsyncBack() is done.
'Back Key is invalid' Is that the app is not back to PageA when you touch the Back Key until Method AsyncBack() Done.
Why? Help me?
Why the bloody hell are you wrapping a async request in a custom thread? That doesn't even remotely make sense.
Then again, your question doesn't make much sense either, but most likely the error is related to the request is attempting to invoke a operation, via. the dispatcher, on the wrong page.
In your code you block UI thread for a long time, so you can't navigate page back, because it happens also on UI thread, put into Dispatcher only code that can't be executed not on UI.
void DoWork()
{
HttpWebRequest request = WebRequest.Create("http://www.googl.com") as HttpWebRequest;
request.BeginGetResponse(new AsyncCallback(AsyncBack), request);
}
void AsyncBack(IAsyncResult ias)
{
HttpWebRequest req = (HttpWebRequest)ias.AsyncState;
using (HttpWebResponse res = req.EndGetResponse(ias) as HttpWebResponse)
{
this.Dispatcher.BeginInvoke(() =>
{
this.PageTitle.Text = res.ContentLength.ToString();
});
long length = res.ContentLength;
for (long i = 0; i < length; i++)
{
long i_ = i;
//here imitate a long time for working
Thread.Sleep(10);
this.Dispatcher.BeginInvoke(() =>
{
this.PageTitle.Text = i_.ToString();
});
if (i == length)
{
break;
}
}
Debug.WriteLine(res.ContentLength);
}
}

Windows Phone app throws exception (quit automatically) when running under 3G, but fine with WIFI. Very weird

I have tried hundreds of times to find errors for this piece of codes.
It only works through WIFI, but When I switch off WIFI on my phone, and run the app again, this app just shut down automatically, which means it thrown an exception.
The app is simple, I used WebClint() to download HTML source and parsed it with HTML Agility Pack, then added them to a list, foreach the list to creat each news object.
I have tried catch the exception stacktrace and bind it to a texblock, It said some of ArgumentOutOfRange exception and Genericlist(int32 index)???
I have no idea about it, It was fine in wifi, but not in 3G network. Can anyone help?
public partial class MainPage : PhoneApplicationPage
{
string srcHTML;
HtmlNode UrlNode;
ObservableCollection<News> newsList = new ObservableCollection<News>();
List<HtmlNode> headlines;
HtmlDocument hd;
News n;
// Constructor
public MainPage()
{
InitializeComponent();
}
private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
{
WebClient webClenet = new WebClient();
webClenet.Encoding = new HtmlAgilityPack.Gb2312Encoding();
webClenet.DownloadStringAsync(new Uri("http://www.6park.com/news/multi1.shtml", UriKind.RelativeOrAbsolute));
webClenet.DownloadStringCompleted += new DownloadStringCompletedEventHandler(webClenet_DownloadStringCompleted);
}
private void webClenet_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
srcHTML = e.Result;
GetHeadlinePage(srcHTML);
}
private void GetHeadlinePage(string srcHTML)
{
hd = new HtmlDocument();
hd.LoadHtml(srcHTML);
try
{
UrlNode = hd.DocumentNode.ChildNodes[1].ChildNodes[3].ChildNodes[8].ChildNodes["tr"].ChildNodes["td"].ChildNodes["ul"];
headlines = UrlNode.Descendants("a").ToList();
foreach (var headline in headlines)
{
if (headline.Attributes["href"].Value.Contains("6park"))
{
n = new News();
n.NewsTitle = headline.InnerText;
n.NewsUrl = headline.Attributes["href"].Value;
n.NewsDetails = headline.NextSibling.InnerText.Replace("- ", "新闻来源:") + headline.NextSibling.NextSibling.InnerText + headline.NextSibling.NextSibling.NextSibling.InnerText;
newsList.Add(n);
}
}
}
catch (Exception ex)
{
//NewsSource.Text = ex.StackTrace + "\n" + ex.Message;
}
NewslistBox.ItemsSource = newsList;
//NewsHeadlineWebBrowser.NavigateToString(ConvertExtendedASCII(headNews));
}
}
I'd debug the value passed to GetHeadlinePage().
I'd suspect that the response is different based on the network or the request is timing out or you're getting some other error.
I'd assume that the call to LoadHtml() is failing as this isn't inside any exception handling/trapping and you've not validating the value passed to it.

UI not updating in async web request callback

I'm using this to make a web request and download some data:
public partial class MainPage : PhoneApplicationPage
{
public MainPage()
{
InitializeComponent();
var client = new WebClient();
client.DownloadStringCompleted += (s, e) => {
textBlock1.Text = e.Result;
};
client.DownloadStringAsync(new Uri("http://example.com"));
}
}
The text of textBlock1 never changes even though e.Result has the correct data. How do I update that from the callback?
Edit: If I add MessageBox.Show(e.Result); in the callback along with the textBlock1.Text assignment, both the messsage box and the text box show the correct data.
Edit Again: If I add a TextBox and set it's text right after the line textBlock1.Text line, they both show the correct text.
I think, it's a bug.
I also ran into some problems with updating the UI from different dispatchers. What I finally did was use the TextBlock's (or other UI Element) own dispatcher and that worked for me. I think the phone framework may be using different dispatchers between the app and UI Elements. Notice the change from dispatcher.BeginInvoke to textbox1.Dispatcher...
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
var dispatcher = Deployment.Current.Dispatcher;
var client = new WebClient();
client.DownloadStringCompleted += (s, e) =>
{
var result = e.Result;
textBlock1.Dispatcher.BeginInvoke(
()=> textBlock1.Text = result
);
};
client.DownloadStringAsync(new Uri("http://example.com"));
}
From browsing through the WP7 forums, a bunch of people were reporting that this was related to a video card driver issue. I've updated my ATI Radeon HD 3400 drivers to the latest version and it appears to work now.
client.DownloadStringAsync is expecting a Uri like this:
client.DownloadStringAsync(new Uri("http://example.com"));
also, shouldn't you update your TextBlock through a Dispatcher.BeginInvoke like this:
client.DownloadStringCompleted += (s, e) =>
{
if (null == e.Error)
Dispatcher.BeginInvoke(() => UpdateStatus(e.Result));
else
Dispatcher.BeginInvoke(() => UpdateStatus("Operation failed: " + e.Error.Message));
};
public partial class MainPage : PhoneApplicationPage
{
public MainPage()
{
InitializeComponent();
Loaded += MainPage_Loaded;
}
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
var dispatcher = Deployment.Current.Dispatcher;
var client = new WebClient();
client.DownloadStringCompleted += (s, e) =>
{
var result = e.Result;
dispatcher.BeginInvoke(
()=> textBlock1.Text = result
);
};
client.DownloadStringAsync(new Uri("http://example.com"));
}
}
I want to comment but can't yet. Yes, I have a very similar issue. In my case it's my viewmodel that is updating a DownloadStatus property, then when the download is completed I do some more work and continue updating this property.
The view stops updating once the ViewModel code hits the OpenReadCompleted method. I've stepped carefully through the code. PropertyChanged fires, and the view even comes back and retrieves the new property value, but never shows the change.
I was sure it was a bug, but then I created a brand new project to reproduce the issue, and it works fine!
Here's a snippet of my non-reproducing code. The UI textblock bound to "DownloadStatus" happily updates properly all the way through. But the same paradigm doesn't work in my main project. Infuriating!
public void BeginDownload(bool doWorkAfterDownload)
{
DownloadStatus = "Starting ...";
_doExtraWork = doWorkAfterDownload;
var webClient = new WebClient();
string auth = "Basic " + Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes("test:password"));
webClient.Headers["Authorization"] = auth;
webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(webClient_DownloadProgressChanged);
webClient.OpenReadCompleted += new OpenReadCompletedEventHandler(webClient_OpenReadCompleted);
webClient.OpenReadAsync(new Uri("http://www.ben.geek.nz/samsung1.jpg"));
}
void webClient_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
if (e.Error != null)
{
DownloadStatus = e.Error.Message;
return;
}
DownloadStatus = "Completed. Idle.";
if(_doExtraWork)
{
Thread t = new Thread(DoWork);
t.Start(e.Result);
}
}
void DoWork(object param)
{
InvokeDownloadCompleted(new EventArgs());
// just do some updating
for (int i = 1; i <= 10; i++)
{
DownloadStatus = string.Format("Doing work {0}/10", i);
Thread.Sleep(500);
}
DownloadStatus = "Completed extra work. Idle.";
InvokeExtraWorkCompleted(new EventArgs());
}

Resources