How to download files using HttpClient with a ProgressBar? - c#

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:

Related

Receiving message from Service Bus queue is working on console apps but not in windows form apps?

I have the following c# functions that allow me to receive messages from a given queue from Azure, it works fine on console apps :
public async Task MainReceiver()
{
queueClient = new QueueClient(connectionString, queueName);
var messageHandlerOptions = new MessageHandlerOptions(ExceptionReceivedHandler)
{
MaxConcurrentCalls = 5,
AutoComplete = false,
};
queueClient.RegisterMessageHandler(ProcessMessagesAsync, messageHandlerOptions);
Console.ReadLine();
await queueClient.CloseAsync();
}
public async Task ProcessMessagesAsync(Message message, CancellationToken token)
{
var jsonString = Encoding.UTF8.GetString(message.Body);
Console.WriteLine($"Person Received: { jsonString }");
List<string> data = new List<string>();
if (messageLists.ContainsKey(queueName))
{
data = messageLists[queueName];
messageLists.Remove(queueName);
data.Add(jsonString);
}
else
{
data.Add(jsonString);
}
messageLists.Add(queueName, data);
await queueClient.CompleteAsync(message.SystemProperties.LockToken);
}
My goal now is to list the messages using a listbox control.
But if i try to call the same main methode in a button click, nothing happends, in debug mode i noticed that my breakpoint in ProcessMessagesAsync is never reached by my win form app.
public async void button2_Click(object sender, EventArgs e)
{
await receiveMessage();
}
public async Task receiveMessage()
{
await messages.MainReceiver();
List<string> data = new List<string>();
data = messages.messageLists["eventsqueue"];
for (int i = 0; i < messages.messageLists.Count; i++)
listBox1.Items.Add(data[i]);
}
I tried to use to queue my task in the thread pool using the following code , but it didn't help :
ThreadPool.QueueUserWorkItem(async (w) => await messages.MainReceiver());
Please follow the below workaround to achieve Service Bus queue messages into Windows Forms
Three major steps to show your data into LISTBOX
Create a list box using the ListBox() constructor is provided by the ListBox class.
ListBox listBox1 = new ListBox();
Set/Add the Items property of the ListBox provided by the ListBox class.
listBox1.Items.Add("your data to add in listbox");
Add this ListBox control to the form using Add() method.
this.Controls.Add(listBox1);
Here I am following the above steps.
Program.cs
using System.Collections.Generic;
using System.Windows.Forms;
namespace servicebuswinform
{
using Microsoft.Azure.ServiceBus;
using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
public class Program
{
const string ServiceBusConnectionString = <ServiceBus Connection string>;
const string QueueName = "<Queue name>";
static IQueueClient queueClient;
public static List<string> data = new List<string>();
[STAThread]
public static void Main()
{
Application.SetHighDpiMode(HighDpiMode.SystemAware);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
public static async Task MainAsync()
{
const int numberOfMessages = 10;
queueClient = new QueueClient(ServiceBusConnectionString, QueueName);
RegisterOnMessageHandlerAndReceiveMessages();
// Send Messages
await SendMessagesAsync(numberOfMessages);
await queueClient.CloseAsync();
}
public static void RegisterOnMessageHandlerAndReceiveMessages()
{
var messageHandlerOptions = new MessageHandlerOptions(ExceptionReceivedHandler)
{
MaxConcurrentCalls = 1,
AutoComplete = false
};
// Register the function that will process messages
queueClient.RegisterMessageHandler(ProcessMessagesAsync, messageHandlerOptions);
}
public static async Task ProcessMessagesAsync(Message message, CancellationToken token)
{
//Adding message into list
data.Add("Received message: SequenceNumber:"+ message.SystemProperties.SequenceNumber.ToString()+" Body:" + Encoding.UTF8.GetString(message.Body));
await queueClient.CompleteAsync(message.SystemProperties.LockToken);
}
// Use this Handler to look at the exceptions received on the MessagePump
public static Task ExceptionReceivedHandler(ExceptionReceivedEventArgs exceptionReceivedEventArgs)
{
Console.WriteLine($"Message handler encountered an exception {exceptionReceivedEventArgs.Exception}.");
var context = exceptionReceivedEventArgs.ExceptionReceivedContext;
Console.WriteLine("Exception context for troubleshooting:");
Console.WriteLine($"- Endpoint: {context.Endpoint}");
Console.WriteLine($"- Entity Path: {context.EntityPath}");
Console.WriteLine($"- Executing Action: {context.Action}");
return Task.CompletedTask;
}
public static async Task SendMessagesAsync(int numberOfMessagesToSend)
{
try
{
for (var i = 0; i < numberOfMessagesToSend; i++)
{
// Create a new message to send to the queue
string messageBody = $"Message {i}";
var message = new Message(Encoding.UTF8.GetBytes(messageBody));
// Write the body of the message to the console
Console.WriteLine($"Sending message: {messageBody}");
// Send the message to the queue
await queueClient.SendAsync(message);
}
}
catch (Exception exception)
{
Console.WriteLine($"{DateTime.Now} :: Exception: {exception.Message}");
}
}
}
}
Form.cs
Here I am adding one button if we hit the button it will get the service bus queue messages and it will be added in Listbox.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace servicebuswinform
{
public partial class Form1 : Form
{
public static List<string> data1 = new List<string>();
public Form1()
{
InitializeComponent();
}
public async void button1_Click(object sender, EventArgs e)
{
await receiveMessage();
ListBox listBox1 = new ListBox();
this.listBox1.Visible = true;
for (int i = 0; i < data1.Count; i++)
{
listBox1.FormattingEnabled = true;
listBox1.Enabled = true;
listBox1.Size = new System.Drawing.Size(692, 214);
listBox1.Location = new System.Drawing.Point(1, 131);
listBox1.MultiColumn = true;
listBox1.SelectionMode = SelectionMode.MultiExtended;
listBox1.BeginUpdate();
listBox1.Items.Add("item" + data1[i].ToString());
}
this.Controls.Add(listBox1);
}
public static async Task receiveMessage()
{
await Program.MainAsync();
data1 = Program.data;
}
private void Form1_Load(object sender, EventArgs e)
{
this.listBox1.Visible = false;
}
}
}
You can follow the above steps which I have mentioned. Using this I can see the list of service bus queue messages in windows forms

Make Check Internet Async win Webclient.Openread

My apps in xamarin form is populate by a json file i download online then i need to be sure device have internet access.
I try this but it freeze UI and timeout not seam to be used then i like to make it async.
private void CheckClicked(object sender, EventArgs e)
{
if (CheckForInternetConnection() == true)
{
isinternet.Text = "Internet ok";
}
else
{
isinternet.Text = "Internet down";
}
}
public static bool CheckForInternetConnection()
{
try
{
using (var client = new MyWebClient(5000))
using (client.OpenRead("http://google.com/generate_204"))
return true;
}
catch
{
return false;
}
}
with this class
public class MyWebClient : WebClient
{
private int timeout;
public int Timeout
{
get
{
return timeout;
}
set
{
timeout = value;
}
}
public MyWebClient()
{
this.timeout = 10000;
}
public MyWebClient(int timeout)
{
this.timeout = timeout;
}
}
You should use HttpClient and the asynchronous methods it implements. Try to stay away from legacy HTTP client implementations such as WebClient.
A quick example would be:
private static readonly HttpClient _httpClient = new HttpClient();
private async void CheckClicked(object sender, EventArgs e)
{
var isConnected = await CheckForInternetConnectionAsync();
if(isConnected)
{
isinternet.Text = "Internet ok";
}
else
{
isinternet.Text = "Internet down";
}
}
private static async Task<bool> CheckForInternetConnectionAsync()
{
using(var tokSource = new CancellationTokenSource(5000))
{
try
{
await _httpClient.GetAsync("https://www.example.com", tokSource.Token);
}
catch(OperationCanceledException)
{
return false;
}
}
return true;
}
This will leave your UI responsive, but at the same time accomplish making a request.

How to call a function in a backgroundworker thread that is to be completed on the main UI thread? [duplicate]

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
});

Handling CancellationToken from different class

I have a class like so:
public class FtpTaskVideo : IFtpTask
{
//some fields
public CancellationTokenSource tokenSource = new CancellationTokenSource();
private Panel CreatePanel(string text, int count, int value)
{
Panel pnlOutput = new Panel();
pnlOutput.Name = "pnlInfo";
pnlOutput.AutoSize = true;
pnlOutput.BorderStyle = BorderStyle.FixedSingle;
//adding some controls
Button btnUserCancel = new Button();
btnUserCancel.Name = "btnUserCancel";
btnUserCancel.AutoSize = true;
btnUserCancel.Text = "Stop";
btnUserCancel.Click += new EventHandler(btnUserCancel_Click);
pnlOutput.Controls.Add(btnUserCancel);
btnUserCancel.BringToFront();
return pnlOutput;
}
public void btnUserCancel_Click(object sender, EventArgs e)
{
tokenSource.Cancel();
}
public void Start()
{
//some code
while(somethingToDownload)
{
var task = Task<SharedConstants.downloadFtpFileStatus>.Factory.StartNew(() => dff.Download(tokenSource.Token), tokenSource.Token);
try
{
downloadStatus = task.Result;
}
catch (System.AggregateException exc)
{
//do something
}
//some code
}
}
And in the second class (dff):
public Shared.Classes.SharedConstants.downloadFtpFileStatus Download(CancellationToken token)
{
if (token.IsCancellationRequested)
{
return Shared.Classes.SharedConstants.downloadFtpFileStatus.CANCELLED;
}
else //do some stuff
}
Now, I have another class, which dff is an instance of and Download is it's method. One of the things dff does is update and redraw the panel according to the data it gets during Download method operation. How, after it draws a button and I press it can I send the cancel token back to original class to stop it from downloading?

The application called an interface that was marshalled for a different thread - Windows Store App

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
});

Categories

Resources