Maybe the question appears to don't have any sense, but is just because this question: ObjectListView cast exception (for hit testing)
I would like to know if at execution-time there is a way to transform a ListViewItem
into a predefined Class with a predefined function that will return the Item and subitems to add it in an ObjectListView
control.
Something like this:
Dim LVI As New ListViewItem("My Item")
LVI.SubItems.AddRange({"My SubItem 1", "My SubItem 2"})
Dim OwnType = ' ListViewItem transformed into a own type.
FastObjectListView1.SetObjects(OwnType.GET())
Could see @Mur Haf answer here How to add a new item into ObjectListView? to make sense of what I'm trying to do, I just would like to automate a dynamic conversion without needing to write the Class just to add few strings in the ObjectListview
.
Let's say that the predefined class is for instance the model example, song
, from the OLV site:
Public Class Song
Public Sub New()
Me.New(String.Empty, DateTime.Now, 0)
End Sub
Public Sub New(title As String, lastPlayed As DateTime, rating As Integer)
Me.Title = title
Me.LastPlayed = lastPlayed
Me.Rating = rating
End Sub
Public Property Title() As String
Public Property LastPlayed() As DateTime
Public Property Rating() As Integer
Public Function GetSizeInMb() As Single
Return 1.2
End Function
End Class
The following assumes that the ListViewItem
is populated:
Note that the OLV method SetObjects
accepts a IEnumerable
object, so we'll return a List(T)
object. (Same as the "Get" method linked in the OP.) Now, add these extension methods to a Module
.
Public Module MyExtensions
<System.Runtime.CompilerServices.Extension()> _
Public Function ToSong(item As ListViewItem) As Song
Dim title As String = item.SubItems(0).Text
'Dim GetSizeInMb() As Single = Single.Parse(item.SubItems(1).Text) < -Skip, It 's a function in this example.
Dim lastPlayed = DateTime.Parse(item.SubItems(2).Text)
Dim rating = Integer.Parse(item.SubItems(3).Text)
Return New Song(title, lastPlayed, rating)
End Function
<System.Runtime.CompilerServices.Extension()> _
Public Function [Get](view As ListView) As List(Of Song)
Dim list As New List(Of Song)
For Each item As ListViewItem In view.Items
list.Add(item.ToSong())
Next
Return list
End Function
<System.Runtime.CompilerServices.Extension()> _
Public Function [Get](collection As IEnumerable(Of ListViewItem)) As List(Of Song)
Dim list As New List(Of Song)
For Each item As ListViewItem In collection
list.Add(item.ToSong())
Next
Return list
End Function
End Module
USAGE:
Single ListViewItem
:
Dim item As ListViewItem = 'Some listviewitem
Me.olvSongs.SetObjects({item.ToSong()})
MultipleListViewItem
:
Dim item1 As ListViewItem = 'Some listviewitem
Dim item2 As ListViewItem = 'Some listviewitem
Dim item3 As ListViewItem = 'Some listviewitem
Me.olvSongs.SetObjects({item1, item2, item3}.[Get]())
Or all items contained in a ListView
:
Dim view As ListView = 'Some listview
Me.olvSongs.SetObjects(view.[Get]())
Option 2 - "The interface way"
The next option is more dynamic. By implementing the interface IMergable(T)
in your predefined classes, all will support the Get
method.
Public Interface IMergable(Of T)
Sub Merge(item As T)
End Interface
Public Module MyExtensions
<System.Runtime.CompilerServices.Extension()> _
Public Function [Get](Of T)(item As ListViewItem) As List(Of T)
Dim list As New List(Of T)
Dim obj As T = Activator.CreateInstance(Of T)()
If (GetType(T).IsAssignableFrom(GetType(IMergable(Of ListViewItem)))) Then
DirectCast(obj, IMergable(Of ListViewItem)).Merge(item)
list.Add(obj)
End If
Return list
End Function
<System.Runtime.CompilerServices.Extension()> _
Public Function [Get](Of T)(view As ListView) As List(Of T)
Dim list As New List(Of T)
If (GetType(T).IsAssignableFrom(GetType(IMergable(Of ListViewItem)))) Then
Dim obj As T = Nothing
For Each item As ListViewItem In view.Items
obj = Activator.CreateInstance(Of T)()
DirectCast(obj, IMergable(Of ListViewItem)).Merge(item)
list.Add(obj)
Next
End If
Return list
End Function
End Module
IMPLEMENTATION:
Public Class Song
Implements IMergable(Of ListViewItem)
...
Private Sub Merge(item As ListViewItem) Implements IMergable(Of ListViewItem).Merge
If (item Is Nothing) Then
Throw New ArgumentNullException("item")
End If
Me.Title = item.SubItems(0).Text
'Me.GetSizeInMb = Single.Parse(item.SubItems(1).Text) < -Skip, It 's a function in this example.
Me.LastPlayed = DateTime.Parse(item.SubItems(2).Text)
Me.Rating = Integer.Parse(item.SubItems(3).Text)
End Sub
End Class
USAGE:
Me.olvSongs.SetObjects(myListView.[Get](Of Song)())
Me.olvSongs.SetObjects(myListViewItem.[Get](Of Song)())
Me.olvSongs.SetObjects(myListView.[Get](Of PreDefClassType1)())
Me.olvSongs.SetObjects(myListView.[Get](Of PreDefClassType2)())
Option 3 - "The reflection way"
This example only supports IConvertible data types.
Public Module MyExtensions
<System.Runtime.CompilerServices.Extension()> _
Public Function [Get](Of T)(view As ListView, ParamArray members As String()) As List(Of T)
Dim list As New List(Of ListViewItem)
For Each item As ListViewItem In view.Items
list.Add(item)
Next
Return list.Get(Of T)(members)
End Function
<System.Runtime.CompilerServices.Extension()> _
Public Function [Get](Of T)(item As ListViewItem, ParamArray members As String()) As List(Of T)
Return New ListViewItem() {item}.Get(Of T)(members)
End Function
<System.Runtime.CompilerServices.Extension()> _
Public Function [Get](Of T)(collection As IEnumerable(Of ListViewItem), ParamArray members As String()) As List(Of T)
Dim list As New List(Of T)
Dim length As Integer = (members.Length - 1)
If (length > -1) Then
Dim type As Type = GetType(T)
Dim info As KeyValuePair(Of MemberInfo, Type)() = New KeyValuePair(Of MemberInfo, Type)(length) {}
For index As Integer = 0 To length
For Each m As MemberInfo In type.GetMember(members(index), (MemberTypes.Method Or MemberTypes.Property), (BindingFlags.Public Or BindingFlags.Instance Or BindingFlags.DeclaredOnly))
If (m.MemberType = MemberTypes.Property) Then
With DirectCast(m, PropertyInfo)
If (.CanWrite AndAlso (.GetIndexParameters().Length = 0)) Then
info(index) = New KeyValuePair(Of MemberInfo, Type)(m, .PropertyType)
Exit For
End If
End With
ElseIf (m.MemberType = MemberTypes.Method) Then
With DirectCast(m, MethodInfo)
Dim params As ParameterInfo() = .GetParameters()
If (params.Length = 1) Then
info(index) = New KeyValuePair(Of MemberInfo, Type)(m, params(0).ParameterType)
Exit For
End If
End With
End If
Next
Next
For Each item As ListViewItem In collection
Dim obj As T = Activator.CreateInstance(Of T)()
Dim ict As Type = GetType(IConvertible)
For index As Integer = 0 To length
Dim pair As KeyValuePair(Of MemberInfo, Type) = info(index)
If (Not pair.Key Is Nothing) Then
If (ict.IsAssignableFrom(pair.Value)) Then
If (pair.Key.MemberType = MemberTypes.Property) Then
DirectCast(pair.Key, PropertyInfo).SetValue(obj, System.Convert.ChangeType(item.SubItems(index).Text, pair.Value), Nothing)
Else
DirectCast(pair.Key, MethodInfo).Invoke(obj, System.Convert.ChangeType(item.SubItems(index).Text, pair.Value))
End If
Else
'TODO: Support other data types.
'If (pair.Key.MemberType = MemberTypes.Property) Then
'DirectCast(pair.Key, PropertyInfo).SetValue(obj, item.SubItems(index).Text, Nothing)
'Else
'DirectCast(pair.Key, MethodInfo).Invoke(obj, New Object() {item.SubItems(index).Text})
'End If
End If
End If
Next
list.Add(obj)
Next
End If
Return list
End Function
End Module
USAGE:
Me.olvSongs.SetObjects(myListView.[Get](Of Song)("Title", "GetSizeInMb", "LastPlayed", "Rating"))
Me.olvSongs.SetObjects(myListViewItem.[Get](Of Song)("Title", "GetSizeInMb", "LastPlayed", "Rating"))
Me.olvSongs.SetObjects(myListView.[Get](Of PreDefClassType1)("Prop1", "Prop2", "Prop3"))
Me.olvSongs.SetObjects(myListView.[Get](Of PreDefClassType2)("PropA", "PropB", "PropC"))