0

I am using an open source graphing library, oxyplot, in a c# wpf application. I am running into an issue where the UI thread is blocked when updating the graphs canvas object. Due to the canvas object belonging to the UI thread the update is done on the UI thread.

I have already moved all code I can to run on background threads, but the actual draw of the canvas isn't as easily solved.

The writer of the library tried with this piece of code using a scheduler from the UI thread's synchronization context but still couldn't get around the UI thread blocking when writing to the canvas.

The example above does work pretty much like the plotting library does so I won't give any of the code for that here.

How could I implement this without blocking the UI thread?

EDIT:

Here is a snippet of my code - it shows how i've attempted to implement this. You can see that I create the path data in the first bit of code. When i'm finished I attempt to loop round these on the UI Thread and add them to the canvas. However, once passed into my Add(p) call I get the error that the object belongs to another thread - even though I have past the pa.ToList() into the addToCanvas call...

        .........
            pa.Add(path);
        }

        Application.Current.Dispatcher.Invoke((Action)(() =>
            {
                addToCanvas(pa.ToList());
            }));
    }

    public void addToCanvas(List<Path> path)
    {
        foreach (Path p in path)
        {
            Add(p);
        }
    }
William Moore
  • 246
  • 2
  • 16
  • Call `ToList()` _before_ you call `Invoke()`. The lambda as you've written it gets a reference to `pa` which belongs to the background thread but the lambda executes on the UI thread. – groverboy Nov 12 '13 at 12:26
  • @groverboy Like this? List copiedList = pa.ToList(); Application.Current.Dispatcher.Invoke((Action)(() => { addToCanvas(copiedList); })); – William Moore Nov 12 '13 at 12:35
  • @groverboy I still get the same 'object belong to another thread message' then once I get into my Add(p) function. – William Moore Nov 12 '13 at 12:40
  • Then it would seem the `Path` objects also belong to the background thread. I think this approach can't work. What about my suggestion to use an intermediate bitmap? – groverboy Nov 12 '13 at 12:48
  • @groverboy I thought that would be the case! Yes, i'm going to have to do that now - although I run into a whole host of other issues then as the canvas size etc belongs to the UI thread - so I need to make sure that the canvas I draw in my thread is the same size etc. – William Moore Nov 12 '13 at 12:52

1 Answers1

1

I think you have to use a little hack here, something like a double buffering - create a buffer (byte, bitmap, etc.) and draw into it in the separate thread, and then place the stuff drawn at the control

  • Yes! But... Once i've drawn my canvas on a seperate thread - how do I then draw that to the UI - I just get the 'object belongs to another thread' error – William Moore Nov 12 '13 at 10:46
  • I think it is possible to create the control on another thread, draw on it and then pass to UI, but that's not done easy. WPF canvas may be not designed for complex drawing, so you can use some else - GDI+ or OpenGL, if needed –  Nov 12 '13 at 11:08
  • 1
    Get the worker thread to [save the image as a bitmap](http://stackoverflow.com/questions/5851168/save-canvas-to-bitmap) then get the UI thread to [render the bitmap](http://stackoverflow.com/questions/12866758/placing-bitmap-in-canvas-in-c-sharp) to the canvas. This way you avoid cross-threading. – groverboy Nov 12 '13 at 11:22
  • I dont understand why after i've drawn my canvas, even invoking the dispatcher to add my canvas to the UI results in getting an error that I now can't access the canvas object I created on my background thread. – William Moore Nov 12 '13 at 11:31
  • 'object belongs to another thread' --> dispatcher.invoke (or dispatcher.beginInvoke) is your friend. – GameAlchemist Nov 12 '13 at 23:00
  • @GameAlchemist thats what I'm trying to do, but because the Path objects belong to the background thread - when I call 'addToCanvas' on the UI thread, the object I pass into that still belongs to the background thread. Is there any way around this? – William Moore Nov 13 '13 at 11:22
  • @WilliamMoore I remember, when I was developing an example for audio processing, I needed to use canvas to render the **waveform**. And drawing a lot of steps was SO slow - I could only make it faster only by making calculations in other thread and reducing drawing steps amount. –  Nov 13 '13 at 11:31