Property in object not updating - vb.net

When I use the following code to create my object, the values I assign in the with portion does no reflect in my class, and I am trying to use that values in the Sub New() portion which is in the 2nd snippet. How can I make that work?
Dim RoughPnl As New RoughPnl(trvPartList, pnlBasePanel, chkRoughUnderFinal.Checked) With
{.Material = cboMaterial.Text, .Label = cboLabel.Text, .Qty = cboQty.Text,
.BoardThickness = cboThickness.Text,
.BoardWidth = cboWidth.Text,
.BoardLength = cboLength.Text,
.BoardColor = picFinalColor.Tag,
.BoardImage = picFinalColor.Image,
.DimLinesON = chkFinalDimLines.Checked,
.LabelsON = chkLabelsON.Checked,
.SizeON = chkSizeON.Checked}
2nd Snippet
Public NotInheritable Class RoughPnl
Inherits MyPanel
Sub New(objTree As TreeView, basePnl As Panel, rpnl As boolean)
MyBase.New(objTree, basePnl)
'Initial values
m_myKey = myKey
m_refNum = refNum
m_material = material
m_label = label
m_qty = qty
m_pnlID = "Rough"
Me.Name = m_pnlID & "key" & m_material & "-" & m_myKey
End Sub
End Class

As suggested by #GlorinOakenfoot, code like this:
Dim obj As New SomeType With {.SomeProperty = someValue}
Is just a shorthand for this:
Dim obj As New SomeType
obj.SomeProperty = someValue
It should be obvious why you can't use someValue in the constructor. If you want to use a value in the constructor then, like for any other method, you must pas that value via a parameter.

Related

Sort all List properties (and subproperties) of an object

I'm trying to sort all of the List properties of an object (using OrderBy). It's a complex object that has Lists that may contain Lists. I figured that reflection and recursion would get me there, but I seem to be stumbling on actually sorting the lists.
Here is the code I have:
Private Sub SortData(ByRef obj As Object)
Dim type As Type = obj.GetType()
Dim properties As PropertyInfo() = type.GetProperties()
For Each item As PropertyInfo In properties
If Not item.PropertyType.FullName.Contains("System.Collection") AndAlso item.PropertyType.FullName.Contains("MyType") Then
Dim value = obj.GetType().GetProperty(item.Name).GetValue(obj)
If value IsNot Nothing Then
SortData(value)
End If
End If
If item.PropertyType.FullName.Contains("System.Collection") Then
Dim value = obj.GetType().GetProperty(item.Name).GetValue(obj)
If value IsNot Nothing Then
Dim subType As Type = value(0).GetType()
Dim subProperties As PropertyInfo() = subType.GetProperties()
If subProperties.Any(Function(x) x.Name = "SortKey") Then
' --- Struggling here! ---
Dim castList = TryCast(value, List(Of MoreDetail))
value = castList.OrderBy(Function(x) x.SortKey)
value = TryCast(value, List(Of MoreDetail))
' --- End struggle ---
End If
For Each subProperty In subProperties
SortData(value.GetType().GetProperty(subProperty.Name).GetValue(value))
Next
End If
End If
Next
End Sub
The main problem is in the "struggling here" section of the code - I seem to have to cast the object into its original type, but that defeats the purpose of the reflection as different objects will have a different list type, so ideally I'd be able to cast the object based on its reflected type.
Thanks for the help!
EDIT: To alleviate the "XY problem" in my question, here's what I'm trying to do... I want to be able to sort all List properties of an object - the problem is that the object can have nested lists, so ideally I'd do this via reflection, recursively checking to see if a list contains a list in any of the child properties. The lists are mostly complex types, so I want to sort a list only if the collection contains objects that have the "SortKey" property defined.
EDIT 2: An example of what I'm after is something like (pseudocode):
Public Class SuperList
Public Property A As String = ""
Public Property BList As New List(Of B)
End Class
Public Class B
Public Property SortKey As String = ""
Public Property Name As String = ""
Public Property CList As New List(Of C)
End Class
Public Class C
Public Property SortKey As String = ""
Public Property Name As String = ""
End Class
Dim cExampleSecond As New C With {.SortKey = "345", .Name = "C Second"}
Dim cExampleFirst As New C With {.SortKey = "123", .Name = "C First"}
Dim BExampleFirst As New B With {.Sortkey = "ABC", .Name = "B First"}
BExampleFirst.CList.Add(cExampleSecond)
BExampleFirst.CList.Add(cExampleFirst)
Dim superExample As New SuperList With {.A = "Whatever"}
superExample.BList.Add(BExampleFirst)
...
SortData(superExample)
...
After running SortData() it should look like:
superExample =
.A = "Whatever"
.BList =
.SortKey = "ABC"
.BExampleFirst =
cExampleFirst =
.SortKey = "123"
.Name = "C First"
cExampleSecond =
.SortKey = "345"
.Name = "C Second"
Note that if superExample.BList had more elements, they would also be sorted...
Looking at your example, I would consider that you want to sort a kind of tree.
1- Tree means recursivity: the SortData() will sort the current level/current list, and call SortData() for every sub-element. You can use inheritance or composition to implement this functionality in every element of your tree.
2- You can use the sort functionality of the List and Dictionary. You only have to implement the IComparable for the element of you list.
Here is some sample code that illustrates the global method.
Public Class SortedElem
Implements IComparable(Of SortedElem)
Public Property Children As New List(Of SortedElem)
Public Property SortKey As String
Public Sub SortData()
Children.Sort()
For Each child In Children
child.Children.Sort()
Next
End Sub
Public Function CompareTo(other As SortedElem) As Integer Implements IComparable(Of SortedElem).CompareTo
Return String.Compare(Me.SortKey, other.SortKey)
End Function
End Class
Public Class SuperList
Inherits SortedElem
Public Property A As String = ""
End Class
Public Class B
Inherits SortedElem
Public Property Name As String = ""
End Class
Public Class C
Inherits SortedElem
Public Property Name As String = ""
End Class
Public Sub AnySub()
Dim cExampleFirst As New C With {.SortKey = "1", .Name = "C First"}
Dim cExampleSecond As New C With {.SortKey = "3", .Name = "C Second"}
Dim cExampleThird As New C With {.SortKey = "2", .Name = "C Third"}
Dim BExampleFirst As New B With {.SortKey = "ABC", .Name = "B First"}
Dim BExampleSecond As New B With {.SortKey = "XYZ", .Name = "B Second"}
Dim BExampleThird As New B With {.SortKey = "DEF", .Name = "B Third"}
BExampleFirst.Children.Add(cExampleFirst)
BExampleFirst.Children.Add(cExampleSecond)
BExampleFirst.Children.Add(cExampleThird)
Dim superExample As New SuperList With {.A = "Whatever"}
superExample.Children.Add(BExampleFirst)
superExample.Children.Add(BExampleSecond)
superExample.Children.Add(BExampleThird)
superExample.SortData()
End Sub

Misunderstanding of collection

Based on this post What are the benefits of using Classes in VBA? I've made a bit of code.
The first class CarteID
Option Explicit
Private agePersonne As Long
Private nomPersonne As String
Public Property Get glAgePersonne() As Long
glAgePersonne = agePersonne
End Property
Public Property Let glAgePersonne(lAgePersonne As Long)
agePersonne = lAgePersonne
End Property
Public Property Get glNomPersonne() As String
glNomPersonne = nomPersonne
End Property
Public Property Let glNomPersonne(lNomPersonne As String)
nomPersonne = lNomPersonne
End Property
Then the second class ProcessCarteID
Option Explicit
Private colCartesID As Collection
Public Property Get gsCartesID() As Collection
Set gsCartesID = colCartesID
End Property
Public Property Set gsCartesID(sCartesID As Collection)
Set colCartesID = sCartesID
End Property
Function RecupAgeMoyen() As Double
Dim cid As CarteID
Dim moyenneAge As Double
moyenneAge = 0
For Each cid In colCartesID
moyenneAge = moyenneAge + cid.glAgePersonne
Next cid
moyenneAge = moyenneAge / colCartesID.Count
RecupAgeMoyen = moyenneAge
End Function
And the module :
Option Explicit
Function PopulateArray() As Collection
Dim colInfos As New Collection
Dim cid As CarteID
Set cid = New CarteID
cid.glNomPersonne = "Fred"
cid.glAgePersonne = 21
colInfos.Add cid
Set cid = New CarteID
cid.glNomPersonne = "Julie"
cid.glAgePersonne = 18
colInfos.Add cid
Set cid = New CarteID
cid.glNomPersonne = "Jean"
cid.glAgePersonne = 25
colInfos.Add cid
Set PopulateArray = colInfos
End Function
Sub TestAgeMoyen()
Dim pci As ProcessCarteID
Set pci = New ProcessCarteID
Set pci.gsCartesID = PopulateArray()
Debug.Print pci.RecupAgeMoyen()
End Sub
In the function RecupAgeMoyen() I tried For Each cid In colCartesID and For Each cid In gsCartesID. Both worked.
I just wanted to know why it worked also with gsCartesID because I am not sure to understand.
Thanks !

Bind list of object to datagridview in VB

I want to bind a list of objects to a DataGridView in VB.
It worked first with using datagrid.Rows.Add(...) for each result, but that blocks the program.
I've read it could be solved by binding a list of objects to the DataGridView with .DataSource.
But it don't work, my datagridview remains empty...
What is wrong? Is it something in the GUI-code (see below)?
This where the logic happens:
Public queryList As New List(Of _study)
...
If _objResponse.Status = CurrentStatus.Pending Then
Dim _newStudy As New _study
' Parse one study from the response (_objResponse.Dataset.[Get](Of String)(DicomTag.StudyDate))
_newStudy._studyID = _objResponse.Dataset.[Get](Of String)(DicomTag.StudyInstanceUID)
_newStudy._name = _objResponse.Dataset.[Get](Of String)(DicomTag.AccessionNumber)
' Save study info for later use
queryList.Add(_newStudy)
ElseIf _objResponse.Status = DicomStatus.Success
dgvResults.DataSource = queryList
...
Else
MsgBox("Failed")
End If
Here is the _newStudy class:
Public Class _study
Private _studyID As String
Private _name As String
Public Property _studyID() As String
Get
Return _studyID
End Get
Set(ByVal value As String)
_studyID = value
End Set
End Property
Public Property _name() As String
Get
Return _name
End Get
Set(ByVal value As String)
_name = value
End Set
End Property
End Class
And here is the code of the DataGridView() GUI:
Private colName As System.Windows.Forms.DataGridViewTextBoxColumn
Private colID As System.Windows.Forms.DataGridViewTextBoxColumn
Private dgvResults As System.Windows.Forms.DataGridView
...
Me.dgvResults = New System.Windows.Forms.DataGridView()
Me.colID = New System.Windows.Forms.DataGridViewTextBoxColumn()
Me.colName = New System.Windows.Forms.DataGridViewTextBoxColumn()
Me.dgvResults.AllowUserToAddRows = false
Me.dgvResults.AllowUserToDeleteRows = false
Me.dgvResults.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.Fill
Me.dgvResults.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize
Me.dgvResults.Columns.AddRange(New System.Windows.Forms.DataGridViewColumn() {Me.colID, Me.colName})
Me.dgvResults.Location = New System.Drawing.Point(6, 19)
Me.dgvResults.Name = "dgvResults"
Me.dgvResults.ReadOnly = true
Me.dgvResults.Size = New System.Drawing.Size(540, 206)
Me.dgvResults.TabIndex = 2
'colID
Me.colID.HeaderText = "Study ID"
Me.colID.DataPropertyName = "_studyID"
Me.colID.Name = "colID"
Me.colID.ReadOnly = true
'colPatientName
Me.colName.HeaderText = "Name"
Me.colName.DataPropertyName = "_name"
Me.colName.Name = "_name"
Me.colName.ReadOnly = true
...
CType(Me.dgvResults,System.ComponentModel.ISupportInitialize).EndInit
I think the DataPropertyName is correct to get the link between the objects and the datagrid, right...?
Thanks a lot!
The List object doesn't transmit any information that the list has been updated or changed.
To do that, try using the BindingList from the System.ComponentModel namespace:
Public queryList As New BindingList(Of _study)

initialize a class from datarow

I'm trying to initialize a class using a DataRow, But when the DataRow item "LOCATION" is a Null value I receive an error. How can I load these variables with their appropriate datatype values when the row item is null?
Public Sub New(ByVal row As DataRow)
intID = row.Item("ID")
strDepartmentCode = row.Item("DEPARTMENT_CODE")
strDepartmentName = row.Item("DEPARTMENT_NAME")
intLocation = row.Item("LOCATION")
End Sub
Something like this:
Dim location As Object = row.Item("LOCATION")
If location Is Convert.DBNull
intLocation = -1
Else
intLocation = Convert.ToInt32(location)
End If

Is there a way to dynamically specify property names in a class?

VB.NET 2010~Framework 3.5
Is there a way to dynamically specify property names of a class?
Sometimes I need a list created from Prop1 and Prop2
Other times I need a list created from Prop2 and Prop4 etc.. The target properties are not known ahead of time, they constantly change as the app is running. . .
Option Strict On
Option Explicit On
Public Class Form1
Private Class Things
Public Property Prop1 As String
Public Property Prop2 As String
Public Property Prop3 As String
Public Property Prop4 As String
End Class
Private Class SubThing
Public Property P1 As String
Public Property P2 As String
End Class
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim mainLst As New List(Of Things)
Dim count As Integer
Do Until count = 20
mainLst.Add(New Things With {.Prop1 = count.ToString, _
.Prop2 = (count + 1).ToString, _
.Prop3 = (count + 2).ToString, _
.Prop4 = (count + 3).ToString})
count += 1
Loop
' Need to dynamically pick properties From mainLst into subLst.
' The commented code below wont compile but demonstrates what I'm trying to do
' can this be done without looping?
'Dim propNameA As String = "Prop1" ' Dynamically specify a property name
'Dim propNameB As String = "Prop4"
'Dim subLst = From mainItem In mainLst
' Select New SubThing() With {.P1 = mainItem.propNameA, .P2 = mainItem.propNameB}
' This code below compiles but lacks the dynamics I need?
Dim subLst = From mainItem In mainLst
Select New SubThing() With {.P1 = mainItem.Prop1, .P2 = mainItem.Prop4}
End Sub
The most direct approach would be to use CallByName (MSDN Link). I'm assuming your example is a simplified version of what you're really working with, but it seems like an even better approach would be to get rid of your Prop1, Prop2, ... string properties and just use a List(Of String) which you can then just index into, without having to frankenstein together the property names with an index value. Example:
Public Property Props As List(Of String)
'...
Dim subLst = From mainItem In mainLst
Select New SubThing() With {.P1 = mainItem.Props(1), .P2 = mainItem.Props(4)}
Not really sure what your exact use case is from your example, but hopefully this points you in the right direction.
Here's an example using reflection as helrich# suggested. (you have to Imports System.Reflection at the top of your .vb file)
1) Naive console outputting example:
Dim thingType As Type = GetType(Things)
Dim prop1Property As PropertyInfo = thingType.GetProperty("Prop1")
Dim thingInstance As Things = New Things()
thingInstance.Prop1 = "My Dynamically Accessed Value"
Dim prop1Value = prop1Property.GetValue(thingInstance).ToString()
Console.WriteLine(prop1Value)
2) Adapted to your example ("probably" works, haven't tested it all):
Dim propNameA As String = "Prop1" ' Dynamically specify a property name
Dim propNameB As String = "Prop4"
Dim propAPropInfo As PropertyInfo = GetType(Things).GetProperty(propNameA)
Dim propBPropInfo As PropertyInfo = GetType(Things).GetProperty(propNameB)
Dim subLst = From mainItem In mainLst
Select New SubThing() With {.P1 = propAPropInfo.GetValue(mainItem).ToString(), .P2 = propBPropInfo.GetValue(mainItem).ToString()}
Option Strict On
Option Explicit On
Imports System.Reflection
Module Module1
Private Class SourceClass
Public Property Prop1 As String
Public Property Prop2 As String
Public Property Prop3 As String
Public Property Prop4 As String
End Class
Private Class SubClass
Public Property P1 As String
Public Property P2 As String
End Class
Sub Main()
Dim mainLst As New List(Of SourceClass)
Dim count As Integer
Do Until count = 20 ' create source list
mainLst.Add(New SourceClass With {.Prop1 = count.ToString, _
.Prop2 = (count + 1).ToString, _
.Prop3 = (count + 2).ToString, _
.Prop4 = (count + 3).ToString})
count += 1
Loop
Dim propAInfo As PropertyInfo = GetType(SourceClass).GetProperty("Prop1") ' Dynamically specify a property name
Dim propBInfo As PropertyInfo = GetType(SourceClass).GetProperty("Prop3")
' create a list of SubClass from SourceClass
Dim subLst = From mainItem In mainLst Select New SubClass() _
With {.P1 = propAInfo.GetValue(mainItem, Nothing).ToString, _
.P2 = propBInfo.GetValue(mainItem, Nothing).ToString}
count = 0
Do Until count = subLst.Count
Debug.WriteLine(subLst(count).P1 & "~" & subLst(count).P2)
count += 1
Loop
End Sub
End Module