I'm developing a windows phone 8.1 application.
I'm making use of Twilio to send text messages at one go with Azure services.
Thing is that I have to make a post request to a URL. When I use Fiddler to make the request, everything works great! However, I don't know how to reproduce the same using C#. I tried using HTTP Client and all but don't know how to make it work good.
Could anyone kindly help me?
Thanking you in advance.
using SDKTemplate;
using Windows.UI.Xaml.Controls;using System;
using System.Threading;
using System.Threading.Tasks;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Navigation;
using Windows.Web.Http;
namespace SDKSample.HttpClientSample
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class Scenario6 : Page, IDisposable
{
// A pointer back to the main page. This is needed if you want to call methods in MainPage such
// as NotifyUser()
MainPage rootPage = MainPage.Current;
private HttpClient httpClient;
private CancellationTokenSource cts;
public Scenario6()
{
this.InitializeComponent();
}
/// <summary>
/// Invoked when this page is about to be displayed in a Frame.
/// </summary>
/// <param name="e">Event data that describes how this page was reached. The Parameter
/// property is typically used to configure the page.</param>
protected override void OnNavigatedTo(NavigationEventArgs e)
{
Helpers.CreateHttpClient(ref httpClient);
cts = new CancellationTokenSource();
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
// If the navigation is external to the app do not clean up.
// This can occur on Phone when suspending the app.
if (e.NavigationMode == NavigationMode.Forward && e.Uri == null)
{
return;
}
Dispose();
}
private async void Start_Click(object sender, RoutedEventArgs e)
{
Uri resourceAddress;
// The value of 'AddressField' is set by the user and is therefore untrusted input. If we can't create a
// valid, absolute URI, we'll notify the user about the incorrect input.
if (!Helpers.TryGetUri(AddressField.Text, out resourceAddress))
{
rootPage.NotifyUser("Invalid URI.", NotifyType.ErrorMessage);
return;
}
Helpers.ScenarioStarted(StartButton, CancelButton, OutputField);
rootPage.NotifyUser("In progress", NotifyType.StatusMessage);
try
{
HttpMultipartFormDataContent form = new HttpMultipartFormDataContent();
form.Add(new HttpStringContent(RequestBodyField.Text), "data");
HttpResponseMessage response = await httpClient.PostAsync(resourceAddress, form).AsTask(cts.Token);
await Helpers.DisplayTextResultAsync(response, OutputField, cts.Token);
rootPage.NotifyUser("Completed", NotifyType.StatusMessage);
}
catch (TaskCanceledException)
{
rootPage.NotifyUser("Request canceled.", NotifyType.ErrorMessage);
}
catch (Exception ex)
{
rootPage.NotifyUser("Error: " + ex.Message, NotifyType.ErrorMessage);
}
finally
{
Helpers.ScenarioCompleted(StartButton, CancelButton);
}
}
private void Cancel_Click(object sender, RoutedEventArgs e)
{
cts.Cancel();
cts.Dispose();
// Re-create the CancellationTokenSource.
cts = new CancellationTokenSource();
}
public void Dispose()
{
if (httpClient != null)
{
httpClient.Dispose();
httpClient = null;
}
if (cts != null)
{
cts.Dispose();
cts = null;
}
}
}
}
So okay,
I've tried that the below. It seems to be sending the request but without a body in json. How can I send a request in JSON using the following code?
using (var client = new HttpClient())
{
var values = new Dictionary<string, string>
{
{ "thing1", "hello" },
{ "thing2", "world" }
};
var content = new FormUrlEncodedContent(values);
var response = await client.PostAsync("http://www.example.com/recepticle.aspx", content);
var responseString = await response.Content.ReadAsStringAsync();
}
Here my new code which is not working (Getting error cannot convert cannot convert from 'windows.web.http.http stringcontent' to 'system.net.http.httpcontent' on line System.Net.Http.HttpResponseMessage response = await client.PostAsync("http://twilionotifs.azure-mobile.net/api/notificationqueue", stringContent);):
private async void Button_Click(object sender, RoutedEventArgs e)
{
HttpStringContent stringContent = new HttpStringContent("{ \"firstName\": \"John\" }",
Windows.Storage.Streams.UnicodeEncoding.Utf8,
"application/json");
System.Net.Http.HttpClient client = new System.Net.Http.HttpClient();
System.Net.Http.HttpResponseMessage response = await client.PostAsync("http://twilionotifs.azure-mobile.net/api/notificationqueue", stringContent);
}
}
Related
I created a new class named SiteDownload and added some links to download images:
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
public class SiteDownload
{
public static List<string> Sites()
{
List<string> list = new List<string>();
list.Add("mysite.com/sites/default/files/1231105.gif");
list.Add("mysite.com/sites/default/files/1231040.gif");
return list;
}
public static async Task<List<Website>> ParallelDownload(IProgress<ProgressReport> progress, CancellationTokenSource cancellationTokenSource)
{
List<string> sites = Sites();
List<Website> list = new List<Website>();
ProgressReport progressReport = new ProgressReport();
ParallelOptions parallelOptions = new ParallelOptions();
parallelOptions.MaxDegreeOfParallelism = 8;
parallelOptions.CancellationToken = cancellationTokenSource.Token;
await Task.Run(() =>
{
try
{
Parallel.ForEach<string>(sites, parallelOptions, (site) =>
{
Website results = Download(site);
list.Add(results);
progressReport.SitesDownloaded = list;
progressReport.PercentageComplete = (list.Count * 100) / sites.Count;
progress.Report(progressReport);
parallelOptions.CancellationToken.ThrowIfCancellationRequested();
});
}
catch (OperationCanceledException ex)
{
throw ex;
}
});
return list;
}
private static Website Download(string url)
{
Website website = new Website();
WebClient client = new WebClient();
website.Url = url;
website.Data = client.DownloadString(url);
return website;
}
public class Website
{
public string Url { get; set; }
public string Data { get; set; }
}
public class ProgressReport
{
public int PercentageComplete { get; set; }
public List<Website> SitesDownloaded { get; set; }
}
}
in form1:
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using static HttpClientFilesDownloader.SiteDownload;
namespace HttpClientFilesDownloader
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
void PrintResults(List<Website> results)
{
richTextBox1.Text = string.Empty;
foreach (var item in results)
richTextBox1.Text += $"{item.Url} downloaded: {item.Data.Length} characters long.{Environment.NewLine}";
}
void ReportProgress(object sender, ProgressReport e)
{
progressBar1.Value = e.PercentageComplete;
label1.Text = $"Completed: {e.PercentageComplete} %";
PrintResults(e.SitesDownloaded);
}
CancellationTokenSource cancellationTokenSource;
private async void button1_Click(object sender, EventArgs e)
{
try
{
cancellationTokenSource = new CancellationTokenSource();
Progress<ProgressReport> progress = new Progress<ProgressReport>();
progress.ProgressChanged += ReportProgress;
var watch = Stopwatch.StartNew();
var results = await SiteDownload.ParallelDownload(progress, cancellationTokenSource);
PrintResults(results);
watch.Stop();
var elapsed = watch.ElapsedMilliseconds;
richTextBox1.Text += $"Total execution time: {elapsed}";
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Message", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
cancellationTokenSource.Dispose();
}
}
private void button2_Click(object sender, EventArgs e)
{
if (cancellationTokenSource != null)
cancellationTokenSource.Cancel();
}
}
}
The desiger
When I click the START button, nothing happens. I don't see the progressBar get any process and the label1 is not updating and nothing in the RichTextBox. It's just not downloading the images.
I'm not getting any errors, it's just not downloading.
I took this example from this site just instead downloading site/s I'm trying to download images files and save them on the hard disk as images:
example
I also need to add header like i did with webclient:
webClient.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:67.0) Gecko/20100101 Firefox/67.0 Chrome");
but not sure how to add the headers to the HttpClient.
An example of a HTTP resource downloader. This class is meant to target .NET 6+, since it's using Parallel.ForEachAsync(). The record keyword requires C# 9+. Nullable enabled
I tried to keep the structure you have used in the OP as much as possible
To start the download of a collection of resources, call the static Download() method, passing an IProgress<ProgressReport> delegate, a collection of strings representing the URLs of the resources and a CancellationTokenSource
The ReportProgress() method marshals to the UI Thread a ProgressReport record. It references a WebData record, which contains the URL of the current resource, the image (in this case) bytes, the Completed status and the Exception thrown in case the resource failed to download for some reason. If the download is canceled in the meantime, the Exception reason is going to be The operation was canceled.
It also returns the overall progress of the downloads, in the form of a percentage.
Note that the progress procedure is completed also when you cancel the operation, since you probably want to know which resource was completed before the operation was canceled and which one couldn't complete
Note: the static Download() method is not Thread-Safe, i.e., you cannot call this method concurrently, e.g., to download multiple lists of resources at the same time (though it can be easily refactored, making it non-static).
Check the IsBusy Property before you call that method again.
public class ResourceDownloader {
private static readonly Lazy<HttpClient> client = new(() => {
HttpClientHandler handler = CreateHandler(autoRedirect: true);
var client = new HttpClient(handler, true) { Timeout = TimeSpan.FromSeconds(60) };
client.DefaultRequestHeaders.Add("User-Agent", #"Mozilla/5.0 (Windows NT 10; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.0");
client.DefaultRequestHeaders.Add("Cache-Control", "no-cache");
client.DefaultRequestHeaders.Add("Accept-Encoding", "gzip, deflate");
// Keep true if you download resources from different collections of URLs each time
// Remove or set to false if you use the same URLs multiple times and frequently
client.DefaultRequestHeaders.ConnectionClose = true;
return client;
}, true);
private static HttpClientHandler CreateHandler(bool autoRedirect)
{
return new HttpClientHandler() {
AllowAutoRedirect = autoRedirect,
CookieContainer = new CookieContainer(),
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
};
}
public record WebData(string Url, byte[]? Data, bool Completed = true, Exception? Ex = null);
public record ProgressReport(WebData Site, int PercentageComplete);
private static object syncObj = new object();
private static ConcurrentBag<WebData> processed = default!;
private static int progressCount = 0;
private static int totalCount = 0;
public static bool IsBusy { get; internal set; } = false;
public static async Task<List<WebData>> Download(IProgress<ProgressReport> progress, IList<string> sites, CancellationTokenSource cts)
{
IsBusy = true;
processed = new ConcurrentBag<WebData>();
progressCount = 0;
totalCount = sites.Count;
try {
ParallelOptions options = new() {
// If it's a single web site, set a value that doesn't get you black-listed
// Otherwise, increase the value
MaxDegreeOfParallelism = 8,
CancellationToken = cts.Token
};
await Parallel.ForEachAsync(sites, options, async (site, token) => {
try {
var dataBytes = await client.Value.GetByteArrayAsync(site, token);
ReportProgress(progress, dataBytes, site, null);
}
catch (Exception ex) {
ReportProgress(progress, null, site, ex);
}
});
}
// To Debug / Log
catch (TaskCanceledException) { Debug.Print("The operation was canceled"); }
finally { IsBusy = false; }
return processed.ToList();
}
private static void ReportProgress(IProgress<ProgressReport> progress, byte[]? data, string site, Exception? ex) {
lock (syncObj) {
progressCount += 1;
var percentage = progressCount * 100 / totalCount;
WebData webData = new(site, data, ex is null, ex);
processed.Add(webData);
progress.Report(new ProgressReport(webData, percentage));
}
}
}
You can setup a Form like this:
Add a TextBox (here, named logger) to show the status of the resources that are being downloaded
A Button used to start the download (named btnStartDownload)
A Button to cancel the download (named btnStopDownload)
A ProgressBar (named progressBar) used to show the overall progress
Note that with an active (not configured) debugger, you may have notifications that Exceptions are thrown, so maybe run the Project with CTRL + F5
public partial class SomeForm : Form {
public SomeForm() => InitializeComponent();
internal List<string> Sites()
{
var list = new List<string>();
list.Add("https://somesite/someresource.jpg");
// [...] add more URLs
return list;
}
IProgress<ResourceDownloader.ProgressReport>? downloadProgress = null;
CancellationTokenSource? cts = null;
private void Updater(ResourceDownloader.ProgressReport progress)
{
StringBuilder log = new(1024);
if (progress.Site.Completed) {
log.Append($"Success! \t {progress.Site.Url}\r\n");
}
else {
log.Append($"Failed! \t {progress.Site.Url}\r\n");
log.Append($"\tReason: {progress.Site.Ex?.Message}\r\n");
}
logger.AppendText(log.ToString());
progressBar.Value = progress.PercentageComplete;
}
private async void btnStartDownload_Click(object sender, EventArgs e)
{
if (ResourceDownloader.IsBusy) return;
var sites = Sites();
// This collection will contain the status (and data) of all downloads in th end
List<ResourceDownloader.WebData>? downloads = null;
using (cts = new CancellationTokenSource()) {
downloadProgress = new Progress<ResourceDownloader.ProgressReport>(Updater);
downloads = await ResourceDownloader.Download(downloadProgress, sites, cts);
}
}
private void btnStopDownload_Click(object sender, EventArgs e) => cts?.Cancel();
}
This is how it works:
So, first I have read a ton of threads on this particular problem and I still do not understand how to fix it. Basically, I am trying to communicate with a websocket and store the message received in an observable collection that is bound to a listview. I know that I am getting a response back properly from the socket, but when it tries to add it to the observable collection it gives me the following error:
The application called an interface that was marshalled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))
I've read some information on "dispatch" as well as some other things, but I am just massively confused! Here is my code:
public ObservableCollection<string> messageList { get; set; }
private void MessageReceived(MessageWebSocket sender, MessageWebSocketMessageReceivedEventArgs args)
{
string read = "";
try
{
using (DataReader reader = args.GetDataReader())
{
reader.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf8;
read = reader.ReadString(reader.UnconsumedBufferLength);
}
}
catch (Exception ex) // For debugging
{
WebErrorStatus status = WebSocketError.GetStatus(ex.GetBaseException().HResult);
// Add your specific error-handling code here.
}
if (read != "")
messageList.Add(read); // this is where I get the error
}
And this is the binding:
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
//await Authenticate();
Gameboard.DataContext = Game.GameDetails.Singleton;
lstHighScores.ItemsSource = sendInfo.messageList;
}
How do I make the error go away while still binding to the observable collection for my listview?
This solved my issue:
Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
() =>
{
// Your UI update code goes here!
}
);
Correct way to get the CoreDispatcher in a Windows Store app
Try replacing
messageList.Add(read);
with
Dispatcher.Invoke((Action)(() => messageList.Add(read)));
If you're calling from outside your Window class, try:
Application.Current.Dispatcher.Invoke((Action)(() => messageList.Add(read)));
Slight modification for task based async methods but the code in here will not be awaited.
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
() =>
{
// Your UI update code goes here!
}
).AsTask();
This code WILL await, and will allow you to return a value:
private async static Task<string> GetPin()
{
var taskCompletionSource = new TaskCompletionSource<string>();
CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
async () =>
{
var pin = await UI.GetPin();
taskCompletionSource.SetResult(pin);
}
);
return await taskCompletionSource.Task;
}
And on Android:
private async Task<string> GetPin()
{
var taskCompletionSource = new TaskCompletionSource<string>();
RunOnUiThread(async () =>
{
var pin = await UI.GetPin();
taskCompletionSource.SetResult(pin);
});
return await taskCompletionSource.Task;
}
Maby this is not a "good" practice, but it works.. I leave a message from webSocket, to mainBody instance, where I have a timered reader...
public class C_AUTHORIZATION
{
public Observer3.A_MainPage_cl parentPageInstance; //еще одни экземпляр родителя
public WebSocket x_Websocket;
private string payload = "";
private DateTime nowMoment = DateTime.Now;
public void GET_AUTHORIZED()
{
bitfinex_Websocket= new WebSocket("wss://*****.com/ws/2");
var apiKey = "";
var apiSecret = "";
DateTime nowMoment = DateTime.Now;
payload = "{}";
x_Websocket.Opened += new EventHandler(websocket_Opened);
x_Websocket.Closed += new EventHandler(websocket_Closed);
}
void websocket_Opened(object sender, EventArgs e)
{
x_Websocket.Send(payload);
parentPageInstance.F_messager(payload);
}
void websocket_Closed(object sender, EventArgs e)
{
parentPageInstance.F_messager("L106 websocket_Closed!");
GET_AUTHORIZED();
}
}
public sealed partial class A_MainPage_cl : Page
{
DispatcherTimer ChartsRedrawerTimer;
public bool HeartBeat = true;
private string Message;
public A_MainPage_cl()
{
this.InitializeComponent();
ChartsRedrawerTimer = new DispatcherTimer() { Interval = new TimeSpan(0, 0, 0, 0, 100) };
ChartsRedrawerTimer.Tick += Messager_Timer;
ChartsRedrawerTimer.Start();
}
private void Messager_Timer(object sender, object e)
{
if(Message !=null) //
{
F_WriteLine(Message);
Message = null; //
}
}
public void F_messager(string message) //
{
Message = message;
}
In Xamarin, I got around this by using:
Device.BeginInvokeOnMainThread(() => {
// code goes here
});
I have a project where I am using System.Net.Http.HttpClient. I am trying to centralize all calls to my Web APIs so that I have common error handing etc. I created the following class in my project.
using ModernHttpClient;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
namespace WebAPIHelper
{
class WebAPICaller
{
public async Task<string> CallWebService(string ps_URI)
{
HttpClient lobj_HTTPClient = null;
HttpResponseMessage lobj_HTTPResponse = null;
string ls_Response = "";
//We assume the internet is available.
try
{
//Get the Days of the Week
lobj_HTTPClient = new HttpClient(new NativeMessageHandler());
lobj_HTTPClient.BaseAddress = new Uri(App.APIPrefix);
lobj_HTTPClient.DefaultRequestHeaders.Accept.Clear();
lobj_HTTPClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
lobj_HTTPResponse = await lobj_HTTPClient.GetAsync(ps_URI);
if (!lobj_HTTPResponse.IsSuccessStatusCode)
{
Debug.WriteLine(lobj_HTTPResponse.ReasonPhrase);
}
else
{
ls_Response = await lobj_HTTPResponse.Content.ReadAsStringAsync();
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
finally
{
if (lobj_HTTPClient != null)
lobj_HTTPClient.Dispose();
if (lobj_HTTPResponse != null)
{
lobj_HTTPResponse.Dispose();
}
}
return ls_Response;
}
}
}
I call the function from an instance object I created in my ViewModel class for languages as follows:
using ModernHttpClient;
using Newtonsoft.Json;
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
namespace WebAPIHelper
{
public class VM_Languages : INotifyPropertyChanged
{
/// <summary>
/// A collection for CGSLanguage objects.
/// </summary>
public ObservableCollection<GBSLanguage_ForList> Items_ForList { get; private set; }
const string ic_LanguagesAPIUrl = #"/languages/true";
/// <summary>
/// Constructor for the Languages view model.
/// </summary>
public VM_Languages()
{
this.Items_ForList = new ObservableCollection<GBSLanguage_ForList>();
}
/// <summary>
/// Indicates of the view model data has been loaded
/// </summary>
public bool IsDataLoaded
{
get;
private set;
}
/// <summary>
/// Creates and adds a the countries to the collection.
/// </summary>
public async Task LoadData()
{
HttpClient lobj_HTTPClient = null;
HttpResponseMessage lobj_HTTPResponse = null;
string ls_Response = "";
try
{
IsDataLoaded = false;
string ls_WorkLanguageURI = ic_LanguagesAPIUrl;
//Get the Days of the Week
lobj_HTTPClient = new HttpClient(new NativeMessageHandler());
lobj_HTTPClient.BaseAddress = new Uri(App.APIPrefix);
lobj_HTTPClient.DefaultRequestHeaders.Accept.Clear();
lobj_HTTPClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
**//This call will not work
//WebAPICaller lobj_APICaller = new WebAPICaller();
//ls_Response = lobj_APICaller.CallWebService(ls_WorkLanguageURI).Result;
//This call will work
lobj_HTTPResponse = await lobj_HTTPClient.GetAsync(ls_WorkLanguageURI);**
if (lobj_HTTPResponse.IsSuccessStatusCode)
{
if (this.Items_ForList != null)
this.Items_ForList.Clear();
ls_Response = await lobj_HTTPResponse.Content.ReadAsStringAsync();
Items_ForList = JsonConvert.DeserializeObject<ObservableCollection<GBSLanguage_ForList>>(ls_Response);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
finally
{
this.IsDataLoaded = true;
NotifyPropertyChanged("GBSLanguages_ForList");
}
}
/// <summary>
/// Notifies subscribers that a property has changed.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I have all my ViewModels in a static class so I only ever get one instance of them as follows:
namespace WebAPIHelper
{
public static class ViewModelObjects
{
private static VM_Languages iobj_Languages;
public static VM_Languages Languages
{
get
{
if (iobj_Languages == null)
iobj_Languages = new VM_Languages();
return iobj_Languages;
}
}
}
}
In my on appearing code of my main page I have the following call to retrieve the data from the WebAPI
protected override async void OnAppearing()
{
Device.BeginInvokeOnMainThread(() =>
{
if (!ViewModelObjects.Languages.IsDataLoaded)
ViewModelObjects.Languages.LoadData();
});
//If the DOW and Language data are not loaded yet - wait
while (!ViewModelObjects.Languages.IsDataLoaded)
{
await Task.Delay(1000);
}
}
The problem is when I attempt to use my WebAPICaller class, it appears to crash on the line. I never get a return from it. No exceptions are ever raised and my program never continues.
lobj_HTTPResponse = await lobj_HTTPClient.GetAsync(ps_URI);
If I make what I believe to be the exact same call from my ViewModel, it works. (I have both the call to the WebAPICaller class as well as a direct call to GetAsync in the View Model so you can test it out by commenting and uncommenting.)
Any idea as to what I am doing wrong?
Link to full sample project:
https://1drv.ms/u/s!Ams6cZUzaeQy3M8uGAuaGggMt0Fi-A
So here is what I found. It seems that awaiting the HTTPClient.GetAsync was causing the error. (Pretty sure the thread was blocked.) So to start with I took out the await and added code to delay the task when HTTPClient's return task was not completed.
var lobj_Result = lobj_HTTPClient.GetAsync(ps_URI);
while (!lobj_Result.IsCompleted)
{
Task.Delay(100);
}
Because I no longer await the call in the LoadData method, I was able to remove the async Task declaration and simply make it a method.
public void LoadData()
{
HttpClient lobj_HTTPClient = null;
HttpResponseMessage lobj_HTTPResponse = null;
string ls_Response = "";
try
{
IsDataLoaded = false;
string ls_WorkLanguageURI = ic_LanguagesAPIUrl;
//Get the Days of the Week
lobj_HTTPClient = new HttpClient(new NativeMessageHandler());
lobj_HTTPClient.BaseAddress = new Uri(App.APIPrefix);
lobj_HTTPClient.DefaultRequestHeaders.Accept.Clear();
lobj_HTTPClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
//This call will not work
WebAPICaller lobj_APICaller = new WebAPICaller();
ls_Response = lobj_APICaller.CallWebService(ls_WorkLanguageURI).Result;
if (ls_Response.Trim().Length > 0)
{
if (this.Items_ForList != null)
this.Items_ForList.Clear();
Items_ForList = JsonConvert.DeserializeObject<ObservableCollection<GBSLanguage_ForList>>(ls_Response);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
finally
{
this.IsDataLoaded = true;
NotifyPropertyChanged("GBSLanguages_ForList");
}
}
The I could remove the async from my on appearing event and simply call the loaddata event synchronously.
if (!ViewModelObjects.Languages.IsDataLoaded)
ViewModelObjects.Languages.LoadData();
After making all these changes, the desired result was achieved. Everything ran in synchronous manner (I know the call to GetAsync function of the HTTPClient is still async) and did not return until the results were retrieve and loaded into the object.
I hope this helps other people out. :)
There are a couple of things I see going on. First, the commented-out code:
//This call will not work
//WebAPICaller lobj_APICaller = new WebAPICaller();
//ls_Response = lobj_APICaller.CallWebService(ls_WorkLanguageURI).Result;
Is using .Result instead of await. This may be the root of your problem. Using .Result should be avoided in general, just use await. See the answer here Await on a completed task same as task.Result? for more details on why.
The second thing is that you are already on the UI thread when OnAppearing is called, so there is no need to call Device.BeginInvokeOnMainThread. What you're doing in that method currently is equivalent to:
protected override async void OnAppearing()
{
if (!ViewModelObjects.Languages.IsDataLoaded)
await ViewModelObjects.Languages.LoadData();
}
The next question is whether or not it's a great idea to be doing this in OnAppearing(). This can cause your app to seem non-responsive.
The general use of Device.BeginInvokeOnMainThread is for when you don't know if you're currently on the main thread, but these UI event handlers always are called on the main thread by the Xamarin platform.
Based on the source code on github
void IPageController.SendAppearing()
{
if (_hasAppeared)
return;
_hasAppeared = true;
if (IsBusy)
MessagingCenter.Send(this, BusySetSignalName, true);
OnAppearing();
EventHandler handler = Appearing;
if (handler != null)
handler(this, EventArgs.Empty);
var pageContainer = this as IPageContainer<Page>;
((IPageController)pageContainer?.CurrentPage)?.SendAppearing();
}
there is still a way to do this with async/await approach.
You will notice that the OnAppearing method is called just before the event is triggered.
Subscribe to the Appearing event of the page/view
protected override void OnAppearing() {
this.Appearing += Page_Appearing;
}
and create an async method like you did originally but this time have it on an actual even handler
private async void Page_Appearing(object sender, EventArgs e) {
if (!ViewModelObjects.Languages.IsDataLoaded)
await ViewModelObjects.Languages.LoadData();
//unsubscribing from the event
this.Appearing -= Page_Appearing;
}
This way there is no need to busy wait delay the thread while waiting for the task to complete.
So, first I have read a ton of threads on this particular problem and I still do not understand how to fix it. Basically, I am trying to communicate with a websocket and store the message received in an observable collection that is bound to a listview. I know that I am getting a response back properly from the socket, but when it tries to add it to the observable collection it gives me the following error:
The application called an interface that was marshalled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))
I've read some information on "dispatch" as well as some other things, but I am just massively confused! Here is my code:
public ObservableCollection<string> messageList { get; set; }
private void MessageReceived(MessageWebSocket sender, MessageWebSocketMessageReceivedEventArgs args)
{
string read = "";
try
{
using (DataReader reader = args.GetDataReader())
{
reader.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf8;
read = reader.ReadString(reader.UnconsumedBufferLength);
}
}
catch (Exception ex) // For debugging
{
WebErrorStatus status = WebSocketError.GetStatus(ex.GetBaseException().HResult);
// Add your specific error-handling code here.
}
if (read != "")
messageList.Add(read); // this is where I get the error
}
And this is the binding:
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
//await Authenticate();
Gameboard.DataContext = Game.GameDetails.Singleton;
lstHighScores.ItemsSource = sendInfo.messageList;
}
How do I make the error go away while still binding to the observable collection for my listview?
This solved my issue:
Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
() =>
{
// Your UI update code goes here!
}
);
Correct way to get the CoreDispatcher in a Windows Store app
Try replacing
messageList.Add(read);
with
Dispatcher.Invoke((Action)(() => messageList.Add(read)));
If you're calling from outside your Window class, try:
Application.Current.Dispatcher.Invoke((Action)(() => messageList.Add(read)));
Slight modification for task based async methods but the code in here will not be awaited.
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
() =>
{
// Your UI update code goes here!
}
).AsTask();
This code WILL await, and will allow you to return a value:
private async static Task<string> GetPin()
{
var taskCompletionSource = new TaskCompletionSource<string>();
CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
async () =>
{
var pin = await UI.GetPin();
taskCompletionSource.SetResult(pin);
}
);
return await taskCompletionSource.Task;
}
And on Android:
private async Task<string> GetPin()
{
var taskCompletionSource = new TaskCompletionSource<string>();
RunOnUiThread(async () =>
{
var pin = await UI.GetPin();
taskCompletionSource.SetResult(pin);
});
return await taskCompletionSource.Task;
}
Maby this is not a "good" practice, but it works.. I leave a message from webSocket, to mainBody instance, where I have a timered reader...
public class C_AUTHORIZATION
{
public Observer3.A_MainPage_cl parentPageInstance; //еще одни экземпляр родителя
public WebSocket x_Websocket;
private string payload = "";
private DateTime nowMoment = DateTime.Now;
public void GET_AUTHORIZED()
{
bitfinex_Websocket= new WebSocket("wss://*****.com/ws/2");
var apiKey = "";
var apiSecret = "";
DateTime nowMoment = DateTime.Now;
payload = "{}";
x_Websocket.Opened += new EventHandler(websocket_Opened);
x_Websocket.Closed += new EventHandler(websocket_Closed);
}
void websocket_Opened(object sender, EventArgs e)
{
x_Websocket.Send(payload);
parentPageInstance.F_messager(payload);
}
void websocket_Closed(object sender, EventArgs e)
{
parentPageInstance.F_messager("L106 websocket_Closed!");
GET_AUTHORIZED();
}
}
public sealed partial class A_MainPage_cl : Page
{
DispatcherTimer ChartsRedrawerTimer;
public bool HeartBeat = true;
private string Message;
public A_MainPage_cl()
{
this.InitializeComponent();
ChartsRedrawerTimer = new DispatcherTimer() { Interval = new TimeSpan(0, 0, 0, 0, 100) };
ChartsRedrawerTimer.Tick += Messager_Timer;
ChartsRedrawerTimer.Start();
}
private void Messager_Timer(object sender, object e)
{
if(Message !=null) //
{
F_WriteLine(Message);
Message = null; //
}
}
public void F_messager(string message) //
{
Message = message;
}
In Xamarin, I got around this by using:
Device.BeginInvokeOnMainThread(() => {
// code goes here
});
he below code just plays 2 videos over an over again using a xaml mediaelement. I'm noticing that the memory is not getting cleaned up on windows 8.1 systems and it eventually dies. I see memory getting reclaimed on Win8.0. Am I doing something wrong?
namespace PlaybackTest
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
StorageFile video1;
StorageFile video2;
StorageFile nextvid;
bool firstvid = true;
Windows.Storage.Streams.IRandomAccessStream _stream;
int iterctr = 0;
public MainPage()
{
this.InitializeComponent();
}
/// <summary>
/// Invoked when this page is about to be displayed in a Frame.
/// </summary>
/// <param name="e">Event data that describes how this page was reached. The Parameter
/// property is typically used to configure the page.</param>
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
meMainPlayer.Source = null;
var filepath = Windows.ApplicationModel.Package.Current.InstalledLocation;
video1 = await filepath.GetFileAsync("Data\\output1.mp4");
video2 = await filepath.GetFileAsync("Data\\output2.mp4");
nextvid = video1;
}
private void MediaElement_MediaEnded_1(object sender, RoutedEventArgs e)
{
Debug.WriteLine("media ended event");
if (firstvid == true)
{
Debug.WriteLine("setting up second vid");
firstvid = false;
nextvid = video2;
}
else
{
Debug.WriteLine("setting up first vid");
firstvid = true;
nextvid = video1;
}
meMainPlayer.Stop();
Debug.WriteLine("after stop ");
meMainPlayer.Position = new TimeSpan(0);
Debug.WriteLine("after position set to 0 ");
meMainPlayer.Source = null;
Debug.WriteLine("meplayer source = null ");
}
private void MediaElement_CurrentStateChanged_1(object sender, RoutedEventArgs e)
{
if (meMainPlayer.CurrentState == MediaElementState.Closed)
{
Debug.WriteLine("current media element state closed");
if (_stream != null)
{
_stream.Dispose();
_stream = null;
}
LoadNextVid();
}
}
private async void LoadNextVid()
{
Debug.WriteLine("");
await System.Threading.Tasks.Task.Delay(1000);
try
{
Debug.WriteLine("before strem opened");
_stream = await nextvid.OpenAsync(FileAccessMode.Read);
Debug.WriteLine("after stream opened");
await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
meMainPlayer.SetSource(_stream, nextvid.ContentType);
txtIter.Text = "Iteration:" + iterctr;
});
iterctr++;
Debug.WriteLine("after dispatcher");
}
catch (Exception ex)
{
Debug.WriteLine("exceoption caught " + ex.Message);
}
}
private async void btnStart_Click_1(object sender, RoutedEventArgs e)
{
LoadNextVid();
}
}
}
You're not leveraging the IDisposable construct when accessing the file as a resource.
Specifically you are missing the "Using" construct when accessing the file as a resource.
As a result, memory stays allocated because this resource does not get disposed.
_stream = await nextvid.OpenAsync(FileAccessMode.Read);
Consider the following example:
public async void ContinueFileOpenPicker(FileOpenPickerContinuationEventArgs args)
{
if (args.Files.Count > 0)
{
var imageFile = args.Files[0] as StorageFile;
// Ensure the stream is disposed once the image is loaded
using (IRandomAccessStream fileStream = await imageFile.OpenAsync(Windows.Storage.FileAccessMode.Read))
{
// Set the image source to the selected bitmap
BitmapImage bitmapImage = new BitmapImage();
await bitmapImage.SetSourceAsync(fileStream);
ImageControl.Source = bitmapImage;
await _viewModel.Upload(imageFile);
}
}
}