Drawing to a panel when clicking a button?

diabolos

Newcomer
Joined
Jan 23, 2009
Location
Tennessee, USA
I apologize if this is in the wrong place; however, I need some assistance.

I am a student at ITT Tech and I had to write my own version of Conway's Game Of Life program. I got it working, I used a listbox and used the words dead or alive to represent the cells. NOW, I would like to use red and green boxes in a grid that are drawn on a panel.
I have been messing with this for a roughly week now. I have a basic idea but I just cant figure out how to get the panel to REDRAW the new boxes when I click my Seed button.

If you need more information please ask, I will be glad to provide any info.


Code:
Public Class Form1

    Dim seed As Double
    Dim num As Double
    Dim MAX = 52
    Dim cells1(MAX, MAX) As Boolean
    Dim cells2(MAX, MAX) As Boolean
    Dim currentRow As String


    Private Sub btnSeed_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnSeed.Click
        If Not IsNumeric(txtGridSize.Text) Or Not IsNumeric(txtSeed.Text) Then
            MessageBox.Show("Please Enter a numerical value into BOTH Grid size AND Seed", "ERROR!", MessageBoxButtons.OK)
            txtGridSize.Clear()
            txtSeed.Clear()
            txtGridSize.Focus()

        Else
            MAX = CInt(txtGridSize.Text)
            ReDim cells1(MAX, MAX)

            Try
                seed = CInt(txtSeed.Text)
                Randomize(seed)
            Catch ex As Exception
                MessageBox.Show("An exception occurred:" & vbCrLf & ex.Message & " : Please enter a number between -2,147,483,648 and 2,147,483,647", "Uh Oh!", MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Error)
            End Try

            ' this will start the loop for populating the cells randomly
            For X = 0 To MAX
                For Y = 0 To MAX
                    num = Rnd()
                    If (num <= 0.5) Then
                        cells1(X, Y) = False
                    Else
                        cells1(X, Y) = True
                    End If
                Next Y
            Next X
        End If

    End Sub

    
    Private Sub Panel1_Paint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Panel1.Paint
        For x As Integer = 0 To MAX
            For y As Integer = 0 To MAX
                e.Graphics.DrawRectangle(Pens.Black, 50 + x * 10, 50 + y * 10, 10, 10)
                If cells1(x, y) = False Then
                    e.Graphics.FillRectangle(Brushes.Red, 50 + x * 10 + 1, 50 + y * 10 + 1, 9, 9) ' these parameters are set to SHOW the gridlines
                Else
                    e.Graphics.FillRectangle(Brushes.Green, 50 + x * 10 + 1, 50 + y * 10 + 1, 9, 9) ' these parameters are set to SHOW the gridlines
                End If
            Next y
        Next x

        Panel1.Invalidate()

    End Sub
End Class
 
Last edited:

snarfblam

Ultimate Contributor
Joined
Jun 10, 2003
Location
USA
The first thing I see wrong is that you are using the Form's paint event to handle painting for the panel. Overriding the Form1.OnPaint method is equivalent to handling the Form's Paint event, but you don't want to draw on the form, you want to draw on the panel. The code in your OnPaint method should be moved to Panel1_Paint. And just a note: whenever you override any OnSomething method, you need to call the overridden (base) method. In other words, you would need to call MyBase.OnPaint(e) in the OnPaint method. Otherwise some event handlers might not be called.

As for your problem of getting things to be redrawn, the important concept here is invalidation. The Paint event (or OnPaint method) is responsible for doing the drawing, but it needs to be told when to draw. This is done by invalidation. You want to call the Panel1.Invalidate method to tell it that part of the control is invalid. For example, suppose the panel were covered by another window, and then that window is dragged out of the way. The panel now needs to be redrawn. It has graphical leftovers on it from the window that was dragged away, and is therefore invalid. In that case, Windows will automatically invalidate your panel for you. In other words, "invalidating" a control simply means informing it that it needs to be redrawn.

If your program's data changes, and that change needs to be shown on the control, you need to invalidate the panel yourself, by calling Panel1.Invalidate.
 

diabolos

Newcomer
Joined
Jan 23, 2009
Location
Tennessee, USA
I see, Thank you for taking the time to sort through this. I really appreciate your feedback! :eek:
I will try what you suggested and get back to you when I find out if this is going to work for me.

Thank you so much!
 

diabolos

Newcomer
Joined
Jan 23, 2009
Location
Tennessee, USA
Ok, so I did as you said; however, I have another question.
How would I perform a call to panel1 to have it paint from the press of a button in form1?

Why does the panel1_paint subroutine get called continuously when I run my program? is there a way to run my program and have it show nothing until I click my seed button to draw the grid and such? I am only assuming it is in a loop because I tried to put the panel1.invalidate() in there and it just flickers forever :)
I am sorry, I am better with C++, and Java :)

Let me give you a better idea of what I want to accomplish here:

1.) Upon running the program, the controls are all visible and the panel HAS NOT been populated with any drawing of any kind YET...
2.) When I enter the grid size and seed value into their respective boxes, I will hit the "Go!" button. This should randomly generate Boolean values throughout the multidimensional array cells1 and then call the paint method to populate the panel1 with a grid size that the user entered and fill the boxes of each grid with red boxes (if false) and green boxes(if true).

NOTE: this is where I am stuck; I have not implemented anything else yet until I can figure out the painting part. The following is what I wish to accomplish after the preceding part is completed

3.) Create a button to "step" through each generation of life of the cells according to the rules of John Conway's game of life.
4.) Each time the step button is pressed, the rules are applied and this could change the values of each cell, thus needing them to be repainted and displayed to the user.


Thanks for your time and patience!

P.S.
I edited my original post to show what my code looks like now.
 
Last edited:

MrPaul

Contributor
Joined
Jun 28, 2006
Location
Hampshire, UK
Call panel1.Invalidate() when needed

I am only assuming it is in a loop because I tried to put the panel1.invalidate() in there and it just flickers forever :)

You put Panel1.Invalidate() in the paint event handler? Each time you call Panel1.Invalidate(), the paint event will be called and the panel will be redrawn.

You should only be calling Panel1.Invalidate() when you want to redraw the panel - it sounds like this will be in two places:

  1. In the event handler for your "Go!" button (after populating the boolean values)
  2. In the event handler for your "step" button (after updating the values)

However, the paint event could be called at any time, for example when the form is shown or when a window which was obscuring the panel is moved away. To compensate for this, your paint event handler needs to check whether "Go!" has been clicked.

For example:

Visual Basic:
Dim goPressed As Boolean = False

Private Sub Panel1_Paint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Panel1.Paint
    If (goPressed) Then
        'Draw game state
    End If
End Sub

Private Sub btnSeed_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnSeed.Click
    'Populate values

    'Draw panel
    goPressed = True
    Panel1.Invalidate()
End Sub

Private Sub btnStep_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStep.Click
    'Update values

    'Redraw panel
    Panel1.Invalidate()
End Sub

Good luck :cool:
 

diabolos

Newcomer
Joined
Jan 23, 2009
Location
Tennessee, USA
Re: Call panel1.Invalidate() when needed

Beautiful, Thank you so much for your help!

It works perfectly!

Cheers!

I figured out how to get rid of the flickering by double buffering the main form and just getting rid of the panel and placing the code in a different sub. I added a handle MyBase.paint to the sub and now, no more flickering :) it is a beautiful thing :)
thanks alot for all the help! :)
 
Last edited:
Top Bottom