|
-
Apr 30th, 2009, 05:02 PM
#1
Problem rendering graphics with a BackgroundWorker
I am trying to make a graphics program with mousewheel-controlled zooming using GDI+. My plan is to provide rapid feedback, such as a low resolution render or even just a bounding rectangle, followed by a full resolution render as soon as possible afterwards. It seems logical to me to try a BackgroundWorker for the latter task. This is my first attempt to use one of these, or any other way of multithreading for that matter.
Mousewheel events can follow one another much more quickly than I can render to the screen. That means I need to kill any rendering process that is already running and start a new one with the latest value of the zoom scaling factor. Here is a stripped down version of my code.
vb.net Code:
Imports System.ComponentModel Public Class ZoomWorker Public Event ProvideFeedBack() Public Event ScaledImageReady() Public SourceImage As Bitmap Public ScaledImage As Bitmap Private _ScaleFactor As Single = 1.0 Private WithEvents ScaleImageWorker As New BackgroundWorker Public Sub New() ScaleImageWorker.WorkerSupportsCancellation = True AddHandler DrawingSurface.MouseWheelTurned, AddressOf SetScale End Sub Private Sub SetScale(ByVal sender As DrawingSurface, ByVal e As MouseEventArgs) 'calculate the new scale factor _ScaleFactor *= CSng(1 + e.Delta * (1 + e.Delta / 120) / 20000) RaiseEvent ProvideFeedBack() If ScaleImageWorker.IsBusy Then ScaleImageWorker.CancelAsync() 'this is where it hangs: Do Until ScaleImageWorker.CancellationPending = False Threading.Thread.Sleep(10) Loop End If ScaleImageWorker.RunWorkerAsync(_ScaleFactor) End Sub Private Sub ScaleImageWorker_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs) Handles ScaleImageWorker.DoWork If e.Cancel Then Exit Sub Dim worker As BackgroundWorker = CType(sender, BackgroundWorker) e.Result = ScaleImage(CSng(e.Argument), ScaleImageWorker, e) End Sub Private Sub ScaleImageWorker_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles ScaleImageWorker.RunWorkerCompleted ScaledImage = DirectCast(e.Result, Bitmap) RaiseEvent ScaledImageReady() End Sub Private Function ScaleImage(ByVal scalefactor As Single, _ ByVal worker As BackgroundWorker, ByVal e As DoWorkEventArgs) As Bitmap If worker.CancellationPending Then e.Cancel = True Else 'render and return the scaled bitmap End If End Function
The above class coexists with a DrawingSurface class, a UserControl which listens for the ProvideFeedback and ScaledImageReady events and paints the image accordingly. It also raises mouse/keyboard events for the zoom and other tools to deal with.
My problem arises in the DO loop waiting for Cancellation to finish. The zooming routine works for one or maybe half a dozen renders, depending on the image size, but then it hangs in that loop and never comes out. Can anyone advise me how to fix it?
Of course, if there's a better alternative approach I'd also be glad to hear about it. I'm not yet ready to take on WPF, unfortunately.
cheers, BB
Last edited by boops boops; Apr 30th, 2009 at 06:44 PM.
-
Apr 30th, 2009, 06:51 PM
#2
Re: Problem rendering graphics with a BackgroundWorker
Are you ever setting CancellationPending to False explicitly? Not that I can see. As far as I'm aware it wouldn't get reset to False implicitly until you call RunWorkerAsync again, which you don't do until CancellationPending is False. Deadlock!
What I think you need to do is test IsBusy in that loop rather than CancellationPending.
-
Apr 30th, 2009, 08:34 PM
#3
Re: Problem rendering graphics with a BackgroundWorker
 Originally Posted by jmcilhinney
Are you ever setting CancellationPending to False explicitly?
I don't know how to do that other than by CancelAsync. CancellationPending is read only.
I tried replacing the loop by this:
Code:
Do Until ScaleImageWorker.IsBusy = False
ScaleImageWorker.CancelAsync()
Loop
but the effect was identical. Then I had another idea and tried replacing the loop by this:
Code:
ScaleImageWorker.CancelAsync()
ScaleImageWorker = New BackgroundWorker
ScaleImageWorker.WorkerSupportsCancellation = True
ScaleImageWorker.RunWorkerAsync(_ScaleFactor)
Amazingly, it does work. I wish I understood why, but at least now I can zoom. However, I am getting fairly frequent "Object is in use elsewhere" errors, mainly in the Paint event of the DrawingSurface which uses ScaledImage. I'm going to have to look at that tomorrow but you may hear me pleading for assistance again.
Thanks very much for your help so far.
regards, BB
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|