Results 1 to 9 of 9

Thread: A lightweight Flexible replacement for a 2D array.

  1. #1

    Thread Starter
    Frenzied Member Gruff's Avatar
    Join Date
    Jan 2014
    Location
    Scappoose Oregon USA
    Posts
    1,293

    A lightweight Flexible replacement for a 2D array.

    I am reconstructing an ancient app someone wrote in VB6.
    The old program used an MSHFlexgrid to construct a report for the windows clipboard.

    The output is usually pasted into Excel.

    My issue is that A) It seems silly to use a graphical control just to prep and store the report and B) The MSHFlexgrid is not fully functional in VB.NET these days.

    To that end I decided to create my own 2D String class that supports some of the functionality of the MSFlexgrid. 2D arrays are too rigid and I assume datatables are overkill.

    The following is the first thing I came up with. It works but could use improvement.
    Any Ideas? Am I reinventing the wheel? Does something exist that would work better?

    Code:
    Public Class clsGridOfString
      Private Property Lattice As New List(Of List(Of String))
      Public Property ColumnCount As Integer = 0
      Public Property RowCount As Integer = 0
    
      Default Public Property Cell(RowIndex As Integer, ColumnIndex As Integer) As String
        Get
          Return Lattice(RowIndex).Item(ColumnIndex)
        End Get
        Set(value As String)
          Lattice(RowIndex).Item(ColumnIndex) = value
        End Set
      End Property
    
      Public Property Row(Rowindex As Integer) As List(Of String)
        Get
          Return Lattice(Rowindex)
        End Get
        Set(value As List(Of String))
          Lattice(Rowindex) = value
        End Set
      End Property
    
    #Region "New Methods"
      Public Sub New(Rows As Integer, Cols As Integer)
        'Initialize Grid to content size
        Dim sData(Cols - 1) As String
    
        For i As Integer = 0 To Rows - 1
          Dim oRow As New List(Of String)
          oRow.AddRange(sData)
          Lattice.Add(oRow)
        Next
    
        ColumnCount = Cols
        RowCount = Rows
      End Sub
    #End Region
    
    #Region "Row Methods"
      Public Sub SetRowText(RowIndex As Integer, sArray As String())
        Dim oRow As New List(Of String)
        oRow.AddRange(sArray)
        Lattice(RowIndex) = oRow
      End Sub
    
      Public Sub RowAdd()
        Dim sData(Lattice(0).Count - 1) As String
        Dim oRow As New List(Of String)
        Lattice.Add(oRow)
        RowCount += 1
      End Sub
    
      Public Sub RowAdd(Count As Integer)
        Dim sData(Lattice(0).Count - 1) As String
        For i As Integer = 0 To Count - 1
          Dim oRow As New List(Of String)
          Lattice.Add(oRow)
          RowCount += Count
        Next
      End Sub
    
      Public Sub RowInsert(RowIndex As Integer)
        Dim sData(ColumnCount - 1) As String
        Dim oRow As New List(Of String)
        Lattice.Insert(RowIndex, oRow)
        RowCount += 1
      End Sub
    
      Public Sub RowRemoveAt(Rowindex As Integer)
        Lattice.RemoveAt(Rowindex)
        RowCount -= 1
      End Sub
    
      Public Sub RowRemoveLast()
        Lattice.RemoveAt(RowCount - 1)
        RowCount -= 1
      End Sub
    
      Public Sub RowClear()
        Lattice.Clear()
        RowCount = 0
      End Sub
    #End Region
    
    #Region "Column Methods"
      Public Sub ColumnAdd()
        For Each oRow As List(Of String) In Lattice
          oRow.Add("")
        Next
        ColumnCount += 1
      End Sub
    
      Public Sub ColumnRemoveAt(Columnindex As Integer)
        For Each oRow As List(Of String) In Lattice
          oRow.RemoveAt(Columnindex)
        Next
        ColumnCount -= 1
      End Sub
    
      Public Sub ColumnRemoveLast()
        For Each oRow As List(Of String) In Lattice
          oRow.RemoveAt(oRow.Count - 1)
        Next
        ColumnCount -= 1
      End Sub
    #End Region
    
    #Region "Output Methods"
    
      Public Function GetAllText(ColumnDelimiter As String, RowDelimiter As String) As String
        Dim s As String = ""
        For y As Integer = 0 To RowCount - 1
          For x As Integer = 0 To ColumnCount - 1
            s &= Cell(y, x)
            If x < ColumnCount - 1 Then
              s &= ColumnDelimiter
            End If
          Next
          s &= RowDelimiter
        Next
        Return s
      End Function
    
      Public Function GetRowText(RowIndex As Integer, ColumnDelimiter As String) As String
        Dim sOut As String = ""
        With Lattice(RowIndex)
          For i As Integer = 0 To .Count - 1
            sOut &= .Item(i)
            If i < .Count - 1 Then
              sOut &= ColumnDelimiter
            End If
          Next
        End With
        Return sOut
      End Function
    
      Public Function GetColumnText(ColumnIndex As Integer, ColumnDelimiter As String) As String
        Dim sOut As String = ""
        For i As Integer = 0 To RowCount - 1
          sOut &= Lattice(i).Item(ColumnIndex)
          If i < RowCount - 1 Then
            sOut &= ColumnDelimiter
          End If
        Next
        Return sOut
      End Function
    #End Region
    End Class
    Brief Tests
    Code:
    Public Class Form1
      Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
        Dim oGrid As New clsGridOfString(4, 3)
    
        With oGrid
          'Fill via cell property
          .Cell(0, 0) = "A"
          .Cell(0, 1) = "B"
          .Cell(0, 2) = "C"
    
          'Fill via Row property
          .Row(1) = New List(Of String) From {"D", "E", "F"}
    
          'Fill via SetRowText method
          .SetRowText(2, New String() {"G", "H", "I"})
          .SetRowText(3, "J,K,L".Split(","c))
    
          'Output Content
          MessageBox.Show(.GetAllText(",", Environment.NewLine))
          MessageBox.Show(.GetRowText(2, ","))
          MessageBox.Show(.GetColumnText(2, ","))
    
          'Show Grid size
          MessageBox.Show(.RowCount.ToString & "," & .ColumnCount.ToString)
          .RowAdd()
          .ColumnAdd()
          MessageBox.Show(.RowCount.ToString & "," & .ColumnCount.ToString)
        End With
      End Sub
    End Class
    Burn the land and boil the sea
    You can't take the sky from me


    ~T

  2. #2
    Super Moderator si_the_geek's Avatar
    Join Date
    Jul 2002
    Location
    Bristol, UK
    Posts
    41,502

    Re: A lightweight Flexible replacement for a 2D array.

    The only changes I would make would be to change the ColumnDelimiter and RowDelimiter parameters so that they are optional (with default values of Tab and NewLine), and alter GetColumnText so that it has RowDelimiter as a parameter rather than ColumnDelimiter (but that depends on how you want it to behave).

  3. #3
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    35,242

    Re: A lightweight Flexible replacement for a 2D array.

    I'd STILL go with a datatable. I don't know that they are overkill, but it makes it super easy to export to various types of files. For example, the datatable has the WriteXML method, and I believe that Excel will read such an output with ease. Messing with strings means that you will not be efficient for any data type other than strings.

    By the way, the MSFlexGrid wasn't fully functional in VB6, either.
    My usual boring signature: Nothing

  4. #4

    Thread Starter
    Frenzied Member Gruff's Avatar
    Join Date
    Jan 2014
    Location
    Scappoose Oregon USA
    Posts
    1,293

    Re: A lightweight Flexible replacement for a 2D array.

    Thank you gentlemen.

    i am going to stick with a custom class rather than a datatable as I do not need to support anything but string data and it doesn't need to write to disk in this case.

    When I have the app working I will go back and attempt a new version that uses a datatable to compare the two tools.

    Adding delimiter options to the SetRowText() method overloads. Makes good sense. The GetColumnText tool might be better served as returning a list(of string) or and array where no delimiter would be used.

    TheGetAllText() method was all i needed for Excel in this case, but I suppose I could use something like the flexgrid's clip method.

    What this small project has done for me is illuminate areas where i need to learn more.
    For instance I would love to create a Rows and a Columns class to encapsulate those relevant methods.

    Where i get confused is how would a column class instance inside the main class affect the lattice data structure?

    How would I expose the Lattice data structure to the instance of the columns or rows class?
    Last edited by Gruff; Jan 17th, 2015 at 08:50 PM.
    Burn the land and boil the sea
    You can't take the sky from me


    ~T

  5. #5
    PowerPoster
    Join Date
    Oct 2010
    Posts
    2,141

    Re: A lightweight Flexible replacement for a 2D array.

    Hi Gruff,
    What this small project has done for me is illuminate areas where i need to learn more.
    For instance I would love to create a Rows and a Columns class to encapsulate those relevant methods.

    Where i get confused is how would a column class instance inside the main class affect the lattice data structure?
    Personally I would use a strongly typed DataTable for this, but I don't want to dissuade you from your chosen route, but I think that will be recreating the wheel so to speak after you fully flush your class out.

    As far as your current code goes, I would suggest defining your Row Property like this:
    Code:
    Public Property Row(Rowindex As Integer) As IList(Of String)
       Get
          Return Lattice(Rowindex)
       End Get
       Set(value As IList(Of String))
          Lattice(Rowindex) = If(TryCast(value, List(Of String)), New List(Of String)(value)) ' null coalescing function
       End Set
    End Property
    The reason for this is that it makes your life a bit easier when assigning row values in code.
    Code:
    oGrid.Row(2) = {"G", "H", "I"}

  6. #6

    Thread Starter
    Frenzied Member Gruff's Avatar
    Join Date
    Jan 2014
    Location
    Scappoose Oregon USA
    Posts
    1,293

    Re: A lightweight Flexible replacement for a 2D array.

    Thank you TnTinMN! I will incorporate your suggestion.
    Also as I said earlier I intend to pursue using a datatable as well.

    This grid tool has become a science experiment and a learning process for the time being.
    The question still remains about creating instances of new classes inside the main class.

    What is the best way for methods in them to gain access to a data structure that is in the main class?
    For example: If I create a class called 'FileIO' that contains read and write methods and create an instance of it in the main class.
    To write to a file I would use something like: Grid.FileIO.WriteAllText(<parameters>) The lattice data structure's scope does not extend to the FileIO class and the WriteAllText() method will fail when I try to interface with the data structure.

    I know I am missing something about class to class scope.
    Burn the land and boil the sea
    You can't take the sky from me


    ~T

  7. #7
    PowerPoster
    Join Date
    Oct 2010
    Posts
    2,141

    Re: A lightweight Flexible replacement for a 2D array.

    To provide the simple answer is you would pass the instance of the parent class (Me) to the constructor of the internal class and keep a local copy of that reference.

    The question you need to ask yourself first though is: "Does this internal class provide any real benefit? Why not expose the method directly in the parent class"". Shaggy has been writing a dissertation on this topic in another thread that I seen lately, so hopefully he will chime in and give you a link to it.
    Last edited by TnTinMN; Jan 18th, 2015 at 07:11 PM.

  8. #8

    Thread Starter
    Frenzied Member Gruff's Avatar
    Join Date
    Jan 2014
    Location
    Scappoose Oregon USA
    Posts
    1,293

    Re: A lightweight Flexible replacement for a 2D array.

    You said 'copy' does that mean the data is duplicated or is it just pointing to the original instance?

    What about passing just he data structure to the constructor of the internal class as byref?
    wouldn't that just pass a pointer to the parent class structure I am after?
    Burn the land and boil the sea
    You can't take the sky from me


    ~T

  9. #9
    PowerPoster
    Join Date
    Oct 2010
    Posts
    2,141

    Re: A lightweight Flexible replacement for a 2D array.

    I think you got the concept. ByRef is not need as a Class is a reference type. Hopefully this clears it up.

    Code:
    Class ParentClass
       Private _child As ChildClass
       Private data As Int32 = 5
       Public Sub New()
          Me._child = New ChildClass(Me)
       End Sub
    
       ' expose the ChildClass instance
       Public ReadOnly Property Child As ChildClass
          Get
             Return _child
          End Get
       End Property
    
       Class ChildClass
          Private parent As ParentClass
          Public Sub New(ByVal parent As ParentClass)
             Me.parent = parent
          End Sub
          Public Function ParentExtractedData() As Int32
             Return Me.parent.data
          End Function
       End Class
    End Class
    Edit: The "parent" variable in ChildClass points to the instance of ParentClass that created it.
    Edit2: "Copy" was a bad choice of a word, I should have stated: keep a local reference of ParentClass.
    Last edited by TnTinMN; Jan 18th, 2015 at 09:25 PM.

Posting Permissions

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



Click Here to Expand Forum to Full Width