Windows Phone 7: reference page controls in thread-safe way - windows-phone-7

Here is what I am trying to do. In my WP7 app, I am loading a page that has two StackPanels. StackPanel1 is "Collapsed" and StackPanel2 is "Visible". On load of the page, I am kicking off an HttpWebRequest and then processing the BeginGetResponse asynchronously. At this point I just want to swap the Visibility of the two StackPanels. However, since the BeginGetResponse is run Asynchronously, I am no longer in the UI thread and cannot manipulate these StackPanel controls. If I try to reference them, of course, I get "An object reference is required for the non-static field, method, or property 'blah.StackPanel1'"
This all makes sense and I get why.
Here are some things I have tried:
Delegates, but any way I sliced it, I needed a static reference to my controls. fail.
I tried to create a static reference to my page class and then use that to reference my controls in the BeginGetResponse. This compiled, but I got a UnauthorizedAccessException 'invalid cross-thread access.' at run-time when I tried to reference the controls.
Searching and searching and searching.
Using Deployment.Current.Dispatcher.BeginInvoke to run on the UI thread.
How can I statically reference these controls?
OR is there a better way to do what I'm doing?
EDIT:
Here is my HttpWebRequest
if (NetworkInterface.GetIsNetworkAvailable())
{
HttpWebRequest httpWebRequest = HttpWebRequest.CreateHttp("http://urlThatWorks.com");
httpWebRequest.Method = "GET";
httpWebRequest.BeginGetResponse((asyncresult) =>
//do processing of my return here
//then here is the problem
StackPanel1.Visibility = System.Windows.Visibility.Visible;
StackPanel2.Visibility = System.Windows.Visibility.Collapsed;
}, httpWebRequest);
}
ANOTHER EDIT:
And here is how I tried with Deployment.Current.Dispatcher.BeginInvoke
httpWebRequest.BeginGetResponse((asyncresult) =>
//do processing of my return here
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
StackPanel1.Visibility = System.Windows.Visibility.Visible;
StackPanel2.Visibility = System.Windows.Visibility.Collapsed;
});
}, httpWebRequest);

You don't really want a static reference, you want a thread-safe way of accessing them.
You can execute it on the UI thread by:
Deployment.Current.Dispatcher.BeginInvoke(()=> SomeMethod);
or
Deployment.Current.Dispatcher.BeginInvoke(()=> { // code });

Related

MessagingCentre Does it pass object by value or by reference?

With reference to .Net Maui: How to read/write (get/set) a global object from any content page (MVVM)
I now have a need to pass an object that is rather a large (>500Mb), it is an OpenMap extract, a RouterDb object (Itinero http://docs.itinero.tech/index.html)
Though I am trying to use MVVM some packages (Mapsui) use code behind so I am left with a mixture of ViewModel and Codebehind. I can overcome the limitation of not having any kind of global object reference by using the MessagingCenter which works remarkably well.
I am wondering if this mechanism can handle the passing of large objects (megabytes) or if internally it simply passes a reference to the object. I suspect from my experiments that it is trying to pass a copy of the object and failing, as I end up with a null object.
Here's the code
MessagingMarker.cs - just a placeholder class
namespace RouteIt.Models;
public class MessagingMarker
{
}
In my MainPageViewModel.cs
//Takes about 10seconds to load
using (var stream = new FileInfo(#"C:\Users\Gordon\source\repos\RouteIt\Resources\Raw\gb.routerdb").OpenRead())
{
routerDb = RouterDb.Deserialize(stream);
}
//send message to codebehind containing object or reference?
MessagingCenter.Send(new MessagingMarker(), "RouterDbLoaded", routerDb);
and in my receiving MainPage.xaml.cs constructor
MessagingCenter.Subscribe<MessagingMarker, RouterDb>(this, "RouterDbLoaded", (sender, arg) =>
{
routerDb = arg;
});
router = new(routerDb);
Any thoughts? (And yes perhaps I need to re-struture my app, but at this stage I don't want to really, one day Mapsui might support mvvm apparently.)
As always thanks for at least reading :)

Amazon IAP Plugin for Xamarin - crash when using TaskCompletionSource

I'm trying to implement a wrapper for the Amazon IAP Plugin for Xamarin. It uses an event based system in the following way:
You can initiate method calls and listen for events. Method calls initiate requests, some of which return a response. Events are asynchronous system-generated messages that are sent in response to method calls to return the requested data to you.
See more here
My goal is to wrap this event based system into some API which allows me to use the plugin with tasks, so I can use the async-await syntax. To achieve that I'm using the TaskCompletionSource like in the following example:
public async Task<bool> GetProductInfoAsync(params string[] productIds)
{
var iapService = AmazonIapV2Impl.Instance;
var tcs = new TaskCompletionSource<bool>();
var skus = new SkusInput { Skus = productIds.ToList() };
var requestId = iapService.GetProductData(skus).RequestId;
GetProductDataResponseDelegator delegator = null;
delegator = new GetProductDataResponseDelegator(response =>
{
if(response.Id == requestId) {
var result = GetResultFromResponse(response);
tcs.SetResult(result);
//iapService.RemoveGetProductDataResponseListener(delegator.responseDelegate);
}
});
iapService.AddGetProductDataResponseListener(delegator.responseDelegate);
return await tcs.Task;
}
This code seems to work fine if the method gets called once, but if it gets called two times in a row the app crashes immediately and the only thing printed to the console is the following message..
[mono] Unhandled Exception:
[mono] System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
[mono-rt] [ERROR] FATAL UNHANDLED EXCEPTION: System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
..which kinda makes no sense at all.
So is there something obvious I'm missing here? Or could it be a bug from the plugin?
I have created a repository with the code above so you can reproduce the problem. It's my playground, so please ignore the whole structure of the project and just focus on the classes AmazonIAPService and MainActivity.
Hint 1:
The commented line //iapService.RemoveGetProductDataResponseListener(delegator.responseDelegate); causes also a crash with the same message but already at the first call of the method.
Hint 2:
The AmazonIAPService contains a commented method which uses await Task.Delay(TimeSpan.FromMilliseconds(1)) and solves the problem from above in a very hacky way which I really don't like.
Problem seems to be that those functions have to run asynchronously. Also mentioned here in the doc. So once you run those functions synchronously somehow they throw exception, i dont what is happening in the library but your hacky solution is the actual solution for that. If you write the function as below. it also works.
PurchaseResponseDelegator delegator = null;
delegator = new PurchaseResponseDelegator(async response =>
{
await Task.Run(() =>
{
if (response.RequestId == requestId)
{
var result = GetPurchaseEventHandler(response);
var sucess = taskCompletionSource.TrySetResult(result);
context.RemovePurchaseResponseListener(delegator.responseDelegate);
}
} );
});
// Register for an event
context.AddPurchaseResponseListener(delegator.responseDelegate);
One other exception I had despite the async-await solution, somehow, it always throws exception for the line taskCompletionSource.SetResult(result); for PurchaseUpdates functions only. if i use instead this line var sucess = taskCompletionSource.TrySetResult(result); it works fine

Data Fetching Crashes in Xamarin Forms

I am trying to fetch Customer data to parse them into customer object to display on TableView. The following code sometimes works, sometimes not. Whenever it does crash, it shows Customer data is empty in the foreach loop even though I run the same code every time. I do not have clue what could be wrong in this circumstances. I am quite new on this platform. If I am missing anything/ extra information, please let me know.
namespace TableViewExample
{
public partial class MyDataServices : ContentPage
{
private ODataClient mODataClient;
private IEnumerable <IDictionary<string,object>> Customers;
public MyDataServices ()
{
InitializeComponent ();
InitializeDataService ();
GetDataFromOdataService ();
TableView tableView = new TableView{ };
var section = new TableSection ("Customer");
foreach (var customers in Customers) {
//System.Diagnostics.Debug.WriteLine ((string)customers ["ContactName"]);
var name = (string)customers ["ContactName"];
var cell = new TextCell{ Text = name };
section.Add (cell);
}
tableView.Root.Add (section);
Padding = new Thickness (10, 20, 10, 10);
Content = new StackLayout () {
Children = { tableView }
};
}
private void InitializeDataService(){
try {
mODataClient = new ODataClient ("myURL is here");
}
catch {
System.Diagnostics.Debug.WriteLine("ERROR!");
}
}
private void GetDataFromOdataService (){
try {
Customers = mODataClient.For ("Customers").FindEntries ();
}
catch {
System.Diagnostics.Debug.WriteLine("ERROR!");
}
}
}
}
Its hard helping out here, however here are some things to consider:-
It sounds like the dataservice could either be not contactable / offline; too busy or it could even be throwing an exception itself and returning a data response that you are not expecting to receive, that then triggers an exception and crash in your application as your always expecting an exact response without catering for any abnormal responses / events.
If you are contacting an external service over the internet it may just be your internet connection is slow / faulty and not returning the information fast enough as other possibilities.
In your code you are assuming that you always get a response from the server - and that this response will always be of an anticipated structure that your expecting to decode - without factoring in any possibility of abnormal responses returned by the dataservice. I have not used ODataClient personally, so not sure how it behaves in the event of maybe no data received / timeout or in your case the dataservice and how it behaves internally in the response to a bad-request etc.
I am assuming an exception would get thrown, and you do get your debug line executed indicating a failure.
You may want to also adjust this statement so that you write out the exception as well, i.e.:-
private void GetDataFromOdataService ()
{
try
{
Customers = mODataClient.For ("Customers").FindEntries ();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("ERROR!" + ex.ToString());
}
}
If there was a bad response, then the line at Customers = ..... would throw the exception as there may be no Customers returned or some other information packaged in the response from the dataservice.
The Customers variable would also be null at this point I am assuming due to this failing.
So when you get back to your code at foreach (var customers in Customers) { it will then throw a null reference exception as Customers is infact null.
As all your current code executes in the constructor without any try and catch block around this, it will also crash your application at this point as well.
Also you are doing all of this work in the constructor. Try seperating this out. I haven't investigated exactly where the constructor gets called in an iOS page life-cycle, however, if it is in the viewDidLoad, then you have something like 10 seconds for everything to complete, otherwise it will exit automatically. I imagine in your case, this isn't applicable however.
Going forward also try putting your layout controls in the constructor, and move your data task to maybe the OnAppearing override instead.
Using async would definitely be advisable as well, but remember you need to inspect the response from your dataservice, as the error could be embedded within the response also and you will need to detect when it is OK to process the data.

Unit testing a custom control in a windows store project

I want to unit test the custom controls I create for a windows store project. Just simple things like "there is a button when X is true".
However, I can't seem to even instantiate the controls in a testing context. Whenever I try to invoke the constructor, I get an exception related to not being run in the UI context. I've also been unable to create coded UI test projects that target windows store projects.
How do I programmatically instantiate a control to test? How do I create a WinRT UI synchronization context?
How do I programmatically send "user" command events to a control?
How do I programmatically instantiate/teardown the entire application?
I've found a hacky way to make non-interactive parts work: with the function Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync.
Obvious, right? However, this still leaves open the question of how to emulate user actions.
/// Runs an action on the UI thread, and blocks on the result
private static void Ui(Action action) {
Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
CoreDispatcherPriority.Normal,
() => action()
).AsTask().Wait();
}
/// Evaluates a function on the UI thread, and blocks on the result
private static T Ui<T>(Func<T> action) {
var result = default(T);
Ui(() => { result = action(); });
return result;
}
[TestMethod]
public void SliderTest() {
// constructing a Slider control is only allowed on the UI thread, so wrap it in UI
var slider = Ui(() => new Slider());
var expected = 0;
// accessing control properties is only allowed on the UI thread, so same deal
Assert.AreEqual(expected, Ui(() => slider.Value));
}

How work with AsyncController in asp.net MVC 3?

Search several blogs about it but always are same examples.
I dunno if misunderstood or'm not knowing use but see no parallel process when work with AsyncController.
The following tests performed
Create a new project of type Asp.net MVC
HomeController.cs
public void IndexAsync()
{
AsyncManager.OutstandingOperations.Increment();
var bg = new BackgroundWorker();
bg.DoWork += (o, e) => GetEntriesBlog();
bg.RunWorkerCompleted += (o, e) =>
{
AsyncManager.Parameters["items"] = e.Result;
AsyncManager.OutstandingOperations.Decrement();
};
bg.RunWorkerAsync();
ViewBag.Message = "Modify this template to kick-start your ASP.NET MVC application.";
}
public ActionResult IndexCompleted(IEnumerable<SyndicationItem> items)
{
return View(items);
}
[NonAction]
public IEnumerable<SyndicationItem> GetEntriesBlog(int page = 0)
{
using (var reader = XmlReader.Create("http://blog.bindsolution.com/rss"))
{
Thread.Sleep(20000);
var rssData = SyndicationFeed.Load(reader);
if (rssData != null)
{
return (from item in rssData.Items
orderby item.PublishDate descending
select item).Take(3).Skip(3 * page).ToList();
}
return null;
}
}
Always delay 20 seconds browsing the site!
I was thinking of using PartialView AsyncController in to perform this task. Work?
I think you are misunderstanding what the Asynchronous Background worker would do.
If the operation takes 20 seconds using a background worker will not reduce that time or make the view render any faster. Using an asynchronous operations will free up the worker process on the server to process other requests while this long running request keep chugging along.
In your case I think you should create a very simple view that returns quickly to the user and kick of the long running operation as an asynch request from the client. For example, render the fast portions of your page (e.g. header, menus, et cetera) and make an AJAX request for the blog entries.
Depending on the nature of the code in GetEntriesBlog you might not need to make the controller operation asynchronous. In theory, since most of the time in this method will be spend waiting for the HTTP GET request to http://blog.bindsolution.com/rss to complete, it might be a good idea but in practice those things need to be bench marked (and perhaps under heavy load) to make sure you are getting the benefit that you expect. Keep in mind that your server code side will be more complex (and harder to maintain) if you make it asynch. I would suggest you go this route only if you do get a significant benefit.

Resources