Wait Inside While Loop to Periodically Check Status - c#

I have a simple process that sends a request to an API, waits until the API has returned a response, and then continues on with this response. I would like to avoid sleeping the main thread as this will be run as a service and I don't want to block starts/stops.
Is there anyway to achieve this with a Timer?
My code looks something like:
string RequestID = api.SendRequest(info);
DateTime fiveMinutesFromNow = GetFiveMinutesFromNow();
ApiResponse response = null;
while (response != null && now < fiveMinutesFromNow)
{
ApiResponse check = api.GetResponse(RequestID);
if(check.status == "Complete")
{
response = check;
break;
}else
{
//Wait for 3 seconds
}
}
if(response.status == "Complete")
{
//Continue on
}
Thank you!
EDIT: Here's my current revision using an AutoResetEvent. Thoughts?
class MyClass
{
private Api _api = new Api();
private ApiResponse _response = new ApiResponse();
private EventWaitHandle _waitHandle = new AutoResetEvent(false);
private string _requestID;
private readonly Object _criticalSection = new Object();
void DoStuff()
{
Info info = GetInfo();
_requestID = _api.SendRequest(info);
Thread pollingThread = new Thread(PollReutersAPI);
pollingThread.isBackground = true;
pollingThread.Start();
_waitHandle.Reset();
if (!_waitHandle.WaitOne(300000))
{
pollingThread.Abort();
}
if(_response.status == "Complete")
{
//Continue on
}
}
void PollApi()
{
while(true)
{
ApiResult check = _api.GetResponse(_requestID);
lock(_criticalSection)
{
if(check.status == "Complete")
{
_response = check;
_waitHandle.Set();
return;
}
else
{
Thread.Sleep(3000);
}
}
}
}
}

Try spinning up a task to avoid the main thread all together. Try something like this:
private CancellationTokenSource tokenSource;
private string RequestID;
ApiResponse response;
private void form1_Load(object sender, EventArgs e)
{
freeUI();
}
private void do_stuff()
{
RequestID = api.SendRequest(info);
DateTime fiveMinutesFromNow = GetFiveMinutesFromNow();
response = null;
while (response != null && now < fiveMinutesFromNow)
{
ApiResponse check = api.GetResponse(RequestID);
if (check.status == "Complete")
{
response = check;
tokenSource.Cancel();
break; // <-- maybe you could remove this since the task was cancelled?
}
else
{
//Wait for 3 seconds
}
}
if (response.status == "Complete")
{
//Continue on
}
}
private async Task freeUI()
{
await start_process();
// do other things
}
private Task start_process()
{
tokenSource = new CancellationTokenSource();
token = tokenSource.Token;
return Task.Factory.StartNew(() =>
{
do_stuff();
}, token);
}
I use this sort of thing for rebinding databound objects or performing large heavy tasks where I want the UI to remain responsive. Since I don't have your API I can't test this but it should work.

Related

How to set Timer in winform for executing 10+ command?

I have around 10 commands of Tx,Rx with time interval of 100 millisecond for each. So Basically a TxRx pair should be executed within every 100 milliseconds. unless the stop button is clicked. Where am I getting Wrong?
Form1 btn_Start
set the time Interval to request and receive the message
private void btn_Start_Click(object sender, EventArgs e)
{
Start();
}
private void Start()
{
btn_Start.Enabled = false;
btn_Stop.Enabled = true;
int Interval = 100;
if (CANTimer.Enabled == false)
{
// CAN Timer confinguration
CANTimer.Enabled = true;
CANTimer.Interval = Convert.ToInt32(fCANInterval); //Interval;
CANTimer.Tick += new System.EventHandler(this.CANTimer_Tick);
CANTimer.Start();
}
else if (CANTimer.Enabled && Program_config_num == 0)
{
CANTimer.Stop();
CANTimer.Interval = Convert.ToInt32(fCANInterval); //Interval;
CANTimer.Enabled = true;
CANTimer.Start();
}
}
Form1 CANTimer_Tick
Execute the command to Send and Receive Messages in every 100 milliseconds, till the btn_Stop is clicked
private void CANTimer_Tick(object sender, EventArgs e)
{
ThreadPool.GetMaxThreads(out maxWorkerThreads, out maxCompletionPortThreads);
ThreadPool.GetAvailableThreads(out workerThreads, out completionPortThread);
int unit_index = 1;
if(IsConnected == false)
{
IsConnected = CANClass.connect();
lbl_Status.Text = "Connected";
}
else
{
Req1_Send = CAN.Send_Req1(unit_index, CANClass); //Sends request for Msg1 to MCU through CANClass
if ((Req1_Send == true) && (Msg1_Received == false))
{
ThreadPool.QueueUserWorkItem(ReadMsg1); //Reads the message and display
}
Req2_Send = CAN.Send_Req2(unit_index, CANClass); //Sends request for Msg2 to MCU through CANClass
if ((Req2_Send == true) && (Msg2_Received == false))
{
ThreadPool.QueueUserWorkItem(ReadMsg2); //Reads the message and display
}
Req3_Send = CAN.Send_Req3(unit_index, CANClass); //Sends request for Msg3 to MCU through CANClass
if ((Req3_Send == true) && (Msg3_Received == false))
{
ThreadPool.QueueUserWorkItem(ReadMsg3); //Reads the message and display
}
............... till the Msg10
}
}
btn_Stop
Stop the connection
private void btn_Stop_Click(object sender, EventArgs e)
{
Stop();
}
private void Stop()
{
btn_Start.Enabled = true;
btn_Stop.Enabled = false;
CanPacket.CanStop();
IsConnected = false;
}
```
The thing with hardware controllers is that (usually) you don't know exactly how long they'll take to fill up the RX Buffer so that you can get the packet response from the TX-RX pair. If you're doing it on a timer tick you're between the terrible risk of buffer underrun (possibly hanging the controller) or wasting time by patting the wait time with margins.
Often it's better to loop in a Task where you perform the IO synchronously (hopefully this involves actual handshaking) for your "10+ Commands" and then get the "100 ms spacing" after all the TX-RX pairs have completed and before the next iteration of the loop.
private async void checkBoxRun_CheckedChanged(object sender, EventArgs e)
{
if (checkBoxRun.Checked)
{
richTextBox.Clear();
await Task.Run(() => loop());
labelIndicator.BackColor = Color.LightGray;
}
}
private async Task loop()
{
do
{
await readCanBusSingle();
if (!checkBoxRun.Checked) return;
// Here's where the spacing of 100 ms or whatever happens.
// I made it longer for test.
await Task.Delay(TimeSpan.FromSeconds(1));
}
while (checkBoxRun.Checked);
}
private async Task readCanBusSingle()
{
string text;
if (_busController.TryConnect())
{
BeginInvoke((MethodInvoker)delegate
{
labelIndicator.BackColor = Color.LightGreen;
richTextBox.SelectionFont = new Font(richTextBox.Font, FontStyle.Bold);
richTextBox.AppendText($"{Environment.NewLine}{DateTime.Now}: REQUEST{Environment.NewLine}");
richTextBox.SelectionFont = new Font(richTextBox.Font, FontStyle.Regular);
});
foreach (RequestID requestID in Enum.GetValues(typeof(RequestID)))
{
var packet = new Packet { RequestID = requestID };
// HARWARE CONTROLLER: Push some bytes into TXQueue
_busController.SendReq(unitIndex: 1, packet: packet);
// HARWARE CONTROLLER: Wait for RXQueue bytes to be present
// Often this means a spin (not an await) until the full
// reapi=onse is available in the MCU.
await Task.Run(() =>
{
while(_busController.Busy)
{
Task.Delay(10).Wait();
}
});
if (packet.MockResponse is uint[] bytes)
{
text = string.Join(" ", bytes.Select(_ => _.ToString("X4")));
}
else
{
text = $"{packet.MockResponse}";
}
BeginInvoke((MethodInvoker)delegate
{
switch (packet.RequestID)
{
case RequestID.REQ1:
richTextBox.SelectionColor = Color.Navy;
break;
case RequestID.REQ2:
richTextBox.SelectionColor = Color.DarkGreen;
break;
case RequestID.REQ3:
richTextBox.SelectionColor = Color.Maroon;
break;
default: throw new InvalidOperationException();
}
richTextBox.AppendText($"{text}{Environment.NewLine}");
richTextBox.ScrollToCaret();
});
}
}
else
{
BeginInvoke((MethodInvoker)delegate
{
labelIndicator.BackColor = Color.LightSalmon;
richTextBox.SelectionColor = Color.Red;
richTextBox.AppendText($"{Environment.NewLine}Connection lost{Environment.NewLine}");
richTextBox.ScrollToCaret();
});
}
}
MOCK
Where:
public enum RequestID { REQ1, REQ2, REQ3 }
public class McuController
{
internal bool TryConnect()
{
switch (Rando.Next(10))
{
case 0:
// The mock controller fails every now and then
return false;
default:
return true;
}
}
internal async void SendReq(int unitIndex, Packet packet)
{
Busy = true;
await packet.SetMockResponse(TimeSpan.FromMilliseconds(Rando.Next(10, 51)));
Busy = false;
}
public static Random Rando { get; } = new Random();
public bool Busy { get; private set; }
}
public class Packet
{
public RequestID RequestID { get; set; }
public int UnitID { get; set; }
// MOCK
public object MockResponse { get; set; }
public async Task SetMockResponse(TimeSpan mockDelay)
{
await Task.Delay(mockDelay);
switch (RequestID)
{
case RequestID.REQ1:
// String
MockResponse =
$"Unit {UnitID}";
break;
case RequestID.REQ2:
// ByteArray
MockResponse =
Enumerable.Range(0, 8)
.Select(_ => (uint)Rando.Next(45000, 55000))
.ToArray();
break;
case RequestID.REQ3:
MockResponse =
mockDelay;
break;
default: throw new InvalidOperationException();
}
}
}

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

RabbitMQ doesn't retrieve messages after resetting OnMessageReceived

I am using RabbitMQ.Client(C#) to work with RabbitMQ. I am having trouble retrieving the messages from the queue once I remove and re-add the message received event handler.
consumer.Received -= OnMessageRecieved;
I have a complex system, where a windows service subscribes to the RabbitMQ queues and process the messages. There are multiple threads running for various things - timer to call PUSH api, another timer to do api authentication etc. If api authentication fails, we don't want to process the messages from the queue. We want to keep the messages in ready state. Only, when the api authentication is success, we want to process the messages. So, on failure event, we remove the event handler and on success we add it back. When we do that, the event handler is added successfully, but now the messages are not retrieved from the queue.
To simulate this, I have created a console app. I have written this in less than an hour, I know this code is very raw and dirty - please excuse me for that.
sub.StopReceiveMessages(); has the code that removes handler consumer.Received -= OnMessageRecieved. And, sub.StartReceiveMessages(); has the code that removes handler consumer.Received += OnMessageRecieved. When you add it back I thought it would work as normal. But, it doesn't hit the MessageReceived() anymore. Is it that we have to call BasicConsume again although I am using the same consumer? Any help would be appreciate.
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Client = RabbitMQ.Client;
namespace RabbitMQTest
{
class Program
{
static void Main(string[] args)
{
MessageBusSubscription sub = new MessageBusSubscription();
sub.Subscription("EmployeeDataChanged", "HR", "CompanyA", 5, 5000);
sub.MessagesReceived += MessageReceived;
Console.WriteLine("Press ESC to exit");
while (!(Console.KeyAvailable && Console.ReadKey(true).Key == ConsoleKey.Escape))
{
// Simulating an event where we have to stop pulling the messages from the queue
sub.StopReceiveMessages();
Thread.Sleep(2000);
// After some time, the issue is resolved and now we resume reading the messages from the queue
sub.StartReceiveMessages();
}
sub.Dispose();
Environment.Exit(0);
}
private static bool MessageReceived(string topic, string subscription, List<MessageContainer> messages)
{
List<MessageContainer> data = null;
data = messages as List<MessageContainer>;
foreach (var messageContainer in data)
{
// Do something with the message
// Ack or Reject based on some logic
}
return true;
}
}
public class MessageBusSubscription : IDisposable
{
#region variables
Client.Events.EventingBasicConsumer consumer;
Client.ConnectionFactory factory;
private System.Timers.Timer _timer = null;
private Client.IModel _channel = null;
private string _topic = string.Empty;
private string _subscription = string.Empty;
int batchCounter = 0;
int batchSize = 0;
ManualResetEvent _waitHandle = new ManualResetEvent(false);
bool _disposing = false;
bool _isSubscribed = false;
List<MessageContainer> messages = new List<MessageContainer>();
private object _processMessageLocker = new object();
public event Func<string, string, List<MessageContainer>, bool> MessagesReceived;
#endregion
public MessageBusSubscription()
{
Client.IConnection conn = GetConnection();
_channel = conn.CreateModel();
}
public void Subscription(string exchangeName, string queueName, string routingKey, int batchSize, double batchInterval)
{
_topic = exchangeName;
_subscription = queueName;
DeclareExchangeAndQueue(exchangeName, queueName, routingKey);
if (batchInterval > 0 && batchSize > 1)
{
_timer = new System.Timers.Timer(batchInterval);
_timer.Elapsed += (o, e) => {
ProcessMessagesReceived(exchangeName, queueName, true);
};
}
Subscribe(routingKey, exchangeName, queueName, batchSize, batchInterval);
}
public Task Subscribe(string routingKey, string topic, string subscription, int _batchSize, double batchInterval)
{
try
{
consumer = new Client.Events.EventingBasicConsumer(_channel);
batchCounter = 0;
batchSize = _batchSize;
consumer.Received += OnMessageRecieved;
_isSubscribed = true;
//RabbitMQ PUSH implementation using RabbitMQ.Client library
var t = Task.Factory.StartNew(() =>
{
try
{
if (_timer != null)
{
_timer.Start();
}
var queueName = string.Join(".", routingKey, topic, subscription);
if (!_disposing)
{
_channel.BasicConsume(queueName, false, consumer);
_waitHandle.WaitOne();
}
if (_timer != null)
{
_timer.Stop();
_timer.Dispose();
}
if (_channel != null)
{
if (_channel.IsOpen)
_channel.Close();
_channel.Dispose();
}
}
catch (Exception ex)
{
}
});
return t;
}
catch (Exception ex)
{
var exTask = new Task(() => { throw new AggregateException(ex); });
exTask.RunSynchronously();
return exTask;
}
}
public void OnMessageRecieved(Client.IBasicConsumer sender, Client.Events.BasicDeliverEventArgs e)
{
try
{
string sourceExchange = string.Empty;
string sourceQueue = string.Empty;
string body = Encoding.ASCII.GetString(e.Body);
string routingKey = e.RoutingKey;
ulong deliveryTag = e.DeliveryTag;
sourceExchange = "";
sourceQueue = "";
MessageContainer msgContainer = new MessageContainer();
msgContainer.Message = body;
batchCounter++;
msgContainer.DeliveryTag = deliveryTag;
lock (_processMessageLocker)
{
messages.Add(msgContainer);
ProcessMessagesReceived(_topic, _subscription, false);
}
}
catch (Exception ex)
{
}
}
public void ProcessMessagesReceived(string topic, string subscription, bool hasTimerElapsed)
{
try
{
// if it's the last message in the batch, or the interval has elapsed
if ((batchCounter % batchSize == 0 && messages.Count > 0) || (hasTimerElapsed && messages.Count > 0))
{
if (_timer != null)
{
_timer.Stop();
}
lock (_processMessageLocker)
{
// process the message
if (!MessagesReceived(topic, subscription, messages))
{
throw new Exception("Message processing exception - look in the default error queue (broker)");
}
messages.Clear();
}
batchCounter = 0;
if (_timer != null)
{
_timer.Start();
}
}
}
catch (Exception ex)
{
throw ex;
}
}
public Client.IConnection GetConnection()
{
factory = new Client.ConnectionFactory();
factory.UserName = "guest";
factory.Password = "guest";
factory.VirtualHost = "/";
factory.HostName = "localhost";
factory.Protocol = Client.Protocols.AMQP_0_9_1;
factory.Port = 5673;
return factory.CreateConnection();
}
public void DeclareExchangeAndQueue(string exchangeName, string queueName, string routingKey)
{
using (var exchangeConn = factory.CreateConnection())
using (Client.IModel channel = exchangeConn.CreateModel())
{
channel.ExchangeDeclare(exchangeName, Client.ExchangeType.Direct);
var queue = String.Join(".", routingKey, exchangeName, queueName);
channel.QueueDeclare(queue, false, false, false, null);
channel.QueueBind(queue, exchangeName, routingKey, null);
}
}
public void StartReceiveMessages()
{
if (_timer != null && !_isSubscribed)
{
_timer.Start();
}
if (consumer != null && !_isSubscribed)
{
consumer.Received += OnMessageRecieved;
_isSubscribed = true;
}
}
public void StopReceiveMessages()
{
if (_timer != null)
{
_timer.Stop();
}
if (consumer != null)
{
consumer.Received -= OnMessageRecieved;
_isSubscribed = false;
}
}
public void Dispose()
{
_disposing = true;
_waitHandle.Set();
_waitHandle?.Dispose();
_waitHandle = null;
if (_timer != null)
{
_timer.Stop();
_timer.Dispose();
}
}
}
public class MessageContainer
{
public ulong DeliveryTag { get; set; }
public string Message { get; set; }
}
}
Don't unsubscribe from the Received event, instead use the BasicCancel method to stop consuming messages, then use BasicConsume again to start consuming.
A lot of the thread synchronization code as well as running the consumer in a Task isn't really the best practice. If you'd like further assistance with this code, save it in a git repository or gist somewhere and follow-up on the official mailing list.
NOTE: the RabbitMQ team monitors the rabbitmq-users mailing list and only sometimes answers questions on StackOverflow.

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