dcsimg
Results 1 to 23 of 23

Thread: Project using 2D array

  1. #1

    Thread Starter
    New Member
    Join Date
    Nov 2017
    Location
    Scotland
    Posts
    11

    Project using 2D array

    Hi, I am currently doing a project in VB 2010 where I am making a game similar to Voltorb Flip. It involves using a 2D array of face down cards containing a randomly generated number between 0 and 3. It is a 6-6 grid of these numbers however I'm trying to get the cards in the last row and column display the sum of the numbers in that row or column instead. I'm also trying to get the cards in the last row and column to display the number of zeros in that row or column as well. I managed to create a 6-6 grid where the the first 5 along and 5 down have numbers however I am having trouble with the solution for the last row and column.

    Can anyone please help me out? Thanks!

  2. #2
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    32,369

    Re: Project using 2D array

    How is this data being stored? I'd be inclined to have a single class called Grid which wrapped the array. Accessing any element of the array would be through properties of the class, which would give you a place to do things. If you just use the array directly, you can't really tell when anybody changes anything without checking the cells. Accessing the array through properties of the class would give you a chance to intercept things. Then, on any change, you could sum the rows and columns, sum the number of zeroes, or whatever you want.

    However, it also sounds like those "cards" are pretty exotic things, since they seem to hold multiple bits of information, and different bits of information depending on where they are in the grid. A class for the cards seems reasonable, but, if the amount of information is different on each, then those would be pretty strange classes.
    My usual boring signature: Nothing

  3. #3

    Thread Starter
    New Member
    Join Date
    Nov 2017
    Location
    Scotland
    Posts
    11

    Re: Project using 2D array

    Well, I stored the 2D array as a variable and populated it with randomly generated numbers between 0 and 3. I am a bit of a novice at coding so I'm not sure how to create a class. Is it possible for you to show me how to create this class so I can understand how I would go about tackling this? Thanks.

  4. #4
    You don't want to know.
    Join Date
    Aug 2010
    Posts
    4,580

    Re: Project using 2D array

    Voltorb Flip brings back memories of my time in Johto. Long, long ago.

    Let's solve this with a smaller array for convenience. A 3x3 tic-tac-toe style grid would be:
    Code:
    Private Const ROWS As Integer = 3
    Private Const COLUMNS As Integer = 3
    
    Private _grid(ROWS - 1, COLUMNS - 1) As Integer
    Roughly speaking, the first index to the array is the "row" and the second is the "column". This is a little backwards from how we usually do (x, y) coordinates, but for reasons it's just how we work with arrays. I'm saying it now not just because it's good to remember, but because I forget the order, too, and things get janky if I forget. I made variables for the numbers so some things are easier later: it's a little confusing asking a 2D array how many rows and columns it has.

    Arrays and loops go really well together, this is still true for 2D arrays. If we want to look at every cell in a particular column, we loop over all of the row indices while keeping the column value the same. Here's how we look at every cell in the 2nd column:
    Code:
    For rowIndex As Integer = 0 To ROWS - 1
        Dim cellValue As Integer = _grid(rowIndex, 1)
        ...
    If we want to look at every cell in a particular row, we loop over all of the column indices while keeping the row index the same. Here's every cell in the 3rd column:
    Code:
    For columnIndex As Integer = 0 To COLUMNS - 1
        Dim cellValue As Integer = _grid(2, columnIndex)
        ...
    So if you want to know "How many zeroes are in a column?":
    Code:
    Function HowManyZeroesInColumn(ByVal columnIndex As Integer) As Integer
        Dim zeroes As Integer = 0
    
        For rowIndex As Integer = 0 To ROWS - 1
            If _grid(rowIndex, columnIndex) = 0 Then
                zeroes += 1
            End If
        Next
    
        Return zeroes
    End Function
    Similar code for the rows and checking for other values, or operations like "sum the row" that I seem to remember are also important for Voltorb Flip.

    Now, philosophically speaking: I wouldn't put those totals in the array.

    That 2D array is your "game board". If you try to put things in it that aren't related to the board, things get kind of complicated when you start trying to count/loop. Saying "every column except the last" and "every row except the last" is harder than saying "every column" or "every row".

    So I'd keep things like "the number of zeroes in each row" in a different array:
    Code:
    Private _numberOfRowZeroes(ROWS - 1) As Integer
    If you do that, you can do the whole array in one shot if you have functions like the one I wrote above:
    Code:
    For rowIndex As Integer = 0 To ROWS - 1
        _numberOfRowZeroes(rowIndex) = HowManyZeroesInRow(rowIndex)
    Next
    Patterns like that are very common when working with arrays. You could get more sophisticated with classes and have actual Row and Column classes to help out, but you'd still write the same kinds of code I just demonstrated. Some things would just get shuffled around a little.

    Once we start thinking about classes we have a few different ways we could approach it, but for hacking out your basic game logic it might be best to stick with the Integer array then convert it to some kind of more sophisticated solution later. (But if Shaggy Hiker has any neat ideas, there's nothing wrong with starting where you're going to end up anyway!)
    This answer is wrong. You should be using TableAdapter and Dictionaries instead.

  5. #5

    Thread Starter
    New Member
    Join Date
    Nov 2017
    Location
    Scotland
    Posts
    11

    Re: Project using 2D array

    This looks really promising and it has helped me understand what I'm doing a bit better. Thanks! I'll try it out and report back later!

  6. #6

    Thread Starter
    New Member
    Join Date
    Nov 2017
    Location
    Scotland
    Posts
    11

    Re: Project using 2D array

    Ok Sitten Spynne so following your help I have managed to come with the following code so far:

    Code:
    Public Class Form1
        Dim bombgrid(5, 5) As Integer
        Dim generator As New Random
        Private Const row As Integer = 5
        Private Const column As Integer = 5
    
        Private Sub BtnStart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BtnStart.Click
            populategrid()
        End Sub
        
        Sub populategrid()
    
            For rows = 0 To bombgrid.GetUpperBound(0) - 1
                For columns = 0 To bombgrid.GetUpperBound(1) - 1
                    bombgrid(row, column) = generator.Next(0, 4)
                    TxtOutput.Text += bombgrid(row, column) & " , "
                Next
                TxtOutput.Text += ControlChars.NewLine
            Next
    
        End Sub
    
        Sub cellvalues()
    
            For rowindex As Integer = 0 To row - 1
                Dim row1cellvalue As Integer = bombgrid(rowindex, 0)
            Next
    
            For rowindex As Integer = 0 To row - 1
                Dim row2cellvalue As Integer = bombgrid(rowindex, 1)
            Next
    
            For rowindex As Integer = 0 To row - 1
                Dim row3cellvalue As Integer = bombgrid(rowindex, 2)
            Next
    
            For rowindex As Integer = 0 To row - 1
                Dim row4cellvalue As Integer = bombgrid(rowindex, 3)
            Next
    
            For rowindex As Integer = 0 To row - 1
                Dim row5cellvalue As Integer = bombgrid(rowindex, 4)
            Next
    
            For columnindex As Integer = 0 To column - 1
                Dim column1cellvalue As Integer = bombgrid(0, columnindex)
            Next
    
            For columnindex As Integer = 0 To column - 1
                Dim column2cellvalue As Integer = bombgrid(1, columnindex)
            Next
    
            For columnindex As Integer = 0 To column - 1
                Dim column3cellvalue As Integer = bombgrid(2, columnindex)
            Next
    
            For columnindex As Integer = 0 To column - 1
                Dim column4cellvalue As Integer = bombgrid(3, columnindex)
            Next
    
            For columnindex As Integer = 0 To column - 1
                Dim column5cellvalue As Integer = bombgrid(4, columnindex)
            Next
    
        End Sub
    
        Function howmanybombsincolumn(ByVal columnindex As Integer) As Integer
            Dim bombs As Integer = 0
    
            For rowindex As Integer = 0 To row - 1
                If bombgrid(rowindex, columnindex) = 0 Then
                    bombs += 1
                End If
            Next
    
            Return bombs
        End Function
    
        Function howmanybombsinrow(ByVal rowindex As Integer) As Integer
            Dim bombs As Integer = 0
    
            For columnindex As Integer = 0 To column - 1
                If bombgrid(rowindex, columnindex) = 0 Then
                    bombs += 1
                End If
            Next
    
            Return bombs
        End Function
    
        Sub displaybombs()
    
            Dim numberofrowbombs(row - 1) As Integer
            For rowindex As Integer = 0 To row - 1
                numberofrowbombs(rowindex) = howmanybombsinrow(rowindex)
            Next
    
            Dim numberofcolumnbombs(column - 1) As Integer
            For columnindex As Integer = 0 To column - 1
                numberofcolumnbombs(columnindex) = howmanybombsincolumn(columnindex)
            Next
    
        End Sub
    End Class
    So far so good right? My main question now is how I would tie all this into the array and display it. I created a 5-5 grid with randomly generated numbers between 0 and three and have coded in how to find the total of each row and column and the number of bombs(zeroes) in them too. The next thing to do would display them in the appropriate rows and columns. Any help I receive would be much appreciated since I am eager to learn from all the aid. Hope to hear back thanks!

    PS. If you see any flaws in what I've done so far I'd be happy to hear it and how to resolve it thanks.
    Last edited by Shaggy Hiker; Nov 28th, 2017 at 04:10 PM. Reason: Added CODE tags.

  7. #7
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    32,369

    Re: Project using 2D array

    I edited your post to add [CODE][/CODE] tags. You can do this yourself by pressing the # button, then pasting the code between the resulting tags. You can also paste in the code, highlight it, then press the # button. Some people prefer the VB button, as that gives you syntax coloring and line numbers.

    EDIT: Also, two simple things you can do:

    1) Go into Project | Properties, go to the Compile tab, and turn Option Strict ON. You've done pretty well here, so you won't get many errors when you do that, but the things that you'd have to change will bring a totally imperceptible benefit to the code. With Option Strict OFF (the default), you can get away with some implicit conversions, such as implicitly converting an integer to a string. Those implicit conversions are always slower than explicit conversions, but you'll only see a speed improvement if you are doing many millions of them in short order, which is quite rare. However, the other thing that lurks behind implicit conversions is bugs. Some implicit conversions will simply fail when you run the program. Therefore, you are best off turning Option Strict ON for the project (better yet, you can go into Tools | Options, and turn it on by default for all projects). You'll quickly form such habits that you won't notice that it IS on, your code will run faster (though you won't notice), and you'll avoid some annoying bugs.

    2) You are concatenating strings using +. While that works, it would probably be best if you used &. With Option Strict ON, this isn't as big an issue, but since + adds numbers as well as concatenating strings, then using + with Option Strict OFF lets the compiler decide whether you are adding or concatenating. When it makes the wrong decision, it's only you who suffers. So, & is a good habit for concatenation....until you move to any C family language, where the only concatenation operator is +....but you can leave that for another day.
    Last edited by Shaggy Hiker; Nov 28th, 2017 at 04:23 PM.
    My usual boring signature: Nothing

  8. #8
    You don't want to know.
    Join Date
    Aug 2010
    Posts
    4,580

    Re: Project using 2D array

    Display is a topic where it's hard for me to answer because I'm experienced enough with the "hard" ways that I turn my nose up at the "easy" ways.

    Again though, loops and coordinate systems are your friend.

    In 'bombgrid', (0, 0) is the coordinate of the first cell. It contains the value that tells you how that cell will be displayed.

    The only inherently grid-like control in .NET is the DataGridView. You could make one with enough rows/columns for the game board. It might be a good "first attempt", even though you probably don't want to use it for the final product.

    (0, 0) in bombgrid will be (0, 0) in the DataGridView's rows/columns. That part's fairly easy. For early testing/debugging you can just display the number as text. For things like the "row totals", well, if (0, 4) is the last cell of the first row, then (0, 5) is the cell where you put the row total. And if (4, 0) is the last cell in the first column, then (5, 0) is the one where you want to put your total.

    You could graduate from that and carefully set up a grid of Label or PictureBox controls on your form. Bonus points if you use a TableLayoutPanel, that's a little tricky but will save you some sizing issues. Something not immediately obvious in .NET is you can make an array of those Labels/PictureBoxes. The same rules with the DataGridView apply: (0, 0) through (3, 3) of your array correspond exactly to your bombgrid array. If you had a "totals" array, then (0) of the "rows" array would be (0, 4) in the array of Label/PictureBox. Etc.

    Most experts would walk down a path where they either derive a custom control or override the form's Paint event to draw the grid manually. This gives a lot of freedom and you don't have to muck about with arrays of controls. It's still basically the same logic as before, though depending on how you decide to draw you might be doing the row/column totals as part of the loop.

    So you have a few choices, arranged roughly in order from "ugliest" to "prettiest" but also "easiest" to "you need to learn a lot".
    This answer is wrong. You should be using TableAdapter and Dictionaries instead.

  9. #9
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    32,369

    Re: Project using 2D array

    I'm not sure that using the Paint event really IS all that hard. After all, Sitten, didn't you write up a tutorial for drawing a grid and items in a grid within the last week or two, for a different question? I don't know the game involved, in this case, but the concept is roughly the same....if you can remember which thread that was...
    My usual boring signature: Nothing

  10. #10

    Thread Starter
    New Member
    Join Date
    Nov 2017
    Location
    Scotland
    Posts
    11

    Re: Project using 2D array

    Ok I'll take all this in and see what I come up with. Thanks guys!

  11. #11
    You don't want to know.
    Join Date
    Aug 2010
    Posts
    4,580

    Re: Project using 2D array

    Long story short:

    I think "drawing a grid control" is just the waterslide into a bunch of other topics like "hit-testing a grid control" and I don't have the bandwidth to visit them all in a reasonable timeframe. You know me. I like to tutorialize. Some 10% of that tutorial is already written, but it took all day.

    Here's another valuable lesson though:

    "If you start with a "bad" but "functional" UI, you can finish your application a lot quicker than if you try to write a "good" UI while you're also implementing logic."

    Follow that up with:

    "Replacing the UI in a finished application is a good way to learn how miserable coupling UI and logic can make you."

    So in a sadistic way I'm going to claim it's my plan to delay the grid tutorial to teach a more valuable lesson.

    But the real reason is "I can't write a short grid tutorial, anyone else who wants to feel free." Also: only my work machine has VB available to me right now, so that severely hampers my ability to mess with code.
    This answer is wrong. You should be using TableAdapter and Dictionaries instead.

  12. #12

    Thread Starter
    New Member
    Join Date
    Nov 2017
    Location
    Scotland
    Posts
    11

    Re: Project using 2D array

    Ok so right now I am trying to populate a datagridview with the 2D array I've made and it's having issues. Here's the code i've made so far concerning this:


    Code:
    Sub populategrid()
    
            DataGridView1.ColumnCount = 5
            DataGridView1.RowCount = 5
    
            For rows = 0 To bombgrid.GetUpperBound(0) - 1
                DataGridView1.Rows.Add(row)
                For columns = 0 To bombgrid.GetUpperBound(1) - 1
                    bombgrid(row, column) = generator.Next(0, 4)
                    DataGridView1.Item(column, row).Value = bombgrid(row, column)
                    DataGridView1.Item(column, row).Style.Alignment = DataGridViewContentAlignment.MiddleCenter
                Next
            Next
        End Sub
    After running it tells me that my index was out of range and must be non-negative and less than the size of the collection. I am not sure how to fix this so any help would be appreciated thanks.


    I have also created an image and created a grid of picture boxes to represent the numbers and richtextboxes to display the clues which I've uploaded down below. The grid of picture boxes and richtextboxes I've made are an ideal UI that I'd like my game to have. Creating an array of picture boxes and binding it to a 2D array isn't something I've ever so I'm wondering if I could also receive help on this as well thanks.
    Name:  Bomb Flip form.jpg
Views: 88
Size:  20.3 KB

    PS. I am relatively new to visual basic so creating this game plus the help from you guys so far is really helping me understand the code better. I think I still have a bit to go with this game but any help I've received so far and any along the way is really appreciated. Just thought I should let you know so thanks!

  13. #13
    You don't want to know.
    Join Date
    Aug 2010
    Posts
    4,580

    Re: Project using 2D array

    The code seems a little sloppy. Part of the reason I use and recommend specific things like constants for the size of the array is they help me stop making mistakes. Here's my example, then the things that went wrong in your code:
    Code:
    Public Class Form1
    
        Private Const ROWS As Integer = 3
        Private Const COLUMNS As Integer = 3
    
        Private _data(,) As Integer = {
            {1, 2, 3},
            {4, 5, 6},
            {7, 8, 9}
        }
    
        Protected Overrides Sub OnShown(e As EventArgs)
            MyBase.OnShown(e)
    
            DataGridView1.ColumnCount = COLUMNS
            DataGridView1.RowCount = ROWS
    
            For rowIndex As Integer = 0 To ROWS - 1
                For columnIndex As Integer = 0 To COLUMNS - 1
                    DataGridView1(rowIndex, columnIndex).Value = _data(rowIndex, columnIndex)
                Next
            Next
        End Sub
    
    End Class
    Name things that are indices into arrays with the word "index". Why? Well, let's look at a line of your loop:
    Code:
    For rows = 0 To bombgrid.GetUpperBound(0) - 1
    The variable that tells you what row you are on is "rows". That word makes me think, 'How many rows are there?' I think it also confused you, because look at how you try to set the value in the array:
    Code:
    bombgrid(row, column) = generator.Next(0, 4)
    Neither "row" nor "column" are defined within this Sub. They're defined elsewhere, and mean something else. Odds are they have a value you don't expect. That's probably why you get the exception.

    Also note how much eaiser it is to say "ROWS - 1" than "bombgrid.GetUpperBound(0) - 1". I don't like remembering "the first rank is the rows". I like saying "rows" when I mean "rows". Even when I don't have a constant grid size, I prefer:
    Code:
    Dim numberOfRows = bombgrid.GetUpperBound(0)
    Novices and journeyman programmers tend to be obsessed with "not creating too many variables". Part of becoming an expert is learning your code makes the most sense when you worry you haven't created enough. "Too many variables" is like saying the sentence "boy throw ball" is superior to "Nolan Ryan threw more strikeouts than any other pitcher that year."

    Get VERY disciplined with how you name things. When you aren't careful with names, you confuse yourself. It's easier to notice this line has an issue:
    Code:
    For rowIndex = 0 To COLUMNS - 1
    Than this:
    Code:
    For i = 0 To arr.GetUpperBound(1) - 1
    This answer is wrong. You should be using TableAdapter and Dictionaries instead.

  14. #14
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    4,581

    Re: Project using 2D array

    Quote Originally Posted by EddieHoughton View Post
    ... The grid of picture boxes and richtextboxes I've made are an ideal UI that I'd like my game to have. Creating an array of picture boxes and binding it to a 2D array isn't something I've ever so I'm wondering if I could also receive help on this as well thanks.
    ...
    There are many ways to go about it.
    If it were me, and I wanted to used pictureboxes, I would probably create the pictureboxes in code and add handlers.
    But if you already have the picturebox controls created in the IDE, you can collect references to them in an array. I would normally probably just use a single dimensioned array and convert 2d indexes into linear indexes as needed.

    But as an example of one way of loading reference to pictureboxes you have already added to your form in a 2d array, I took a few minutes to create a very basic implementation of the "Lites Out" game. If you're not familiar with the game, you had a device with a 5x5 matrix of buttons that can light up. A pattern of lit buttons would be presented, and you press buttons to try to get all the lights to go out within in some limited number of button presses. If you got the lights out within the allowable range of presses, you advanced to a more complicated puzzle (the minimum number of buttons that had to be pressed increased).
    Since this is just a quick example, it just generates a random valid puzzle, without knowledge of degree of difficulty.

    When you press a button, the light toggles for the button clicked, and the buttons above and below, and to the left and right. It doesn't wrap around the edges.

    The code creates a simple class to keep track of the row and column where the picturebox is located and the tag property of the picturebox is set to an instance of that class so that a click on a particular picturebox can be converted to which row and column it is in easily and the other pictureboxes that are horizontal and vertical neighbors can be easily determined and toggled as well.
    A common event handler is used for all 25 pictureboxes.

    You may have noted above that I said "valid puzzle". If you just randomly toggled the color of a number of pictureboxes, the chances are 75% that the puzzle would be unsolvable. Only 25% of the possible patterns that could be created can have the lights all toggled off based on the toggling rules. To ensure the puzzle is valid, you have to follow the toggling rules when creating the puzzle, which is why there is a number of calls to the ProcessClick sub to generate the puzzle, rather than calls to ToggleColor.

    To try the example, place 25 pictureboxes in a 5x5 matrix on the form in the order
    1,2,3,4,5
    6,7,8,9,10
    11,12,13,14,15
    16,17,18,19,20
    21,22,23,24,25

    Place a button on the form for generating a puzzle.
    Paste in the code below, and run.
    Code:
    Public Class Form1
    
      Private board(,) As PictureBox
      Private rand As New Random
    
      Private Class RowColumn
        Public Row As Integer
        Public Col As Integer
    
        Public Sub New(ByVal theRow As Integer, ByVal theCol As Integer)
          Row = theRow
          Col = theCol
        End Sub
      End Class
    
      Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        board = {{PictureBox1, PictureBox2, PictureBox3, PictureBox4, PictureBox5},
                 {PictureBox6, PictureBox7, PictureBox8, PictureBox9, PictureBox10},
                 {PictureBox11, PictureBox12, PictureBox13, PictureBox14, PictureBox15},
                 {PictureBox16, PictureBox17, PictureBox18, PictureBox19, PictureBox20},
                 {PictureBox21, PictureBox22, PictureBox23, PictureBox24, PictureBox25}}
        Dim row, col As Integer
        For col = 0 To 4
          For row = 0 To 4
            board(row, col).BackColor = Color.White
            board(row, col).Tag = New RowColumn(row, col)
          Next
        Next
    
      End Sub
    
      Private Sub toggleColor(ByVal pic As PictureBox)
        If pic.BackColor = Color.White Then
          pic.BackColor = Color.Green
        Else
          pic.BackColor = Color.White
        End If
      End Sub
    
      Private Sub PictureBox_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles PictureBox9.Click, PictureBox8.Click, PictureBox7.Click, PictureBox6.Click, PictureBox5.Click, PictureBox4.Click, PictureBox3.Click, PictureBox25.Click, PictureBox24.Click, PictureBox23.Click, PictureBox22.Click, PictureBox21.Click, PictureBox20.Click, PictureBox2.Click, PictureBox19.Click, PictureBox18.Click, PictureBox17.Click, PictureBox16.Click, PictureBox15.Click, PictureBox14.Click, PictureBox13.Click, PictureBox12.Click, PictureBox11.Click, PictureBox10.Click, PictureBox1.Click
        ProcessClick(DirectCast(sender, PictureBox))
      End Sub
    
      Private Sub ProcessClick(ByVal pb As PictureBox)
        Dim rc As RowColumn = DirectCast(pb.Tag, RowColumn)
    
        Dim row, col As Integer
    
        'toggle the color of the picturebox clicked
        toggleColor(pb)
    
        'toggle the color of the picturebox to the left and right of the box clicked
        row = rc.Row
        col = rc.Col - 1
        If col >= 0 Then toggleColor(board(row, col))
        col = rc.Col + 1
        If col < 5 Then toggleColor(board(row, col))
    
        'toggle the color of the picturebox above and below the one clicked
        col = rc.Col
        row = rc.Row - 1
        If row >= 0 Then toggleColor(board(row, col))
        row = rc.Row + 1
        If row < 5 Then toggleColor(board(row, col))
      End Sub
    
      Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        generatePuzzle()
      End Sub
    
      Private Sub generatePuzzle()
        For i As Integer = 1 To 25
          ProcessClick(board(rand.Next(5), rand.Next(5)))
        Next
      End Sub
    End Class
    Last edited by passel; Dec 3rd, 2017 at 06:30 PM.

  15. #15

    Thread Starter
    New Member
    Join Date
    Nov 2017
    Location
    Scotland
    Posts
    11

    Re: Project using 2D array

    Ok I'll take on that on board and see what I come up with thanks!

  16. #16

    Thread Starter
    New Member
    Join Date
    Nov 2017
    Location
    Scotland
    Posts
    11

    Re: Project using 2D array

    How would I bind my grid of picture boxes to my 2-D array so that each picture box represents a number in the 2-D array? I get a data conversion error whenever I try so i'm asking if there's a solution to this? Thanks

  17. #17
    You don't want to know.
    Join Date
    Aug 2010
    Posts
    4,580

    Re: Project using 2D array

    Well, the answer is more complicated than you might want.

    Any kind of data binding requires a feature called "change notification". Like the name implies, that means the things being bound need to be able to tell each other when they change.

    Arrays are primitives and don't support any kind of "change notification". Windows Forms controls don't always have built-in binding. When they do, they perform their binding via a set of interfaces like IBindingSource that are quite difficult to implement.

    In WPF, where there's an easier data binding infrastructure, you could consider the ObservableCollection(Of T) type. It's like an array but has change notification. You can make something like a 2D array by making an ObservableCollection(Of T) that contains other instances of ObservableCollection(Of T). That's pretty clunky, too.

    You could maybe use a DataTable, but that comes with a host of other problems. Upside: it's a 2D grid data structure designed to support WinForms binding. Downside: it technically only holds Object which means a little more work when you're getting things out of it. I have an almost-irrational hatred of DataTable and won't consider it, particularly since I don't think it has a direct binding scenario like you want anyway.

    Me, I'd make a wrapper class that does its own change notification. Then I'd add a little bit of glue in the Form to pull off the "binding". Binding in WinForms always has sarcasm quotes like that. Outside of some cases designed around databases and text-entry controls like TextBox, you're almost always doing most of the work to support binding on your end.

    So the trick is to start by elevating your "array" into a more proper "grid":
    Code:
    Public Class Grid
    
        Private _grid(,) As Integer
    
        Public ReadOnly Property Columns As Integer
        Public ReadOnly Property Rows As Integer
    
        Public Sub New(ByVal rows As Integer, ByVal columns As Integer)
            Me.Columns = columns
            Me.Rows = rows
    
            ReDim _grid(rows - 1, columns - 1)
        End Sub
    
        Public Function GetValue(row As Integer, column As Integer)
            Return _grid(row, column)
        End Function
    
        Public Sub SetValue(row As Integer, column As Integer, value As Integer)
            _grid(row, column) = value
        End Sub
    End Class
    You use that where you used to use the array. It's important that all changes happen through SetValue(), because that's how it tells the Form what changed. To get there, we need to add an event. It will be nice to tell the form WHICH element changed, so let's make a new EventArgs-based class to do that:
    Code:
    Public Class GridChangedEventArgs
        Inherits EventArgs
    
        Public ReadOnly Property Column As Integer
        Public ReadOnly Property Row As Integer
    
        Public Sub New(row As Integer, column As Integer)
            Me.Column = column
            Me.Row = row
        End Sub
    
    End Class
    With that, adding change notification to the grid is trivial. This code only has the changes:
    Code:
    Public Class Grid
    
        ' [...]
    
        Public Sub SetValue(row As Integer, column As Integer, value As Integer)
            _grid(row, column) = value
            RaiseEvent GridChanged(Me, New GridChangedEventArgs(row, column))
        End Sub
    
        Public Event GridChanged As EventHandler(Of GridChangedEventArgs)
    End Class
    Now the Form has to add an event handler when it creates the Grid:
    Code:
    _myGrid = New Grid(Rows, Columns)
    AddHandler _myGrid.GridChanged, AddressOf WhenGridChanges
    And the handler might look like:
    Code:
    Sub WhenGridChanges(sender As Object, e As GridChangedEventArgs)
        Dim newValue = _myGrid.GetValue(e.Row, e.Column)
        Dim newImage = <get the image for that value>
        <set the image at e.Row, e.Column to be newImage>
    End Sub
    Speaking honestly, it's easier to update the image as you change the value if your project is small and lives inside one form. As you can see, separation and binding are tools that take a little bit of extra work. We use them as projects get more complex because in larger projects, isolation helps us stop changing one thing from breaking half the project.
    Last edited by Sitten Spynne; Jan 2nd, 2018 at 10:45 AM.
    This answer is wrong. You should be using TableAdapter and Dictionaries instead.

  18. #18

    Thread Starter
    New Member
    Join Date
    Nov 2017
    Location
    Scotland
    Posts
    11

    Re: Project using 2D array

    By taking your code, I now have this:
    Name:  Bomb Flip code.jpg
Views: 46
Size:  26.8 KB

    Name:  Bomb Flip Code2.jpg
Views: 44
Size:  24.5 KB

    Can you provide me with the correct structure and explain to me how you reached your solution please? Thanks.

  19. #19
    You don't want to know.
    Join Date
    Aug 2010
    Posts
    4,580

    Re: Project using 2D array

    Always post text when asking questions about code. It's much, much harder to take a screenshot and upload it, and these forums are engineered resize images so it's impossible to read a screenshot of more than about 3 lines of text.

    I think the last paragraph of my post that said "it's not worth it" is the most important paragraph.
    This answer is wrong. You should be using TableAdapter and Dictionaries instead.

  20. #20

    Thread Starter
    New Member
    Join Date
    Nov 2017
    Location
    Scotland
    Posts
    11

    Re: Project using 2D array

    Sorry about that, I thought that using a screenshot would help you identify the errors better. Here is what I've put down:

    Code:
    Public Class grid
    
        Private bombgrid(,) As Integer
        Public ReadOnly Property Columns As Integer
            Get
    
            End Get
        End Property
        Public ReadOnly Property Rows As Integer
            Get
    
            End Get
        End Property
    
        Public Sub New(ByVal rows As Integer, ByVal columns As Integer)
            Me.Columns = columns
            Me.Rows = rows
    
            ReDim bombgrid(rows - 1, columns - 1)
        End Sub
    
        Public Function GetValue(ByVal row As Integer, ByVal column As Integer)
            Return bombgrid(row, column)
        End Function
    
        Public Sub SetValue(ByVal row As Integer, ByVal column As Integer, ByVal value As Integer)
            bombgrid(row, column) = value
            RaiseEvent GridChanged(Me, New GridChangedEventArgs(row, column))
        End Sub
    
            Public Event GridChanged As EventHandler(Of GridChangedEventArgs
    
        Private Sub grid_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    
        End Sub
    End Class
    Private Sub grid_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        _mygrid = New grid(rows, Columns)
        AddHandler _mygrid.GridChanged, AddressOf WhenGridChanges
    End Sub
    Sub WhenGridChanges(ByVal sender As Object, ByVal e As GridChangedEventArgs)
        Dim NewValue = _mygrid.GetValue(e.Row, e.Column)
        Dim NewImage = <get the image for that value>
                           <set the image at e.row, e.column to be newImage>
    
                           </set>
                       </get>
    End Sub
    Public Class GridChangedEventArgs
        Inherits EventArgs
    
        Public ReadOnly Property Column As Integer
            Get
    
            End Get
        End Property
        Public ReadOnly Row As Integer
    
        Public Sub New(ByVal row As Integer, ByVal column As Integer)
            Me.Column = column
            Me.Row = row
        End Sub
    
    End Class
    Public Class gridclues
        Private Sub BtnStart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BtnStart.Click
    
        End Sub
        Sub cellvalues()
            For rowindex As Integer = 0 To rows - 1
                Dim row1cellvalue As Integer = bombgrid(rowindex, 0)
            Next
    
            For rowindex As Integer = 0 To rows - 1
                Dim row2cellvalue As Integer = bombgrid(rowindex, 1)
            Next
    
            For rowindex As Integer = 0 To rows - 1
                Dim row3cellvalue As Integer = bombgrid(rowindex, 2)
            Next
    
            For rowindex As Integer = 0 To rows - 1
                Dim row4cellvalue As Integer = bombgrid(rowindex, 3)
            Next
    
            For rowindex As Integer = 0 To rows - 1
                Dim row5cellvalue As Integer = bombgrid(rowindex, 4)
            Next
    
            For columnindex As Integer = 0 To columns - 1
                Dim column1cellvalue As Integer = bombgrid(0, columnindex)
            Next
    
            For columnindex As Integer = 0 To columns - 1
                Dim column2cellvalue As Integer = bombgrid(1, columnindex)
            Next
    
            For columnindex As Integer = 0 To columns - 1
                Dim column3cellvalue As Integer = bombgrid(2, columnindex)
            Next
    
            For columnindex As Integer = 0 To columns - 1
                Dim column4cellvalue As Integer = bombgrid(3, columnindex)
            Next
    
            For columnindex As Integer = 0 To columns - 1
                Dim column5cellvalue As Integer = bombgrid(4, columnindex)
            Next
        End Sub
        Function howmanybombsincolumn(ByVal columnindex As Integer) As Integer
            Dim bombs As Integer = 0
    
            For rowindex As Integer = 0 To rows - 1
                If bombgrid(rowindex, columnindex) = 0 Then
                    bombs += 1
                End If
            Next
    
            Return bombs
        End Function
        Function howmanybombsinrow(ByVal rowindex As Integer) As Integer
            Dim bombs As Integer = 0
    
            For columnindex As Integer = 0 To columns - 1
                If bombgrid(rowindex, columnindex) = 0 Then
                    bombs += 1
                End If
            Next
    
            Return bombs
        End Function
        Sub displaybombs()
            Dim numberofrowbombs(rows - 1) As Integer
            For rowindex As Integer = 0 To rows - 1
                numberofrowbombs(rowindex) = howmanybombsinrow(rowindex)
            Next
    
            Dim numberofcolumnbombs(columns - 1) As Integer
            For columnindex As Integer = 0 To columns - 1
                numberofcolumnbombs(columnindex) = howmanybombsincolumn(columnindex)
            Next
        End Sub
    End Class
    Also, what class would the handlers belong to since right now they are not in a valid namespace?

  21. #21
    You don't want to know.
    Join Date
    Aug 2010
    Posts
    4,580

    Re: Project using 2D array

    It looks like you got everything kind of mixed up and pasted everything into your Form? It also looks like you added some new code inside the Grid class. Let's be clear.

    1. This is not a thing you need to make the project work. It would be much easier to update things as you make changes.
    2. But if you want to persist:

    Your Form is one class in its own file. I'm going to call it MainForm.

    Grid is its own class in its own file. You have to glue together two of my samples to make the "final" version, this was done to help you understand "what it does" separate from "how it does change notification".

    GridChangedEventArgs is its own class in its own file.

    Grid is a substitute for the 2D array. So after you add Grid and GridChangedEventArgs, you have to edit your form to stop using the array and start using the Grid class instead. Part of that involves adding the event handlers I talked about. They go in the form.
    This answer is wrong. You should be using TableAdapter and Dictionaries instead.

  22. #22
    You don't want to know.
    Join Date
    Aug 2010
    Posts
    4,580

    Re: Project using 2D array

    Here's a real quick example. Remember, Grid and GridChangedEventArgs need to be in their own file.

    There's a tiny error in my Grid class as posted. I just editecd it to fix it. The problem was I used the wrong name for something in Sub New. If you've copy/pasted the code, make sure to update it!

    Let's say we have a Form, and on that form Label1 is meant to represent the grid square at (0, 0). I'm going to add a Button1 that will change the value of that Grid square. I'm also going to add a Button2 that updates a different square, so we can make sure binding is only looking at the right thing. Here's what the Form's code for that looks like:
    Code:
    Public Class Form1
    
        Private Const ROWS As Integer = 3
        Private Const COLUMNS As Integer = 3
    
    
        Private _grid As Grid
    
        Public Sub New()
            InitializeComponent()
    
            _grid = New Grid(ROWS, COLUMNS)
            AddHandler _grid.GridChanged, AddressOf WhenGridChanges
            UpdateBindings(0, 0)
        End Sub
    
        Private Sub UpdateBindings(ByVal row As Integer, ByVal column As Integer)
            If row = 0 AndAlso column = 0 Then
                Label1.Text = _grid.GetValue(0, 0).ToString()
            End If
        End Sub
    
        Private Sub WhenGridChanges(ByVal sender As Object, ByVal e As GridChangedEventArgs)
            UpdateBindings(e.Row, e.Column)
        End Sub
    
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            Dim oldValue As Integer = _grid.GetValue(0, 0)
            _grid.SetValue(0, 0, oldValue + 1)
        End Sub
    
        Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
            Dim oldValue As Integer = _grid.GetValue(1, 1)
            _grid.SetValue(1, 1, oldValue + 1)
        End Sub
    
    End Class
    I feel like in the larger picture UpdateBindings() would have a version that updates everything, but this is scaled down for the example.
    This answer is wrong. You should be using TableAdapter and Dictionaries instead.

  23. #23
    Frenzied Member ChrisE's Avatar
    Join Date
    Jun 2017
    Location
    Frankfurt
    Posts
    1,217

    Re: Project using 2D array

    Quote Originally Posted by Sitten Spynne View Post

    You could graduate from that and carefully set up a grid of Label or PictureBox controls on your form. Bonus points if you use a TableLayoutPanel, that's a little tricky but will save you some sizing issues. Something not immediately obvious in .NET is you can make an array of those Labels/PictureBoxes. The same rules with the DataGridView apply: (0, 0) through (3, 3) of your array correspond exactly to your bombgrid array. If you had a "totals" array, then (0) of the "rows" array would be (0, 4) in the array of Label/PictureBox. Etc.
    @Sitten Spynne
    just played around with the 'TableLayoutPanel'

    Code:
    Public Class Form3
    
        Private numberOfCards As Integer = 30I
        Private CardLayout As New FlowLayoutPanel With {.FlowDirection = FlowDirection.LeftToRight, .AutoScroll = True, .Dock = DockStyle.Fill}
        Private TableLayoutPanel1 As New TableLayoutPanel With {.Dock = DockStyle.Fill, .ColumnCount = 1, .RowCount = 2}
    
    
        Private Sub Cards_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
    
            For i As Integer = 1I To numberOfCards
                Dim mCards As New Label With {.Text = "Card " & i, _
                                            .Tag = i, _
                                            .Width = 35, _
                                            .BorderStyle = BorderStyle.Fixed3D, _
                                            .Enabled = True, _
                                            .Height = 40, _
                                            .BackColor = Color.SandyBrown}
    
                Me.CardLayout.Controls.Add(mCards)
            Next
            Me.TableLayoutPanel1.ColumnStyles.Add(New ColumnStyle(SizeType.Percent, 100.0!))
            Me.TableLayoutPanel1.RowStyles.Add(New RowStyle(SizeType.Percent, 100.0!))
            Me.TableLayoutPanel1.RowStyles.Add(New RowStyle())
            Me.TableLayoutPanel1.Controls.Add(Me.CardLayout, 0I, 0I)
            Me.Controls.Add(Me.TableLayoutPanel1)
        End Sub
    
    End Class
    regards
    Chris
    Last edited by ChrisE; Jan 2nd, 2018 at 12:53 PM.
    to hunt a species to extinction is not logical !
    since 2010 the number of Tigers are rising again in 2016 - 3900 were counted. with Baby Callas it's 3901, my wife and I had 2-3 months the privilege of raising a Baby Tiger.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Featured


Click Here to Expand Forum to Full Width