Beric
Beric

Reputation: 83

Dispatcher.Invoke to update WPF canvas causes performance problems

I am using Websockets to draw data on my canvas:

webSocket.OnMessage += (sender, e) =>
{
    String websocketData = e.Data.Substring(3);
    WebSocketDataStructure jsonData = JsonConvert.DeserializeObject<WebSocketDataStructure>(websocketData);

    // Give control back to main thread for drawing
    Application.Current.Dispatcher.BeginInvoke(
      DispatcherPriority.Background,
      new Action(() => this.updateCanvas(jsonData)));
    };

private void updateCanvas(WebSocketDataStructure myData)
{
    CanvasUtils.DrawLine(MyCanvas, colorNormalBrush, myData.hsLine.x1, myData.hsLine.y1, myData.hsLine.x2, myData.hsLine.y2);
}

When I get multiple messages per second the application starts to lag. I read that using Dispatcher.BeginInvoke() is bad for handling frequent data, since we immediately switch back to the UI-Thread every time.

Is there a better way to implement this? I thought about creating a timer and updating the UI-Thread every full second. This would work by storing websocket data in a List, and process it on the UI-Thread (https://en.wikipedia.org/wiki/Producer%E2%80%93consumer_problem). My only problem with this approach was, that I couldn't set up an endless loop with Thread.Sleep(1000) on the UI-Thread.

Upvotes: 2

Views: 1074

Answers (1)

sTrenat
sTrenat

Reputation: 1049

You could queue your high-frequent data and read items from the data queue at a lower pace. By using a DispatcherTimer you could avoid the need for directly calling the Dispatcher.

var queue = new ConcurrentQueue<WebSocketDataStructure>();

webSocket.OnMessage += (s, e) =>
{
    var websocketData = e.Data.Substring(3);
    var jsonData = JsonConvert.DeserializeObject<WebSocketDataStructure>(websocketData);

    queue.Enqueue(jsonData);
};

var timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) };

timer.Tick += (s, e) =>
{
    WebSocketDataStructure data;

    while (queue.TryDequeue(out data))
    {
        CanvasUtils.DrawLine(MyCanvas, colorNormalBrush,
            data.hsLine.x1, data.hsLine.y1, data.hsLine.x2, data.hsLine.y2);
    }
};

timer.Start();

Upvotes: 2

Related Questions