Infinite Redraw Loop with "Program not Responding" Message

ibby

Member
Joined
Jul 30, 2011
Messages
7
Programming Experience
3-5
Hello Community,

I have an urgent problem to which I would appreciate quick replies. I have written a program for a school project which graphs mathematical equations. Within Visual Studio (in debug mode) everything works perfectly. However, when I publish the program and run it as an installed application, I get the following problem:

If the user enters a complicated function, the prorgam can take considerable time (i.e. about 10 seconds or more) to compute its graph. The graph is first generated in memory on a bitmap and then printed onto a control using GDI+. The problem is that sometimes windows indicates that the program has stopped working while the graph is still being computed, which of course is not the case - its just taking a long time. When the computation actually finishes, the program window flashes, probably redrawing itself, and that causes the graph to be computed again. This in turn results in Windows thinking the program crashed again and so the loop continuous. I really have no idea what is causing windows to think that the program is not responding, as it works perfectly in Visual Studio... I am using Windows 7 and .Net 4. I will of course give any other details/coding required to solve this problem. I am quite frustrated as I have to hand the program in soon :bawling:....

Thanks for helping out!
 
For a best reply, put code here to check,

Try threading as Herman suggests. check for window.paint events etc, so many places to look. need code
 
Thanks a lot Herman and SLPx. I have had a quick look at My.Application.DoEvents on MSDN and it look promising, but I am not entirely sure how to implement that. Also, I have never worked with multiple threads so I might need a bit of help with that. As requested, here is the coding mainly responsible for drawing the graphs to the screen. I will try to explain as I go along:

Firstly, I have implemented a class called 'GraphPaper' that inherits from System.Windows.Forms.UserControl. That class has methods to draw border, axes, grid lines, labels and of course graphs of equations. This class has a private sub called 'Draw' which handles MyBase.Paint:

    Private Sub Draw(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint
        ' This If-statement just ensures that the drawing mechanism isn't currently disabled.
        If Not Me.DrawingLocked Then
            Dim PaperGraphics As System.Drawing.Graphics = e.Graphics
            PaperGraphics.Clear(Me.BackColor)

            ' Draw Border, Grid and Axes:
            Border(PaperGraphics)
            Grid(PaperGraphics)
            Axes(PaperGraphics)

            ' Within this Sub, another event called 'Graphing' is raised (see below for implementation).
            ' The Main window has an instance of this control and listens to its Graphing event.
            ' When the event is fired, the form repeatedly calls the GraphPaper's Public 'GraphEquation' sub, passing to it, one by one, all the equations the user entered.
            ' This sub graphs each equation and after that the execution of the program jumps back to this point.
            ' (It is these calls to 'GraphEquation' that take the most time to complete and cause the problem.
            RaiseEvent Graphing(Me, System.EventArgs.Empty)

            ' Redraw the Axes in case they are hidden behind a big graph and finally add labels.
            Axes(PaperGraphics)
            Labels(PaperGraphics)
        End If
    End Sub


Here is the custom event declaration for the event 'Graphing'. The only reason I have a custom event rather than just a simple ordinary event is that the graphing equations mechanism can also be individually locked either by the user or internally by the class. This is sometimes required but shouldn't make a difference to the main problem I have:

    Public Delegate Sub GraphingEventHandler(ByVal sender As Object, ByVal e As System.EventArgs)
    Public Custom Event Graphing As GraphingEventHandler
        AddHandler(ByVal Value As GraphingEventHandler)
            GraphingHandlers = CType([Delegate].Combine(GraphingHandlers, Value), GraphingEventHandler)
        End AddHandler
        RemoveHandler(ByVal Value As GraphingEventHandler)
            GraphingHandlers = CType([Delegate].Remove(GraphingHandlers, Value), GraphingEventHandler)
        End RemoveHandler
        RaiseEvent(ByVal sender As Object, ByVal e As System.EventArgs)
            ' This is the If-statement that verifies that this event should indeed be raised because it isn't in the locked state.
            If Not (Me.GraphingLocked Or Me.InternalGraphingLocked) Then
                If GraphingHandlers IsNot Nothing Then
                    GraphingHandlers.Invoke(sender, e)
                End If
            End If
        End RaiseEvent
    End Event


Here now is the code in the main form that listens to the 'Graphing' Event of the GraphPaper instance:

    ' The name of the GraphPaper instance on the form is 'MyGraphPaper'.
    Private Sub GraphEquations(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyGraphPaper.Graphing
        ' 'EquationPanels' are just the control that the user enters an equation into. All the EquationPanels are located in 'MySplitContainer.Panel1'.
        ' This list therefore just extracts all the EquationPanels from 'MySplitContainer.Panel1'.
        Dim EquationPanelList As New List(Of EquationPanel)(MySplitContainer.Panel1.Controls.OfType(Of EquationPanel))

            ' All this just takes care of indicating to the user that the program is currently Graphing equations.
            Me.Cursor = Cursors.WaitCursor
            CoordXToolStripStatusLabel.Visible = False
            CoordYToolStripStatusLabel.Visible = False
            GraphingToolStripStatusLabel.Visible = True

            ' Unfortunatelly, I have to refresh the form before the changes I made in the previous four lines take effect.
            ' But of course doing so would refresh MyGraphPaper as well and we'd be in another redrawing loop.
            ' That is why I lock MyGraphPaper before refreshing and unlock it afterwards.
            ' I know this is probably really bad programming. I am ready to remove the previous four lines and the next three lines if that makes it any easier.
            MyGraphPaper.LockGraphing()
            Me.Refresh()
            MyGraphPaper.UnlockGraphing()

            ' Finally, call 'MyGraphPaper.GraphEquation' for each equation the user entered.
            ' (There is a technical reason to do it in reverse order, namely to graph the equations in the same order they were entered by the user)
            For I = EquationPanelList.Count - 1 To 0 Step -1
                If EquationPanelList(I).Graph.IsValid and EquationPanelList(I).Graph.IsActive Then
                    MyGraphPaper.GraphEquation(EquationPanelList(I).Graph)
                End If
            Next

            ' This changes the cursor back and stops indicating that the program is currently graphing equations.
            GraphingToolStripStatusLabel.Visible = False
            CoordXToolStripStatusLabel.Visible = True
            CoordYToolStripStatusLabel.Visible = True
            Me.Cursor = Cursors.Arrow
    End Sub


And that should be all the relevant code that causes the problem. I have attached a picture to show you the GUI - that might help you to understand how the program is structured. I know this is a long post but I any help would be really appreciated! Thanks!
 

Attachments

  • GraphiteEdited.png
    GraphiteEdited.png
    94.9 KB · Views: 48
Ok. There are few things possible in this case

1. Why you use the 'Draw' Method to shadow the paint event. Use a shell method to wrap the paint event and call from it the Draw method, if you need to draw the graph
2. In draw method, you are drawing axes(twice), if it not a must, draw axes last making it one time call
3. In graph equation you can use multiple threads to draw graphs simultaneously, unless they use common variables (Threading) making the execution speed
4. Instead of using the full graph rendering, try using with 'cliprectangle ' every time you need to render a graph portion on refresh
5. You inherently using threading in your app by using the keywords delegate and invoke. Its the same thing in threading.
6. if you want to avoid a portion of the programme should not run, use 'static' variable instead DIM variable and hold your last value there

other than that, I wont see a technical outflow here. Raising events in a chain is tricky.
anyway drawing in a canvas and keep it cached in a BMP or memory while redrawing only necessary portions will make it ok.
draw each graph as a layer in several BMP files or memory using threading and superimpose them one over along to the users input order

In additionally and if possible compute the graph function part by part over threading for each separable segments, functions can be segmented over + and - signs, trigonometric functions, etc


Check .Net framework is OK in your target platform, Thats all I can say for this.
 
Last edited:
Hey

Thanks for your tips, I do agree that there is a lot of room for improvement in my code (this is the first time I have written such a big program and I learned a lot from it as I went along). Unfortunatelly I do not have the time to make major changes now. However, a big Thanks to Herman for pointing me towards Application.DoEvents() - I used that in my graphing subroutines and everything works fine now! Moreover, I actually understood why that resolved the problem :) Sure, Multithreading would have been better but I have never done that ...

Thanks again to both of you!
 
Back
Top