Frage

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.

War es hilfreich?

Lösung

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:

  • Title
    • Size
    • Last played
    • Rank

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"))
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top