Update: FOund the error. my code was working as intended but my colleague added a small code to the device i was communicating with. The result was that it ignored my second command because it was busy doing something else.
Fixed it and now its working again.
At the beginning of the week I asked a question about restricting access to the serial port to just one instance.
Someone really helped me there and mentioned the SemaphoreSlim to me.
This really did the trick at that time and I thought that would be it.
Now a few days later I finally fixed aynother major problem and while testing it I noticed the semaphore not working anymore.
I rolled back my code to a previous version where I just implemented the semaphore and where it was definitely working.
But even that code is not working anymore.
I didn't change anything in my Visual Studio, my PC or my Raspberry...
Does anybody have an idea why something which should theoretically work doesn't work anymore?
Would really appreciate the help here :)
Added a very short version of the code I'm using.
Normally I would process the data I get into Int or string arrays.
I would also have a CRC16 verification and everything would be contained in try/catch blocks to catch exceptions when they occur.
But testwise this is everything I need so far.
I will try to provide more detailed information if needed. Just let me know.
Current behavior:
First task starts and works fine.
Second task doesnt start and every new task after that doesn't start either.
Expected behavior:
Start first task and complete it.
After the first task is done load second task and finish it.
When I start another Task after that it should also run that.
Mainpage.xaml
public MainPage()
{
this.InitializeComponent();
InitPort();
}
private async void getData_Click(object sender, RoutedEventArgs e)
{
// Call the async operations.
// I usually don't call the same operation twice at the same time but it's just a test here.
// In normal usage the data contains different sizes.
await getData();
await getData();
}
private async Task getData()
{
ReadData readData = new ReadData();
byte[] readOutput = await readData.ReadParaBlock();
DisplayTextBox.Text = BitConverter.ToString(readOutput);
}
public async void InitPort()
{
string success = await ReadWriteAdapter.Current.Init();
DisplayTextBox.Text = success;
}
ReadData.cs
public class ReadData
{
private ReadBlock readBlock = new ReadBlock();
public async Task<byte[]> ReadParaBlock()
{
// Load task into the semaphore
await ReadWriteAdapter.Current.semaphore.WaitAsync();
// start writing to device
await readBlock.Write();
// dropped check of recieved checksum because obsolete for test
// start reading from device
byte[] recievedArray = await readBlock.Read();
// release the task from semaphore
ReadWriteAdapter.Current.semaphore.Release();
return recievedArray;
}
}
ReadBlock.cs
public class ReadBlock
{
public async Task<uint> Write()
{
// Command sent to device to get data
byte[] WriteArray = System.Text.Encoding.ASCII.GetBytes("getdata");
return await ReadWriteAdapter.Current.WriteAsync(WriteArray);
}
public async Task<byte[]> Read()
{
byte[] ListenOut = await ReadWriteAdapter.Current.Listen(100);
// dropped data conversion because obsolete for test
return ListenOut;
}
}
ReadWriteAdapter.cs
public class ReadWriteAdapter
{
public SemaphoreSlim semaphore { get; private set; }
private static readonly Object lockObject = new object();
private static ReadWriteAdapter instance;
private DataWriter dataWriter = null;
private DataReader dataReader = null;
private SerialDevice serialPort = null;
public static ReadWriteAdapter Current
{
get
{
if (instance == null)
{
lock (lockObject)
{
if (instance == null)
{
instance = new ReadWriteAdapter();
}
}
}
return instance;
}
}
private ReadWriteAdapter()
{
this.semaphore = new SemaphoreSlim(1, 1);
}
// initialize the serial port and configure it
public async Task<string> Init()
{
string aqs = SerialDevice.GetDeviceSelector();
DeviceInformationCollection devices = await DeviceInformation.FindAllAsync(aqs, null);
if (devices.Any())
{
string deviceId = devices[0].Id;
serialPort = await SerialDevice.FromIdAsync(deviceId);
serialPort.WriteTimeout = TimeSpan.FromMilliseconds(1000);
serialPort.ReadTimeout = TimeSpan.FromMilliseconds(1000);
serialPort.BaudRate = 19200;
serialPort.Parity = SerialParity.None;
serialPort.StopBits = SerialStopBitCount.One;
serialPort.DataBits = 8;
serialPort.Handshake = SerialHandshake.None;
dataWriter = new DataWriter(serialPort.OutputStream);
dataReader = new DataReader(serialPort.InputStream);
}
return "found port";
}
// start to listen to the serial port
public async Task<byte[]> Listen(uint BufferLength)
{
byte[] listen = new byte[BufferLength];
dataReader = new DataReader(serialPort.InputStream);
listen = await ReadAsync(BufferLength);
if (dataReader != null)
{
dataReader.DetachStream();
dataReader.Dispose();
dataReader = null;
}
return listen;
}
// function to read and interpret the data from serial port
private async Task<byte[]> ReadAsync(uint ReadBufferLength)
{
Task<uint> loadAsyncTask;
byte[] returnArray = new byte[ReadBufferLength];
dataReader.InputStreamOptions = InputStreamOptions.Partial;
loadAsyncTask = dataReader.LoadAsync(ReadBufferLength).AsTask();
uint bytesRead = await loadAsyncTask;
if (bytesRead > 0)
{
dataReader.ReadBytes(returnArray);
}
return returnArray;
}
// write the data using the serial port
public async Task<uint> WriteAsync(byte[] data)
{
dataWriter.WriteBytes(data);
Task<uint> storeAsyncTask = dataWriter.StoreAsync().AsTask();
return await storeAsyncTask;
}
}
This looks like it should work, and indeed, a version modified to use a MemoryStream instead of a serial port works just fine. I don't have access to the I/O APIs you're using, so I don't know anything about how they're supposed to be used. The semaphore, however, seems to be doing its job just fine. Below you'll find a simplified version of your example, with buttons to run GetData() in serial or in parallel.
If there is a problem with the semaphore, it could only be that you are not calling Release() from inside a finally. Otherwise, your use of the semaphore looks fine.
To see that the semaphore is working, you need only change the initializer to new SemaphoreSlim(8, 8) and see the threads clobber each other.
ReadBlock.cs:
public class ReadBlock {
private static int nextString;
private static readonly string[] strings = {
"ONE ", "TWO ", "THREE", "FOUR ",
"FIVE ", "SIX ", "SEVEN", "EIGHT"
};
public static async Task<byte[]> ReadParaBlock() {
var id = Interlocked.Increment(ref nextString) - 1;
var name = strings[id % strings.Length];
try
{
await ReadWriteAdapter.Current.Semaphore.WaitAsync();
Trace.WriteLine($"[{name.Trim()}] Entered Semaphore");
await Write(Encoding.ASCII.GetBytes("TEST_" + name));
return await Read();
}
finally {
Trace.WriteLine($"[{name.Trim()}] Exiting Semaphore");
ReadWriteAdapter.Current.Semaphore.Release();
}
}
public static async Task<uint> Write(byte[] bytes) =>
await ReadWriteAdapter.Current.WriteAsync(bytes);
public static async Task<byte[]> Read() => await ReadWriteAdapter.Current.Listen(10);
}
ReadWriteAdapter.cs:
public class ReadWriteAdapter {
private static ReadWriteAdapter instance;
public static ReadWriteAdapter Current
=> LazyInitializer.EnsureInitialized(
ref instance,
() => new ReadWriteAdapter());
private readonly MemoryStream stream = new MemoryStream();
public SemaphoreSlim Semaphore { get; } = new SemaphoreSlim(1, 1);
public async Task<string> Init()
=> await Task.FromResult("found port");
public async Task<byte[]> Listen(uint bufferLength)
=> await ReadAsync(bufferLength);
private async Task<byte[]> ReadAsync(uint readBufferLength) {
await Task.CompletedTask;
var returnArray = new byte[readBufferLength];
await stream.ReadAsync(returnArray, 0, returnArray.Length);
return returnArray;
}
public async Task<uint> WriteAsync(byte[] data) {
stream.SetLength(stream.Capacity);
stream.Position = 0;
await Task.Delay(1);
await stream.WriteAsync(data, 0, data.Length);
stream.SetLength(data.Length);
stream.Position = 0;
return (uint)data.Length;
}
}
MainWindow.xaml.cs:
public partial class MainWindow {
private class TextBoxTraceListener : TraceListener {
private readonly TextBoxBase _textBox;
public TextBoxTraceListener(TextBoxBase textBox)
=> _textBox = textBox;
public override void WriteLine(string message)
=> Write(message + Environment.NewLine);
public override void Write(string message) {
_textBox.Dispatcher.BeginInvoke(
DispatcherPriority.Normal,
new Action(() => _textBox.AppendText(message)));
}
}
public MainWindow() {
TaskScheduler.UnobservedTaskException +=
(s, e) => Trace.TraceError(e.ToString());
InitializeComponent();
Trace.Listeners.Add(new TextBoxTraceListener(textBox));
InitPort();
}
private async void InitPort() {
textBox.AppendText(await ReadWriteAdapter.Current.Init());
}
private async void OnGetDataInSerialClick(object sender, RoutedEventArgs e) {
textBox.Clear();
for (var i = 0; i < 8; i++) await GetData();
}
private async void OnGetDataInParallelClick(object sender, RoutedEventArgs e) {
textBox.Clear();
await Task.WhenAll(Enumerable.Range(0, 8).Select(i => GetData()));
}
private async Task GetData() {
await Task.Delay(50).ConfigureAwait(false); // Get off the UI thread.
Trace.WriteLine(Encoding.ASCII.GetString(await ReadBlock.ReadParaBlock()));
}
}
MainWindow.xaml:
<Window x:Class="WpfTest2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<DockPanel LastChildFill="True">
<Button DockPanel.Dock="Bottom" Click="OnGetDataInSerialClick"
Content="Load Data in Serial" />
<Button DockPanel.Dock="Bottom" Click="OnGetDataInParallelClick"
Content="Load Data in Parallel" />
<TextBox x:Name="textBox" TextWrapping="Wrap" />
</DockPanel>
</Window>
Related
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
});
From the documentation here: "The methods of this class help protect against errors that can occur when the scheduler switches contexts while a thread is updating a variable that can be accessed by other threads..."
Also, an answer to this question states "INTERLOCKED METHODS ARE CONCURRENTLY SAFE ON ANY NUMBER OF COREs OR CPUs" which seems pretty clear.
Based on the above I thought Interlocked.Add() would be sufficient for multiple threads to do addition on a variable. Apparently I'm wrong or I'm using the method incorrectly. In the runnable code below I expect Downloader.ActiveRequestCount to be zero when Run() completes. If I do not lock around the call to Interlocked.Add I get a random non-zero result. What is the correct usage of Interlocked.Add()?
class Program
{
private Downloader downloader { get; set; }
static void Main(string[] args)
{
new Program().Run().Wait();
}
public async Task Run()
{
downloader = new Downloader();
List<Task> tasks = new List<Task>(100);
for (int i = 0; i < 100; i++)
tasks.Add(Task.Run(Download));
await Task.WhenAll(tasks);
Console.Clear();
//expected:0, actual when lock is not used:random number i.e. 51,115
Console.WriteLine($"ActiveRequestCount is : {downloader.ActiveRequestCount}");
Console.ReadLine();
}
private async Task Download()
{
for (int i = 0; i < 100; i++)
await downloader.Download();
}
}
public class Downloader :INotifyPropertyChanged
{
private object locker = new object();
private int _ActiveRequestCount;
public int ActiveRequestCount { get => _ActiveRequestCount; private set => _ActiveRequestCount = value; }
public async Task<string> Download()
{
string result = string.Empty;
try
{
IncrementActiveRequestCount(1);
result = await Task.FromResult("boo");
}
catch (Exception ex)
{
Console.WriteLine("oops");
}
finally
{
IncrementActiveRequestCount(-1);
}
return result;
}
public void IncrementActiveRequestCount(int value)
{
//lock (locker) // is this redundant
//{
_ActiveRequestCount = Interlocked.Add(ref _ActiveRequestCount, value);
//}
RaisePropertyChanged(nameof(ActiveRequestCount));
}
#region INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged([CallerMemberNameAttribute] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
#endregion
}
Replace
_ActiveRequestCount = Interlocked.Add(ref _ActiveRequestCount, value);
with
Interlocked.Add(ref _ActiveRequestCount, value);
Interlocked.Add is thread-safe and takes a ref parameter, so that it can do the assignment safely. You additionally perform an (unnecessary) unsafe assignment (=). Just remove it.
Having below reference code for Async TCP server, I want to pass CancellationToken to OnDataReceived:
public sealed class TcpServer : IDisposable
{
private readonly TcpListener _listener;
private CancellationTokenSource _tokenSource;
private CancellationToken _token;
private bool _listening;
public event EventHandler<ConnectedEventArgs> OnDataReceived;
public TcpServer(IPAddress address, int port)
{
_listener = new TcpListener(address, port);
}
public async Task StartAsync(CancellationToken? token = null)
{
_tokenSource = CancellationTokenSource.CreateLinkedTokenSource(token ?? new CancellationToken());
_token = _tokenSource.Token;
_listener.Start();
_listening = true;
try
{
while (!_token.IsCancellationRequested)
{
await Task.Run(async () =>
{
var tcpClientTask = _listener.AcceptTcpClientAsync();
var tcpClient = await tcpClientTask;
OnDataReceived?.Invoke(tcpClient, new ConnectedEventArgs(tcpClient.GetStream()));
}, _token);
}
}
finally
{
_listener.Stop();
_listening = false;
}
}
public void Stop()
{
_tokenSource?.Cancel();
}
public void Dispose()
{
Stop();
}
}
public class ConnectedEventArgs : EventArgs
{
public NetworkStream Stream { get; private set; }
public ConnectedEventArgs(NetworkStream stream)
{
Stream = stream;
}
}
The following code, shows how I am consuming the above code, currently in ReadStreamBytesAsync I am passing new CancellationToken() instead I want to pass the token argument from MainAsync:
class Program
{
static void Main(string[] args)
{
InitAsyncWork();
Console.WriteLine("ended.");
Console.ReadKey();
}
static void InitAsyncWork()
{
var cts = new CancellationTokenSource();
Console.CancelKeyPress += (s, e) =>
{
e.Cancel = true;
cts.Cancel();
};
MainAsync(cts.Token).GetAwaiter().GetResult();
}
static async Task MainAsync(CancellationToken token)
{
using (var server = new TcpServer(IPAddress.Any, 54001))
{
server.OnDataReceived += TcpClientOnlyReceive;
await server.StartAsync(token);
}
}
private static async void TcpClientOnlyReceive(object sender, ConnectedEventArgs e)
{
try
{
using (TcpClient client = (TcpClient)sender)
using (NetworkStream stream = e.Stream)
{
while (Utilities.IsTcpClientConnected(client))
{
if (client.Available == 0)
{
await Task.Delay(50);
continue;
}
byte[] rawX = await ReadStreamBytesAsync(stream, client.ReceiveBufferSize, new CancellationToken());
}//while(client.Connected)
}//using(client)using(stream)
}
catch (Exception ex)
{
Utilities.logger.Error("TcpClientOnlyReceive():Exception!!\r\nMsg: {1}\r\nStack: {2}", ex.Message, ex.StackTrace);
}
}
private static async Task<byte[]> ReadStreamBytesAsync(NetworkStream stream, int maxBytesToRead, CancellationToken token)
{
var bytesRead = 0;
var totalBytesRead = 0;
var clientMsg = new byte[maxBytesToRead];
byte[] contentBytes;
using (MemoryStream ms = new MemoryStream())
{
do
{
bytesRead = await stream.ReadAsync(clientMsg, totalBytesRead, clientMsg.Length - totalBytesRead, token);
await ms.WriteAsync(clientMsg, totalBytesRead, bytesRead, token);
totalBytesRead += bytesRead;
}
while (bytesRead > 0 && stream.DataAvailable);
contentBytes = ms.ToArray();
}
return contentBytes;
}
}
Should I define cts as global variable? Or modify ConnectedEventArgs class so that it accept CancellationToken as property ?
Clarifications:
Based on the received comments and 1 vote to close the question!
My main objective is to close the application once the user press ctrl+c, currently that's prevent accepting new connections but it won't cancel the established connections.
I never heard of Tasks meant to replace events, if that's true, please share a reference.
It will be great to share better version of the code, if its BAD design! personally I think its clean and robust.
I would like to create a task to run serial commands on. At this time I do not need to return anything from the method that is doing the work. This will probably change later, but I am now curious as to how this.
This is what I have. I would like to use a separate method for the task instead of creating an anonymous action. I have tried returning void, with the result of "void can not be explicitly converted to a Task". I have also tried. Task<void>. The Last thing I have tried is returning a Task, but I receive, error "Not all Code paths return a value" and "Can not implicily convert void to type task"
In the pass I have used a Thread to accomplish this, but I'd like to use Tasks this time around.
internal class Hardware
{
private EventHandler<SequenceDoneEventArgs> SequenceDone;
private List<Step> Steps;
private System.IO.Ports.SerialPort comport = null;
private Task SequenceTask;
private CancellationTokenSource RequestStopSource;
private CancellationToken RequestStopToken;
private void Initialize()
{
comport = new System.IO.Ports.SerialPort("COM2", 115200, System.IO.Ports.Parity.None,8);
comport.DataReceived += Comport_DataReceived;
}
public async void RunSequence()
{
if (comport == null)
{
Initialize();
}
if (!comport.IsOpen)
{
comport.Open();
}
RequestStopSource = new CancellationTokenSource();
RequestStopToken = RequestStopSource.Token;
SequenceTask = await Task.Run(() => { doSequence(); });
}
private Task doSequence()
{
//** Run Sequence stuff here
}
}
ETA:
In the end this is my the complete solution
internal class Hardware
{
private EventHandler<SequenceDoneEventArgs> SequenceDone;
private List<Step> Steps;
private System.IO.Ports.SerialPort comport = null;
private Task SequenceTask;
private CancellationTokenSource RequestStopSource;
private CancellationToken RequestStopToken;
private void Initialize()
{
comport = new System.IO.Ports.SerialPort("COM2", 115200, System.IO.Ports.Parity.None,8);
comport.DataReceived += Comport_DataReceived;
}
public async void RunSequence()
{
if (comport == null)
{
Initialize();
}
if (!comport.IsOpen)
{
comport.Open();
}
RequestStopSource = new CancellationTokenSource();
RequestStopToken = RequestStopSource.Token;
SequenceTask = await Task.Factory.StartNew(async () => { await doSequence(); });
}
private Task doSequence()
{
//** Run Sequence stuff here
//return null;
return Task.CompletedTask;
}
}
Just mark doSequence as async (assuming it uses await):
private async Task doSequence()
Also, it's a good idea to return this Task in the delegate you pass to Task.Run:
SequenceTask = await Task.Run(() => doSequence());
I would like to create a task to run serial commands on.
This leads me to believe that using async and Task may not be the best solution for your scenario. I suggest you look into TPL Dataflow.
SequenceTask = await Task.Factory.StartNew(async() => { await doSequence(); });
Also your RunSequence() should return Task instead of void.
Actually if you await the Task this should result in the same:
SequenceTask = await doSequence();
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
});