VB.net Objectlistview filtering column by range of numbers - vb.net

I have an objectlistview with a column of numbers ranging from -3000 to 10000. I need to apply a filter for anything less than 2000 (this should include all the negative numbers as well). I've read the examples and help (http://objectlistview.sourceforge.net/cs/filtering.html#filtering-label) but it's in C# and I'm working with VB.net. I can normally figure the conversion out but this one is stumping me.
I have another piece of code that uses a function instead of a delegate (when applying an image) but I couldn't get it to work in this filtering instance. I also tried using a regex but I just feel that since I'm dealing with numbers I should do it without regex.
Can someone please show me a custom filtering example with number ranges in VB.net to help me get over this?
Thanks!
Here is an example I threw together:
When you click "Apply Filter", it should only show Mary Swanson and Jiminy Cricket (both under a height of 30).
Here is the code I used to create the olv
Private Sub Button3_Click_1(sender As Object, e As EventArgs) Handles Button3.Click
Dim LvLst As New List(Of Person)
Dim LvItm As New Person With {.FirstName = "Joe",
.LastName = "Blow",
.Glasses = "Y",
.Height = "75",
.HeightBar = "75"}
LvLst.Add(LvItm)
Dim LvItm2 As New Person With {.FirstName = "Mary",
.LastName = "Swanson",
.Glasses = "N",
.Height = "25",
.HeightBar = "25"}
LvLst.Add(LvItm2)
Dim LvItm3 As New Person With {.FirstName = "Mike",
.LastName = "Tyson",
.Glasses = "N",
.Height = "125",
.HeightBar = "125"}
LvLst.Add(LvItm3)
Dim LvItm4 As New Person With {.FirstName = "Jiminy",
.LastName = "Cricket",
.Glasses = "Y",
.Height = "-9",
.HeightBar = "-9"}
LvLst.Add(LvItm4)
ObjectListView3.View = View.Details
Dim myImages = New ImageList
myImages.Images.Add(My.Resources.Hipster_Glasses_icon)
myImages.Images.Add(My.Resources.Button_important_icon)
ObjectListView3.SmallImageList = myImages
ObjectListView3.UseCellFormatEvents = True
ObjectListView3.OwnerDraw = True
Col_Glasses.ImageGetter = Function(x As Object) As Integer
Dim casted As Person = DirectCast(x, Person)
If casted.Glasses = "Y" Then
Return 0
Else
Return 1
End If
End Function
Col_Height.Renderer = New BarRenderer(0, 100, Pens.Black, Brushes.Gold)
'Set no data message
ObjectListView3.EmptyListMsg = "No Data Found"
ObjectListView3.EmptyListMsgFont = New Font("Tahoma", 18)
'Allows you to type and search inside the olv
ObjectListView3.IsSearchOnSortColumn = True
ObjectListView3.SetObjects(LvLst)
End Sub
And this is the code behind the filter button that I need help on
Private Sub Button1_Click_1(sender As Object, e As EventArgs) Handles Button1.Click
ObjectListView3.ModelFilter = Function(x As Object) As ModelFilter
Dim casted As Person = DirectCast(x, Person)
If casted.Height <= CInt(HeightFilter.Text) Then
Return x
End If
End Function
End Sub
Person Class
Public Class Person
Public Property FirstName As String
Public Property LastName As String
Public Property Glasses As String
Public Property Height As Integer
Public Property HeightBar As Integer
End Class
The error says that IModelFilter is not a delegate type. I don't know what i should be returning from the function?? Do you see the imagegetter I used for the glasses column? I was trying to use the same approach but I've never used it for a IModelFilter. Thanks for the help!

Set the filter to a new ModelFilter. x is the object being passed into the function, cast it to your Personclass, then filter by height. The filter basically returns True (to keep it) or False (to filter it out) as it processes each Person.
ObjectListView3.ModelFilter = New BrightIdeasSoftware.ModelFilter(Function(x) CType(x, Person).Height <= CInt(Me.HeightFilter.Text))

Related

Visual Basic Text File

I'm currently learning about Visual Basic text files but I came across a problem. I'm supposed to create a text file (Players) with data inside and I have to design a form with listbox to include the players’ names that are more than 30 years old.
This is my current code:
Dim q1 = From itm As String In IO.File.ReadAllLines("Players.txt")
Let Data=itm.Split(","c)
Let fname = Data(0)
Let age = Data(4)
Let newline = fname * " "& age
Where age > 30
For Each itm1 As String in q1
ListBox1.Items.Add(itm1)
Next
My expected output should show the names of players that are over 30 years old. Thank you in advance to anyone that can help me solve this issue.
You can use linq. For example: assume you have a txt like that
Giuseppe, 30
Pippo, 13
Luca, 32
to extract only over 30 years old you can do...
Dim obj = My.Computer.FileSystem.ReadAllText("Players.txt").Split(vbCrLf).ToList()
Dim ret = (From a In obj Where a.Split(",")(1) > 30 Select a).ToList
The result is
Luca, 32
Best to use a class to define Player. I also made a class Players to hide the file processing from the consumer.
Public Class Player
Public Property Name As String
Public Property Age As Integer
End Class
Public Class Players
Private _list As New List(Of Player)()
Public ReadOnly Property List As IEnumerable(Of Player)
Get
Return _list
End Get
End Property
Public Sub New(path As String)
Dim lines = File.ReadAllLines(path)
For Each line In lines
Dim split = line.Split(","c)
If split.Count = 2 Then
_list.Add(New Player() With {.Name = split(0), .Age = Integer.Parse(split(1))})
End If
Next
End Sub
End Class
And use databinding to populate the ListBox
Dim ps = New Players("Players.txt")
Me.ListBox1.DataSource = ps.Items.Where(Function(p) p.Age >= 30).ToList()
Me.ListBox1.DisplayMember = "Name"
If you're not into the whole Players class and Items property, you can still use the Player class, and just do all the processing in your consuming code (it's basically the same thing, but the processing code is not encapsulated in the model).
Dim ps = New List(Of Player)()
Dim lines = File.ReadAllLines("Players.txt")
For Each line In lines
Dim split = line.Split(","c)
If split.Count = 2 Then
ps.Add(New Player() With {.Name = split(0), .Age = Integer.Parse(split(1))})
End If
Next
Me.ListBox1.DataSource = ps.Where(Function(p) p.Age >= 30).ToList()
Me.ListBox1.DisplayMember = "Name"

set new DataGridViewComboBoxCell for after validation of previous DataGridViewTextBoxCell in same column and row

I have this DataGridView and it has a DataGridViewTextBoxColumn where user can type a number, and after he types it I perform a search to find previous records under that number.
I want to show this records so user can select one of them, or keep the value typed, which means he wants to create a new record.
For that, I want to replace each DataGridViewTextBoxCell for a DataGridViewComboBoxCell with those options when user has finished typing.
However, it is raising this exception when I try to permform this replacement: "System.InvalidOperationException" in System.Windows.Forms.dll. Additional information: The operation is not valid because it results in a reentering call to the function SetCurrentCellAddressCore.
Here is my code (I already tried to handle CellLeave instead of CellValidated):
Private Sub DataGridViewDebitos_CellValidated(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridViewDebitos.CellValidated
DataGridViewDebitos.EndEdit(DataGridViewDataErrorContexts.Commit)
If e.ColumnIndex = ColumnDebito.Index Then
Dim cellDebito = DataGridViewDebitos.Rows(e.RowIndex).Cells(ColumnDebito.Index)
Dim numDebito = String.Concat(If(cellDebito.Value, "").ToString.Where(Function(c) Char.IsLetterOrDigit(c)))
If TypeOf cellDebito Is DataGridViewTextBoxCell AndAlso numDebito.Length >= 3 Then
Dim prcsa As New List(Of JObject) 'In real version, prcsa is populated by an external function, but it doesn't affect the result
Dim j = New JObject
j.SetProperty("id", 0)
j.SetProperty("nome", cellDebito.Value)
prcsa.Insert(0, j) 'This option is always present, it allows user to keep simply what was typed
'Exception hapens here
DataGridViewDebitos(cellDebito.ColumnIndex, cellDebito.RowIndex) =
New DataGridViewComboBoxCell With {
.DataSource = prcsa,
.DisplayMember = "nome",
.FlatStyle = FlatStyle.Flat,
.ValueMember = "id",
.Value = prcsa(0).Value(Of Integer)("id")}
End If
End If
End Sub
Thank you very much
I've put the problematic statement into a Lambda sub via .BeginInvoke and this solved the problem, only I don't know why...
Can anyone explain the mechanics of it to me? Thank you!
Private Sub DataGridViewDebitos_CellValidated(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridViewDebitos.CellValidated
DataGridViewDebitos.EndEdit(DataGridViewDataErrorContexts.Commit)
If e.ColumnIndex = ColumnDebito.Index Then
Dim cellDebito = DataGridViewDebitos.Rows(e.RowIndex).Cells(ColumnDebito.Index)
Dim numDebito = String.Concat(If(cellDebito.Value, "").ToString.Where(Function(c) Char.IsLetterOrDigit(c)))
If TypeOf cellDebito Is DataGridViewTextBoxCell AndAlso numDebito.Length >= 3 Then
Dim prcsa As New List(Of JObject) 'In real version, prcsa is populated by an external function, but it doesn't affect the result
Dim j = New JObject
j.SetProperty("id", 0)
j.SetProperty("nome", cellDebito.Value)
prcsa.Insert(0, j) 'This option is always present, it allows user to keep simply what was typed
'Exception hapens here
DataGridViewDebitos.BeginInvoke(
Sub()
DataGridViewDebitos(cellDebito.ColumnIndex, cellDebito.RowIndex) =
New DataGridViewComboBoxCell With {
.DataSource = prcsa,
.DisplayMember = "nome",
.FlatStyle = FlatStyle.Flat,
.ValueMember = "id",
.Value = prcsa(0)("id")}
End Sub)
End If
End If
End Sub

DataGridView Cascading/Dependent ComboBox Columns

So I work from time to time in Winforms on a legacy app and am not familiar with best practices at all times with binding objects. Basically I have a three part set where I have two people, they may have only one product, but that product could cause the possibility to have different sets of SKUs. Is there a way to trigger an event and population of a combobox from the values of a first combobox? I have been looking around and I am either finding basic data of just how to bind a combobox(I can do that fine) or do something with how you bind it. Not binding after a dependent parent change is triggered and changing the dataset. Example below:
POCOS:
Public Class Person
Public Property PersonID As Integer
Public Property FirstName As String
Public Property LastName As String
Public Property ProductId As Integer
Public Property SkuId As Integer
End Class
Public Class Product
Public Property ProductId As Integer
Public Property Description As String
End Class
Public Class Sku
Public Property SKUId As Integer
Public Property ProductId As Integer
Public Property Description As String
End Class
Main code example (basic UI really only has a Datset labeled 'ds' that matches nearly identically the Person and Product POCOS with datatables. A datagridview 'dgv' whose columns are bound to data in Person EXCEPT for a column called SKU that has no binding as I want to bind it after the fact and that is where I am failing miserably at.
Update 9-13-2016
I can get the below code to work EXCEPT in certain large scale solutions(the whole reason I did this). It basically will NOT execute the line that casts the cell() to a datagridviewcomboboxcell and ignores it and jumps over the line. No reason why, it just jumps over it. I am wondering if in larger classes the datagrid views can become corrupt or something.
Main Code:
Private _people As List(Of Person) = New List(Of Person)
Private _products As List(Of Product) = New List(Of Product)
Private _SKUs As List(Of Sku) = New List(Of Sku)
Private _initialLoadDone = False
Private _currentRow As Integer? = Nothing
Private Sub DynamicComboBoxDoubleFill_Load(sender As Object, e As EventArgs) Handles MyBase.Load
_products = New List(Of Product)({
New Product With {.ProductId = 1, .Description = "Offline"},
New Product With {.ProductId = 2, .Description = "Online"}
})
Dim s = ""
For Each o In _products
Dim row As DataRow = ds.Tables("tProducts").NewRow
row("ProductId") = o.ProductId
row("Description") = o.Description
ds.Tables("tProducts").Rows.Add(row)
Next
_SKUs = New List(Of Sku)({
New Sku With {.SKUId = 1, .ProductId = 1, .Description = "Mail"},
New Sku With {.SKUId = 2, .ProductId = 1, .Description = "Magazine"},
New Sku With {.SKUId = 3, .ProductId = 2, .Description = "Email"},
New Sku With {.SKUId = 4, .ProductId = 2, .Description = "APIRequest"}
})
Dim items = _SKUs
_people = New List(Of Person)({
New Person With {.PersonID = 1, .FirstName = "Emily", .LastName = "X", .ProductId = 1, .SkuId = 1},
New Person With {.PersonID = 2, .FirstName = "Brett", .LastName = "X", .ProductId = 2, .SkuId = 3}
})
For Each p In _people
Dim row As DataRow = ds.Tables("tPeople").NewRow
row("PersonId") = p.PersonId
row("FirstName") = p.FirstName
row("LastName") = p.LastName
row("ProductId") = p.ProductId
row("SkuId") = p.SkuId
ds.Tables("tPeople").Rows.Add(row)
Next
For Each row As DataGridViewRow In dgv.Rows
ArrangeValuesForSKUComboBox(row)
Next
_initialLoadDone = True
End Sub
Private Sub ArrangeValuesForSKUComboBox(row As DataGridViewRow)
Dim productId = CInt(row.Cells("ProductId")?.Value)
Dim skus = _SKUs.Where(Function(x) x.ProductId = productId).ToList().Select(Function(x) New With {Key .SkuId = x.SKUId, .SkuDesc = x.Description}).ToList()
Dim cell = row.Cells("SKU")
'Yeah I don't always work. In this example I do, in others I won't.
'For this reason I just want more ideas. I don't care if you completely blow up how the binding is done and do something else entirely.
Dim combobox = CType(cell, DataGridViewComboBoxCell)
combobox.DataSource = skus
combobox.ValueMember = "SKUId"
combobox.DisplayMember = "SkuDesc"
combobox.Value = skus.FirstOrDefault()?.SkuId
End Sub
Private Sub dgv_CellValueChanged(sender As Object, e As DataGridViewCellEventArgs) Handles dgv.CellValueChanged
If _initialLoadDone Then
Dim headerText As String = TryCast(sender, DataGridView).Columns(e.ColumnIndex).HeaderText
If headerText = "PRODUCT" Then
ArrangeValuesForSKUComboBox(dgv?.CurrentRow)
End If
End If
End Sub
To have dependent (cascading or master/slave) ComboBox columns in DataGridView, you can follow this steps:
Set DataSource of slave column to all available values.
Goal: Here the goal is prevent rendering errors at first load, so all slave combo boxes can show value correctly.
Hanlde EditingControlShowing event of the grid and check if the current cell is slave combo cell, then get the editing control using e.Control which is of type DataGridViewComboBoxEditingControl. Then check the value of master combo cell and set the DataSource property of editing control to a suitable subset of values based on the value of master combo cell. If the value of master cell is null, set the data source to null.
Goal: Here the goal is setting data source of slave combo to show only suitable values when selecting values from slave combo.
Handle CellValueChanged and check if the current cell is master combo, then set the value for dependent cell to null.
Note: Instead of setting the value of slave cell to null, you can set it to first available valid value based on master cell value.
Goal: Here the goal is prevent the slave combo to have invalid values after the value of master combo changed, so we reset the value.
Following above rules you can have as many dependent combo boxes as you need.
Example
In below example I have a Country (Id, Name) table, a State(Id, Name, CountryId) table and a Population(CountryId, StateId, Population) table. And I want to perform data entry for Population table using 2 combo columns for country and state and a text column for population. I know this is not a normal db design, but it's just for example of having master/slave (dependent) combo box columns in grid:
Private Sub EditingControlShowing(sender As Object, _
e As DataGridViewEditingControlShowingEventArgs) _
Handles PopulationDataGridView.EditingControlShowing
Dim grid = DirectCast(sender, DataGridView)
If (grid.CurrentCell.ColumnIndex = 1) Then 'State column
Dim combo = DirectCast(e.Control, DataGridViewComboBoxEditingControl)
If (grid.CurrentRow.Cells(0).Value IsNot DBNull.Value) Then
Dim data = Me.DataSet1.State.AsDataView()
data.RowFilter = "CountryId = " + grid.CurrentRow.Cells(0).Value.ToString()
combo.DataSource = data
Else
combo.DataSource = Nothing
End If
End If
End Sub
Private Sub CellValueChanged(sender As Object, e As DataGridViewCellEventArgs) _
Handles PopulationDataGridView.CellValueChanged
Dim grid = DirectCast(sender, DataGridView)
If (e.ColumnIndex = 0 And e.RowIndex >= 0) Then 'Country Column
grid.Rows(e.RowIndex).Cells(1).Value = DBNull.Value 'State Column
End If
End Sub

Multi-Threading IP Address Pings Causing Application Crash

Boy, learning something new can be a real headache if you can't find a solid source. I have been designing applications in a linear fashion for some time now and want to step up into a more powerful approach. I have been reading up on threading, and perhaps have gone to an larger level than I should. However, one usually steps up when the application calls for it and no better time than the present to learn something new.
My program is designed to do something that seems rather simple, but has become extremely difficult to create in a smooth running manor. The original design created object of each device on the network it wished to ping, in my real world environment they are Kindles. The goal was to ensure they were still connected to the network by Pining them. I used a For Loop and Obj Array to do this set on a Timer. This had unexpected results causing the ListView to flicker and load slowly after the ListView1.Items.Clear. I evolved into updating the List Items rather than clearing them and the flicker remained.
I assumed this was due to the slow process of the array and pings so I started hunting for solutions and came across Multi-Threading. I have known about this for some time, but have yet to dive into the practice. My program seemed to need more speed and smoother operation so I took a stab at it. The below code in its complete form is the result, however it crashes and throws errors. Clearly I have not used Threading as it was intended. Using it in simpler functions works fine and I feel I have the grasp. That is if i want my program to pointlessly run counters.
I don't know what to do next in my steps for getting this task done, and figure I am combining several different methods into a mush of dead program. I could really use some help getting back on track with this. All comments welcome and thank you for checking out my code.
Form1 Code
Public Class Form1
'Obj Array
Public Shared objDevice As New List(Of kDevice)
'Thread Array for each Obj
Public Shared thread() As System.Threading.Thread
Private Sub ipRefresh(objID, itemPos)
Dim objDev As kDevice = objID
If My.Computer.Network.Ping(objDev.kIP) Then
objDev.kStatus = "Online"
objDev.kPings = 0
Else
objDev.kPings += 1
End If
If objDev.kPings >= 8 Then
objDev.kStatus = "Offline"
objDev.kPings = 0
ListView1.Items(itemPos).BackColor = Color.Red
End If
Dim str(4) As String
Dim itm As ListViewItem
str(0) = objDev.kName
str(1) = objDev.kIP
str(2) = objDev.kStatus
str(3) = objDev.kPings
itm = New ListViewItem(str)
ListView1.Items(itemPos) = itm
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.CheckForIllegalCrossThreadCalls = False
' Adding ListView Columns
ListView1.Columns.Add("Device", 100, HorizontalAlignment.Left)
ListView1.Columns.Add("IP Address", 150, HorizontalAlignment.Left)
ListView1.Columns.Add("Status", 60, HorizontalAlignment.Left)
ListView1.Columns.Add("Pings", 60, HorizontalAlignment.Left)
Dim ipList As New List(Of String)
Dim nameList As New List(Of String)
Using MyReader As New Microsoft.VisualBasic.FileIO.TextFieldParser("kDevices.csv")
MyReader.TextFieldType = Microsoft.VisualBasic.FileIO.FieldType.Delimited
MyReader.Delimiters = New String() {","}
Dim currentRow As String()
Dim rowP As Integer = 1
While Not MyReader.EndOfData
Try
currentRow = MyReader.ReadFields()
Dim cellP As Integer = 0
Dim nTemp As String = ""
For Each currentField As String In currentRow
Select Case cellP
Case 0
nameList.Add(currentField.Replace("""", ""))
Case 1
ipList.Add(currentField.Replace("""", ""))
End Select
cellP += 1
Next
Catch ex As Microsoft.VisualBasic.FileIO.MalformedLineException
MsgBox("Line " & ex.Message & " is invalid. Skipping")
End Try
rowP += 1
End While
End Using
Dim nameLAR As String() = nameList.ToArray
Dim ipLAR As String() = ipList.ToArray
ReDim Preserve thread(nameLAR.Length)
For i As Integer = 0 To nameLAR.Length - 1
Dim newDevice As New kDevice
Dim objNum = i
objDevice.Add(newDevice)
newDevice.kName = nameLAR(i)
newDevice.kIP = ipLAR(i)
If My.Computer.Network.Ping(newDevice.kIP) Then
newDevice.kStatus = "Online"
Else
newDevice.kStatus = "Loading"
End If
Dim str(4) As String
Dim itm As ListViewItem
str(0) = newDevice.kName
str(1) = newDevice.kIP
str(2) = newDevice.kStatus
str(3) = newDevice.kPings
itm = New ListViewItem(str)
If newDevice.kStatus = "Loading" Then
itm.BackColor = Color.Yellow
End If
ListView1.Items.Add(itm)
thread(objNum) = New System.Threading.Thread(Sub() Me.ipRefresh(objDevice(objNum), objNum))
Next
End Sub
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
For i As Integer = 0 To objDevice.Count - 1
thread(i).Start()
Next
End Sub
End Class
kDevice Class
Public Class kDevice
Private strkName As String
Private strkIP As String
Private strkStatus As String
Private strkLastStatus As String
Private strkPings As Integer = 0
Public Property kName As String
Get
Return strkName
End Get
Set(value As String)
strkName = value
End Set
End Property
Public Property kIP As String
Get
Return strkIP
End Get
Set(value As String)
strkIP = value
End Set
End Property
Public Property kStatus As String
Get
Return strkStatus
End Get
Set(value As String)
strkStatus = value
End Set
End Property
Public Property kPings As Integer
Get
Return strkPings
End Get
Set(value As Integer)
strkPings = value
End Set
End Property
End Class
The Error / Crash on Line 32 of my code which is when it tries to pass the update to the ListView Item
An unhandled exception of type 'System.ArgumentException'
occurred in Microsoft.VisualBasic.dll
Additional information: InvalidArgument=Value of '18'
is not valid for 'index'.
or
An unhandled exception of type 'System.NullReferenceException'
occurred in Microsoft.VisualBasic.dll
Additional information: Object reference not set to an instance
of an object.
If my code does not make sense, or at lease the idea of what I was trying to make it do, please let me know and I will explain whichever parts are unclear. Again thank you for looking over my issue.
Just a possible issue I noticed:
Dim str(4) As String
Dim itm As ListViewItem
str(0) = newDevice.kName
str(1) = newDevice.kIP
str(2) = newDevice.kStatus
str(3) = newDevice.kPings
itm = New ListViewItem(str)
If newDevice.kStatus = "Loading" Then
itm.BackColor = Color.Yellow
End If
ListView1.Items.Add(itm)
In this bit here, you declare str(4) which would be 5 possible indexes (remember it starts at zero), where you should have 4 (str(3)) . I don't think this is the whole issue, but just a small bit you should probably fix. You also may want to look into other ways to update the listview without setting
Me.CheckForIllegalCrossThreadCalls = False
Here's an awesome guide that helped me when I did my first multi threaded application: http://checktechno.blogspot.com/2012/11/multi-thread-for-newbies.html

ObjectListView adding items to it

I am a bit lost and I would like to add some items to a Fast ObjectListView. what I have is not working, and I cant seem to find anything online with vb.net samples
Dim LvItm As BrightIdeasSoftware.OLVListItem = lstMain.Items.Add("title")
With LvItm
.SubItems.Add("name")
.SubItems.Add("last")
.SubItems.Add("phone")
.SubItems.Add("address")
.EnsureVisible()
End With
ObjectListView works completely different from normal ListView, usually you dont add individual items.
In short:
- create columns
- set aspect names of created columns to property names of your objects
- point objectlistview to list of objects
See example below:
Imports BrightIdeasSoftware
Public Class Person
Public Property name As String
Public Property last As String
Public Property phone As String
Public Property address As String
End Class
Dim LvItm As New Person With {.name = "John",
.last = "Smith",
.phone = "555-69997-44",
.address = "Main Str. 1"}
Dim LvLst As New List(Of Person)
LvLst.Add(LvItm)
ObjectListView1.View = View.Details
ObjectListView1.Columns.Add(New OLVColumn With {.Text = "Name",
.AspectName = "name"})
ObjectListView1.Columns.Add(New OLVColumn With {.Text = "Last Name",
.AspectName = "last"})
ObjectListView1.Columns.Add(New OLVColumn With {.Text = "Phone",
.AspectName = "phone"})
ObjectListView1.Columns.Add(New OLVColumn With {.Text = "Address",
.AspectName = "address"})
ObjectListView1.SetObjects(LvLst)
With everything set-up you can add items to the list or manipulate in any way,
hitting ObjectListView1.SetObjects(LvLst) again to refresh view.
You can also add items to ObjectListView directly:
Dim p As New Person
p.name = "Steve"
p.last = "Wilson"
p.phone = "777-888-9987"
p.address = "First Str. 1"
ObjectListView1.AddObject(p)
Remember that items added directly were not added to your List(Of Person)