Problem with Object containing array of objects in Visual Basic .NET

J

Jamie Noble

I am experiencing the following problem and I could really use some
assistance.

Basically I have created a class called PhaseDescription. This class
has the following three private members, Name, Description and
ImageFile.

I also have a class called Phases which contains an array of
PhaseDescription objects. There are also additional subroutines in the
class.

Finally in my program I have a public variable called PhaseList that
is defined as follows in my main code module.

Public PhasesList As Phases = New Phases()


I then have a form called frmEditPhase that is used to work with this
variable. When the form loads it populates a combo box on the form
called cmbPhaseName with the names of the objects contained by the
PhasesList variable.

On initial load into the form everything works fine and the objects
contain the correct information that they loaded with. However if I
just strictly manipulate the combo box after loading the form then
after three or four selections I will lose the ImageFile reference.
The ImageFile is a string variable that contains the filename of a jpg
image. The description string however continues to keep it's
information while the ImageFile string gets reset to NONE.

I have a second set of objects that are 99% identical to keep athlete
records. Those two objects work fine! I have even taken those objects
and performed the bare minimum needed to convert them to Phases and
PhaseDescriptions. The problem still existed so I then modified the
selected index code to mirror what's in use in the form Athlete that
works fine.

I have use breaks and watched the progress through the code as well. I
honestly cannot see why I am loosing the reference.

Does anyone have any suggestions on what is going on here. It reminds
me of memory pointer issues in C++ but the fact that the descriptions
are being preserved while the imagefile info in the same object is
being lost has me stumped.

Any suggestions? (Code follows).

~Jamie N

Private Sub frmEditPhase_Load(ByVal sender As System.Object, ByVal e
As System.EventArgs) Handles MyBase.Load
Dim iTempCounter As Integer
Dim aSkillNames() As String

cmbPhaseName.Sorted = True
PhasesList.PopulateNamesComboBox(cmbPhaseName)
If PhasesList.NumberPhases > -1 Then
cmbPhaseName.SelectedItem = PhasesList.GetFirstPhaseName
End If

End Sub

Private Sub cmbPhaseName_SelectedIndexChanged(ByVal sender As
System.Object, ByVal e As System.EventArgs) Handles
cmbPhaseName.SelectedIndexChanged
Dim sImageFileName As String
Dim sLevelName As String
Dim iLoopCounter As Integer
Dim TempPhase As PhaseDescription = New PhaseDescription()

TempPhase = PhasesList.View(cmbPhaseName.SelectedItem)
txtPhaseDescription.Text = TempPhase.Description
If TempPhase.ImageFile = "NONE" Then
picSkillShot.Image = Nothing
Else
picSkillShot.Image = Image.FromFile(TempPhase.ImageFile)
End If
' cmbPhaseName.SelectedItem = TempPhase.Name
TempPhase = Nothing
' CommentsList.PopulateCommentsListBox(cmbPhaseName.SelectedItem,
lstSkillComments)
End Sub


Imports System.Xml
Imports System.Xml.Xsl
Imports System.IO
Public Class Phases
Private PhaseArray(-1) As PhaseDescription

ReadOnly Property NumberPhases() As Integer
' Returns the actual number of comments available in zero
based notation.
' Add one to the answer if you want to think numbers the way
people do.
Get
Return PhaseArray.GetUpperBound(0)
End Get
End Property

Public Sub Add(ByVal TempPhase As PhaseDescription)
Dim iNumPhases As Integer = NumberPhases
iNumPhases += 1
ReDim Preserve PhaseArray(iNumPhases)
PhaseArray(iNumPhases) = New PhaseDescription()
PhaseArray(iNumPhases) = TempPhase

End Sub

Public Sub Delete(ByVal sName As String)

Dim iLoopCounter As Integer
Dim iOrigLastElement As Integer
Dim iFoundComment As Integer = -1

iOrigLastElement = NumberPhases

For iLoopCounter = 0 To iOrigLastElement
If PhaseArray(iLoopCounter).Name.ToUpper = sName.ToUpper
Then
iFoundComment = iLoopCounter
Exit For
End If
Next

If iFoundComment > -1 Then
iOrigLastElement -= 1
For iLoopCounter = iFoundComment To iOrigLastElement
PhaseArray(iLoopCounter) = PhaseArray(iLoopCounter +
1)
Next
ReDim Preserve PhaseArray(iOrigLastElement)
'AthleteArray(iOrigLastElement + 1) = Nothing
End If

End Sub

Public Function GetFirstPhaseName() As String
If NumberPhases > -1 Then
Return PhaseArray(0).Name
Else
Return "NONE"
End If
End Function

Public Sub LoadXML(ByVal sFileName As String)
Dim xtr As New XmlTextReader(sFileName)
Dim iReturnCode As Integer = 0
'Dim TempAthlete As New Athlete()
If File.Exists(sFileName) = True Then

While Not xtr.EOF
Dim TempPhase As PhaseDescription = New
PhaseDescription()

iReturnCode = TempPhase.LoadXML(xtr)
If iReturnCode = 0 Then
Add(TempPhase)
TempPhase = Nothing
End If
End While
'MessageBox.Show(iAthleteCounter & " objects loaded",
"Number of Athlete Objects")
xtr.Close()
xtr = Nothing
End If
End Sub

Public Sub PopulateNamesListBox(ByRef PhasesListbox As ListBox)
Dim iLoopCounter As Integer

PhasesListbox.Items.Clear()
For iLoopCounter = 0 To NumberPhases
PhasesListbox.Items.Add(PhaseArray(iLoopCounter).Name)
Next
End Sub

Public Sub PopulateNamesComboBox(ByRef PhaseComboBox As ComboBox)
Dim iLoopCounter As Integer

PhaseComboBox.Items.Clear()
For iLoopCounter = 0 To NumberPhases
PhaseComboBox.Items.Add(PhaseArray(iLoopCounter).Name)
Next
End Sub

Public Sub SaveXML(ByVal sFileName As String)
Dim xtw As New XmlTextWriter(sFileName,
System.Text.Encoding.UTF8)
Dim iLoopCounter As Integer

xtw.Formatting = Formatting.Indented
xtw.Indentation = 2
xtw.QuoteChar = """"c
xtw.WriteStartDocument(True)
xtw.WriteStartElement("PhaseList")

For iLoopCounter = 0 To NumberPhases
PhaseArray(iLoopCounter).SaveXML(xtw)
Next
xtw.WriteEndElement()
xtw.WriteEndDocument()
xtw.Close()
xtw = Nothing
End Sub

Public Function View(ByVal sName As String) As PhaseDescription
Dim iLoopCounter As Integer
For iLoopCounter = 0 To NumberPhases
If PhaseArray(iLoopCounter).Name.ToUpper = sName.ToUpper
Then
Return PhaseArray(iLoopCounter)
Exit Function
End If
Next

End Function

Public Sub Edit(ByVal TempPhase As PhaseDescription)
Delete(TempPhase.Name)
Add(TempPhase)

End Sub
Public Function PhaseExists(ByVal sName As String) As Boolean
Dim iOrigLastElement As Integer
Dim iLoopCounter As Integer

iOrigLastElement = NumberPhases

For iLoopCounter = 0 To iOrigLastElement
If PhaseArray(iLoopCounter).Name.ToUpper = sName.ToUpper
Then
Return True
End If
Next
Return False
End Function

End Class


Imports System.IO
Imports System.Xml
Imports System.Xml.Xsl
Public Class PhaseDescription
Private PName As String
Private PicFile As String
Private PDescription As String

Public Property Name() As String
Get
Return PName
End Get
Set(ByVal Value As String)
If Value = "" Then
PName = "NONE"
Else
PName = Value
End If
End Set
End Property

Public Property Description() As String
Get
Return PDescription
End Get
Set(ByVal Value As String)
If Value = "" Then
PDescription = "NONE"
Else
PDescription = Value
End If
End Set
End Property

Public Property ImageFile() As String
Get
If File.Exists(PicFile) = True Then
Return PicFile
Else
Return "NONE"
End If
Return PicFile
End Get
Set(ByVal Value As String)
If Value = "" Then
PicFile = "NONE"
Else
PicFile = Value
End If
End Set
End Property

Public Sub New(ByVal sName As String, _
ByVal sDescription As String, _
ByVal sPictureFile As String)

Name = sName
ImageFile = sPictureFile
Description = sDescription

End Sub

Public Sub New()

Name = "NONE"
Description = "NONE"
ImageFile = "NONE"

End Sub

Public Sub SaveXML(ByVal xtw As XmlTextWriter)

xtw.WriteStartElement("PhaseDescription")
xtw.WriteElementString("Name", Name)
xtw.WriteElementString("ImageFile", ImageFile)
xtw.WriteElementString("Description", Description)
xtw.WriteEndElement()
End Sub

Public Function LoadXML(ByVal xtr As XmlTextReader) As Integer
Dim sTempString As String
Dim ReturnSuccess As Integer = 0
Dim ReturnFailure As Integer = 1

Do While xtr.Read

If xtr.NodeType = XmlNodeType.EndElement Then
If xtr.Name = "PhaseList" Then
Return ReturnFailure
Exit Function
End If
End If

If xtr.NodeType = XmlNodeType.EndElement Then
If xtr.Name = "PhaseDescription" Then
Return ReturnSuccess
Exit Function
End If
End If

If xtr.NodeType = XmlNodeType.Element Then
Select Case xtr.Name
Case "Name"
xtr.Read()
Name = xtr.Value
Case "Description"
xtr.Read()
Description = xtr.Value
Case "ImageFile"
xtr.Read()
ImageFile = xtr.Value
End Select
End If
Loop
Return ReturnFailure
End Function

End Class
 
T

Tom Leylan

Hi Jamie,

There are a couple of things worth noting however I'll start with what
appears to be the main problem.

Is there any chance that you are checking the .ImageFile property when you
say that the string is returning "NONE"? You have that as the return value
if the file doesn't exist. So while the string might say
"\mypictures\test1.jpg" if the file isn't there you will get "NONE" back but
that's not the value in PicFile.
Public Property ImageFile() As String
Get
If File.Exists(PicFile) = True Then
Return PicFile
Else
Return "NONE"
End If
Return PicFile
End Get
Set(ByVal Value As String)
If Value = "" Then
PicFile = "NONE"
Else
PicFile = Value
End If
End Set
End Property

Also note that you are creating a new instance of PhaseDescription() which
looks unnecessary since you immediately assign it a reference to an existing
on in PhasesList. and you have variables defined sImageFileName, sLevelName
and iLoopCounter which aren't used at all.
Private Sub cmbPhaseName_SelectedIndexChanged(ByVal sender As
System.Object, ByVal e As System.EventArgs) Handles
cmbPhaseName.SelectedIndexChanged
Dim sImageFileName As String
Dim sLevelName As String
Dim iLoopCounter As Integer
Dim TempPhase As PhaseDescription = New PhaseDescription()

TempPhase = PhasesList.View(cmbPhaseName.SelectedItem)
txtPhaseDescription.Text = TempPhase.Description
If TempPhase.ImageFile = "NONE" Then
picSkillShot.Image = Nothing
Else
picSkillShot.Image = Image.FromFile(TempPhase.ImageFile)
End If
' cmbPhaseName.SelectedItem = TempPhase.Name
TempPhase = Nothing
' CommentsList.PopulateCommentsListBox(cmbPhaseName.SelectedItem,
lstSkillComments)
End Sub

And you might want (as time permits) to consider something other than an
array in which to contain your objects. You can see the effort you have to
go through to add new elements and to delete old ones.

Tom
 
J

Jamie Noble

Hi Tom.

Thanks for taking the time to reply. It's appreciated!
Is there any chance that you are checking the .ImageFile property when you
say that the string is returning "NONE"? You have that as the return value
if the file doesn't exist. So while the string might say
"\mypictures\test1.jpg" if the file isn't there you will get "NONE" back but
that's not the value in PicFile.

This is a good question. The image is being added to the picture box
initially and then over time it disappears. So what I am finding is
that initially the object contains a filename reference to the
picture. If I play with the combobox the link to the name will
eventually be reset to NONE. What's interesting is that if you read
the value of PicFile in the if statement it's already set to NONE.
Also note that you are creating a new instance of PhaseDescription() which
looks unnecessary since you immediately assign it a reference to an existing
on in PhasesList. and you have variables defined sImageFileName, sLevelName
and iLoopCounter which aren't used at all.

Yes the extra variables were left overs from earlier code. I have
removed them. I also eliminated the creation of a new instance.
And you might want (as time permits) to consider something other than an
array in which to contain your objects. You can see the effort you have to
go through to add new elements and to delete old ones.
Do you have suggestions? I want to continue to basically have the
implementation of the internal functions of the Athletes object hidden
from the GUI programmer.

Thanks again for your help. Greatly appreciated. After making the
changes detailed above the application still does not work correctly.

~Jamie N
 
J

Jamie Noble

Hi again!

Actually I just located the problem.

There was another text box on the form called TxtDescription. Whenever
the text in that box is changed then it automatically updates the
object with a copy of the new text in the box.

In that part of the form I was successfully resetting the description
and Name for the object but nothing else. What I neglected to do was
to add a line to grab a copy of the object first and then edit the
object.

The net effect of my omission was that everytime the description
changed then the image file was reset to nothing.

Problem resolved. Still interested in your comments about other
options to consider though.

~Jamie N
 
T

Tom Leylan

Jamie Noble said:
Actually I just located the problem.

Well that's good news.
Problem resolved. Still interested in your comments about other
options to consider though.

Take a look at the ArrayList it should prove easier to use.

Tom
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top