Librería de Snippets para VB.NET !! (Compartan aquí sus snippets)

<< < (119/120) > >>

Eleкtro:
Comparto una forma que he ideado para automatizar la traducción, al idioma actual de la aplicación, los valores booleanos en un propertygrid (por ejemplo), mediante el uso clases de atributos.






El modo de empleo es muy sencillo:

Código
public class TestClass
 
<LocalizableBoolean>
<TypeConverter(GetType(LocalizableBooleanConverter))>
Public Property FeatureEnabled As Boolean = True
 
end class

Código
Me.PropertyGrid1.SelectedObject = new TestClass()

También se puede utilizar de esta forma alternativa para una representación arbitraria en los idiomas que se especifiquen mediante un string separado por comas (en este ejemplo, el español y el francés):

Código
<LocalizableBoolean("es, fr", "ssssí!!, Oui!", "nope!, Non!")>
<TypeConverter(GetType(LocalizableBooleanConverter))>
Public Property FeatureEnabled As Boolean = True




El código:

LocalizedBoolean.vb
Código
''' <summary>
''' Represents localized strings for <see langword="True"/> and <see langword="False"/> <see cref="Boolean"/> values.
''' </summary>
<DebuggerStepThrough>
Public NotInheritable Class LocalizedBoolean
 
   ''' <summary>
   ''' The <see cref="CultureInfo"/> that represents the region for
   ''' the localized strings in <see cref="LocalizedBoolean.True"/>
   ''' and <see cref="LocalizedBoolean.False"/> properties.
   ''' </summary>
   Public ReadOnly Property Culture As CultureInfo
 
   ''' <summary>
   ''' The localized string representation for <see langword="True"/> <see cref="Boolean"/> value.
   ''' </summary>
   Public ReadOnly Property [True] As String
 
   ''' <summary>
   ''' The localized string representation for <see langword="False"/> <see cref="Boolean"/> value.
   ''' </summary>
   Public ReadOnly Property [False] As String
 
   ''' <summary>
   ''' Initializes a new instance of the <see cref="LocalizedBoolean"/> class.
   ''' </summary>
   '''
   ''' <param name="culture">
   ''' The <see cref="CultureInfo"/> that represents the region for the localized strings.
   ''' </param>
   '''
   ''' <param name="trueString">
   ''' The localized string representation for <see langword="True"/> <see cref="Boolean"/> value.
   ''' </param>
   '''
   ''' <param name="falseString">
   ''' The localized string representation for <see langword="False"/> <see cref="Boolean"/> value.
   ''' </param>
   Public Sub New(culture As CultureInfo, trueString As String, falseString As String)
       If culture Is Nothing Then
           Throw New ArgumentNullException(paramName:=NameOf(culture))
       End If
       If String.IsNullOrWhiteSpace(trueString) Then
           Throw New ArgumentNullException(paramName:=NameOf(trueString))
       End If
       If String.IsNullOrWhiteSpace(falseString) Then
           Throw New ArgumentNullException(paramName:=NameOf(falseString))
       End If
 
       Me.Culture = culture
       Me.True = trueString
       Me.False = falseString
   End Sub
 
   ''' <summary>
   ''' Prevents a default instance of the <see cref="LocalizedBoolean"/> class from being created.
   ''' </summary>
   Private Sub New()
   End Sub
 
End Class

LocalizableBooleanAttribute.vb
Código:

''' <summary>
''' Specifies that a <see cref="Boolean"/> property can display localized string representations
''' for <see langword="True"/> and <see langword="False"/> values.
''' </summary>
<AttributeUsage(AttributeTargets.Property, AllowMultiple:=False, Inherited:=True)>
<DebuggerStepThrough>
Public NotInheritable Class LocalizableBooleanAttribute : Inherits Attribute

    ''' <summary>
    ''' Gets the localized boolean representations.
    ''' <para></para>
    ''' The dictionary Key is the ISO 639-1 two-letter code for the language.
    ''' </summary>
    Public ReadOnly Property Localizations As Dictionary(Of String, LocalizedBoolean)

    ''' <summary>
    ''' Initializes a new instance of the <see cref="LocalizedBoolean"/> class.
    ''' </summary>
    Public Sub New()
        Me.Localizations = New Dictionary(Of String, LocalizedBoolean)(StringComparison.OrdinalIgnoreCase) From {
            {"af", New LocalizedBoolean(CultureInfo.GetCultureInfo("af"), "Ja", "Nee")}, ' Afrikaans
            {"am", New LocalizedBoolean(CultureInfo.GetCultureInfo("am"), "እወዳለሁ", "አይደለሁ")}, ' Amharic
            {"ar", New LocalizedBoolean(CultureInfo.GetCultureInfo("ar"), "نعم", "لا")}, ' Arabic
            {"az", New LocalizedBoolean(CultureInfo.GetCultureInfo("az"), "Bəli", "Xeyr")}, ' Azerbaijani
            {"be", New LocalizedBoolean(CultureInfo.GetCultureInfo("be"), "Так", "Не")}, ' Belarusian
            {"bg", New LocalizedBoolean(CultureInfo.GetCultureInfo("bg"), "Да", "Не")}, ' Bulgarian
            {"bn", New LocalizedBoolean(CultureInfo.GetCultureInfo("bn"), "হ্যাঁ", "না")}, ' Bengali
            {"ca", New LocalizedBoolean(CultureInfo.GetCultureInfo("ca"), "Sí", "No")}, ' Catalan
            {"cs", New LocalizedBoolean(CultureInfo.GetCultureInfo("cs"), "Ano", "Ne")}, ' Czech
            {"cy", New LocalizedBoolean(CultureInfo.GetCultureInfo("cy"), "Ie", "Na")}, ' Welsh
            {"da", New LocalizedBoolean(CultureInfo.GetCultureInfo("da"), "Ja", "Nej")}, ' Danish
            {"de", New LocalizedBoolean(CultureInfo.GetCultureInfo("de"), "Ja", "Nein")}, ' German
            {"el", New LocalizedBoolean(CultureInfo.GetCultureInfo("el"), "Ναι", "Όχι")}, ' Greek
            {"en", New LocalizedBoolean(CultureInfo.GetCultureInfo("en"), "Yes", "No")}, ' English
            {"es", New LocalizedBoolean(CultureInfo.GetCultureInfo("es"), "Sí", "No")}, ' Spanish
            {"et", New LocalizedBoolean(CultureInfo.GetCultureInfo("et"), "Jah", "Ei")}, ' Estonian
            {"eu", New LocalizedBoolean(CultureInfo.GetCultureInfo("eu"), "Bai", "Ez")}, ' Basque
            {"fa", New LocalizedBoolean(CultureInfo.GetCultureInfo("fa"), "بله", "خیر")}, ' Persian
            {"fi", New LocalizedBoolean(CultureInfo.GetCultureInfo("fi"), "Kyllä", "Ei")}, ' Finnish
            {"fr", New LocalizedBoolean(CultureInfo.GetCultureInfo("fr"), "Oui", "Non")}, ' French
            {"ga", New LocalizedBoolean(CultureInfo.GetCultureInfo("ga"), "Tá", "Níl")}, ' Irish
            {"gd", New LocalizedBoolean(CultureInfo.GetCultureInfo("gd"), "Tha", "Chan eil")}, ' Scottish Gaelic
            {"gl", New LocalizedBoolean(CultureInfo.GetCultureInfo("gl"), "Si", "Non")}, ' Galician
            {"gu", New LocalizedBoolean(CultureInfo.GetCultureInfo("gu"), "હા", "ના")}, ' Gujarati
            {"hi", New LocalizedBoolean(CultureInfo.GetCultureInfo("hi"), "हाँ", "नहीं")}, ' Hindi
            {"hr", New LocalizedBoolean(CultureInfo.GetCultureInfo("hr"), "Da", "Ne")}, ' Croatian
            {"ht", New LocalizedBoolean(CultureInfo.GetCultureInfo("ht"), "Wi", "Pa")}, ' Haitian Creole
            {"hu", New LocalizedBoolean(CultureInfo.GetCultureInfo("hu"), "Igen", "Nem")}, ' Hungarian
            {"id", New LocalizedBoolean(CultureInfo.GetCultureInfo("id"), "Ya", "Tidak")}, ' Indonesian
            {"ig", New LocalizedBoolean(CultureInfo.GetCultureInfo("ig"), "Ee", "Mba")}, ' Igbo
            {"is", New LocalizedBoolean(CultureInfo.GetCultureInfo("is"), "Já", "Nei")}, ' Icelandic
            {"it", New LocalizedBoolean(CultureInfo.GetCultureInfo("it"), "Sì", "No")}, ' Italian
            {"ja", New LocalizedBoolean(CultureInfo.GetCultureInfo("ja"), "はい", "いいえ")}, ' Japanese
            {"jv", New LocalizedBoolean(CultureInfo.GetCultureInfo("jv"), "Iya", "Ora")}, ' Javanese
            {"kk", New LocalizedBoolean(CultureInfo.GetCultureInfo("kk"), "Иә", "Жоқ")}, ' Kazakh
            {"km", New LocalizedBoolean(CultureInfo.GetCultureInfo("km"), "បាទ/ចាស", "ទេ")}, ' Khmer
            {"kn", New LocalizedBoolean(CultureInfo.GetCultureInfo("kn"), "ಹೌದು", "ಇಲ್ಲ")}, ' Kannada
            {"ko", New LocalizedBoolean(CultureInfo.GetCultureInfo("ko"), "예", "아니오")}, ' Korean
            {"ku", New LocalizedBoolean(CultureInfo.GetCultureInfo("ku"), "Belê", "Na")}, ' Kurdish (Kurmanji)
            {"ky", New LocalizedBoolean(CultureInfo.GetCultureInfo("ky"), "Ооба", "Жок")}, ' Kyrgyz
            {"la", New LocalizedBoolean(CultureInfo.GetCultureInfo("la"), "Ita", "Non")}, ' Latin
            {"lg", New LocalizedBoolean(CultureInfo.GetCultureInfo("lg"), "Yee", "Nedda")}, ' Luganda
            {"lt", New LocalizedBoolean(CultureInfo.GetCultureInfo("lt"), "Taip", "Ne")}, ' Lithuanian
            {"lv", New LocalizedBoolean(CultureInfo.GetCultureInfo("lv"), "Jā", "Nē")}, ' Latvian
            {"mg", New LocalizedBoolean(CultureInfo.GetCultureInfo("mg"), "Eny", "Tsia")}, ' Malagasy
            {"mi", New LocalizedBoolean(CultureInfo.GetCultureInfo("mi"), "Āe", "Kāo")}, ' Maori
            {"mk", New LocalizedBoolean(CultureInfo.GetCultureInfo("mk"), "Да", "Не")}, ' Macedonian
            {"ml", New LocalizedBoolean(CultureInfo.GetCultureInfo("ml"), "അതെ", "ഇല്ല")}, ' Malayalam
            {"mn", New LocalizedBoolean(CultureInfo.GetCultureInfo("mn"), "Тийм", "Үгүй")}, ' Mongolian
            {"mr", New LocalizedBoolean(CultureInfo.GetCultureInfo("mr"), "होय", "नाही")}, ' Marathi
            {"ms", New LocalizedBoolean(CultureInfo.GetCultureInfo("ms"), "Ya", "Tidak")}, ' Malay
            {"mt", New LocalizedBoolean(CultureInfo.GetCultureInfo("mt"), "Iva", "Le")}, ' Maltese
            {"my", New LocalizedBoolean(CultureInfo.GetCultureInfo("my"), "ဟုတ်ကဲ့", "မဟုတ်ဘူး")}, ' Burmese
            {"ne", New LocalizedBoolean(CultureInfo.GetCultureInfo("ne"), "हो", "होइन")}, ' Nepali
            {"nl", New LocalizedBoolean(CultureInfo.GetCultureInfo("nl"), "Ja", "Nee")}, ' Dutch
            {"no", New LocalizedBoolean(CultureInfo.GetCultureInfo("no"), "Ja", "Nei")}, ' Norwegian
            {"ny", New LocalizedBoolean(CultureInfo.GetCultureInfo("ny"), "Yewo", "Ayawo")}, ' Chichewa
            {"pa", New LocalizedBoolean(CultureInfo.GetCultureInfo("pa"), "ਹਾਂ", "ਨਹੀਂ")}, ' Punjabi
            {"pl", New LocalizedBoolean(CultureInfo.GetCultureInfo("pl"), "Tak", "Nie")}, ' Polish
            {"ps", New LocalizedBoolean(CultureInfo.GetCultureInfo("ps"), "هو", "نه")}, ' Pashto
            {"pt", New LocalizedBoolean(CultureInfo.GetCultureInfo("pt"), "Sim", "Não")}, ' Portuguese
            {"rm", New LocalizedBoolean(CultureInfo.GetCultureInfo("rm"), "Gia", "Betg")}, ' Romansh
            {"ro", New LocalizedBoolean(CultureInfo.GetCultureInfo("ro"), "Da", "Nu")}, ' Romanian
            {"ru", New LocalizedBoolean(CultureInfo.GetCultureInfo("ru"), "Да", "Нет")}, ' Russian
            {"sd", New LocalizedBoolean(CultureInfo.GetCultureInfo("sd"), "هاڻي", "نه")}, ' Sindhi
            {"si", New LocalizedBoolean(CultureInfo.GetCultureInfo("si"), "ඔව්", "නැත")}, ' Sinhala
            {"sk", New LocalizedBoolean(CultureInfo.GetCultureInfo("sk"), "Áno", "Nie")}, ' Slovak
            {"sl", New LocalizedBoolean(CultureInfo.GetCultureInfo("sl"), "Da", "Ne")}, ' Slovenian
            {"sm", New LocalizedBoolean(CultureInfo.GetCultureInfo("sm"), "Ioe", "Leai")}, ' Samoan
            {"sn", New LocalizedBoolean(CultureInfo.GetCultureInfo("sn"), "Yebo", "Cha")}, ' Shona
            {"so", New LocalizedBoolean(CultureInfo.GetCultureInfo("so"), "Haa", "Maya")}, ' Somali
            {"sq", New LocalizedBoolean(CultureInfo.GetCultureInfo("sq"), "Po", "Jo")}, ' Albanian
            {"sr", New LocalizedBoolean(CultureInfo.GetCultureInfo("sr"), "Да", "Не")}, ' Serbian (Cyrillic)
            {"su", New LocalizedBoolean(CultureInfo.GetCultureInfo("su"), "Iya", "Teu")}, ' Sundanese
            {"sv", New LocalizedBoolean(CultureInfo.GetCultureInfo("sv"), "Ja", "Nej")}, ' Swedish
            {"sw", New LocalizedBoolean(CultureInfo.GetCultureInfo("sw"), "Ndiyo", "Hapana")}, ' Swahili
            {"ta", New LocalizedBoolean(CultureInfo.GetCultureInfo("ta"), "ஆம்", "இல்லை")}, ' Tamil
            {"te", New LocalizedBoolean(CultureInfo.GetCultureInfo("te"), "అవును", "కాదు")}, ' Telugu
            {"tg", New LocalizedBoolean(CultureInfo.GetCultureInfo("tg"), "Ҳа", "Не")}, ' Tajik
            {"th", New LocalizedBoolean(CultureInfo.GetCultureInfo("th"), "ใช่", "ไม่")}, ' Thai
            {"ti", New LocalizedBoolean(CultureInfo.GetCultureInfo("ti"), "እወ", "አይወ")}, ' Tigrinya
            {"tk", New LocalizedBoolean(CultureInfo.GetCultureInfo("tk"), "Hawa", "Ýok")}, ' Turkmen
            {"to", New LocalizedBoolean(CultureInfo.GetCultureInfo("to"), "ʻIo", "ʻEa")}, ' Tongan
            {"tr", New LocalizedBoolean(CultureInfo.GetCultureInfo("tr"), "Evet", "Hayır")}, ' Turkish
            {"tt", New LocalizedBoolean(CultureInfo.GetCultureInfo("tt"), "Әйе", "Юк")}, ' Tatar
            {"ug", New LocalizedBoolean(CultureInfo.GetCultureInfo("ug"), "ھەئە", "ياق")}, ' Uighur
            {"uk", New LocalizedBoolean(CultureInfo.GetCultureInfo("uk"), "Так", "Ні")}, ' Ukrainian
            {"ur", New LocalizedBoolean(CultureInfo.GetCultureInfo("ur"), "جی ہاں", "نہیں")}, ' Urdu
            {"uz", New LocalizedBoolean(CultureInfo.GetCultureInfo("uz"), "Ha", "Yo'q")}, ' Uzbek
            {"vi", New LocalizedBoolean(CultureInfo.GetCultureInfo("vi"), "Có", "Không")}, ' Vietnamese
            {"xh", New LocalizedBoolean(CultureInfo.GetCultureInfo("xh"), "Ewe", "Hayi")}, ' Xhosa
            {"yi", New LocalizedBoolean(CultureInfo.GetCultureInfo("yi"), "יאָ", "ניי")}, ' Yiddish
            {"yo", New LocalizedBoolean(CultureInfo.GetCultureInfo("yo"), "Bẹẹni", "Bẹẹkoo")}, ' Yoruba
            {"zh", New LocalizedBoolean(CultureInfo.GetCultureInfo("zh"), "是", "不")}, ' Chinese (Simplified)
            {"zu", New LocalizedBoolean(CultureInfo.GetCultureInfo("zu"), "Yebo", "Cha")} ' Zulu
        }
    End Sub

    ''' <summary>
    ''' Initializes a new instance of the <see cref="LocalizedBoolean"/> class.
    ''' </summary>
    '''
    ''' <param name="cultureNames">
    ''' A comma-separated value of the ISO 639-1 two-letter code languages (e.g.: "en,es,fr").
    ''' </param>
    '''
    ''' <param name="trueStrings">
    ''' A comma-separated value of the localized string representation for "True" boolean value (e.g.: "Yes,Sí,Oui").
    ''' </param>
    '''
    ''' <param name="falseStrings">
    ''' A comma-separated value of the localized string representation for "False" boolean value (e.g.: "No,No,Non").
    ''' </param>
    Public Sub New(cultureNames As String, trueStrings As String, falseStrings As String)
        Me.New()

        If String.IsNullOrWhiteSpace(cultureNames) Then
            Throw New ArgumentNullException(paramName:=NameOf(cultureNames))
        End If
        If String.IsNullOrWhiteSpace(trueStrings) Then
            Throw New ArgumentNullException(paramName:=NameOf(trueStrings))
        End If
        If String.IsNullOrWhiteSpace(falseStrings) Then
            Throw New ArgumentNullException(paramName:=NameOf(falseStrings))
        End If

        Dim cultureNamesArray As String() = cultureNames.Split({","c}, StringSplitOptions.RemoveEmptyEntries)
        Dim trueStringsArray As String() = trueStrings.Split({","c}, StringSplitOptions.RemoveEmptyEntries)
        Dim falseStringsArray As String() = falseStrings.Split({","c}, StringSplitOptions.RemoveEmptyEntries)

        If cultureNamesArray.Length <> trueStringsArray.Length OrElse cultureNamesArray.Length <> falseStringsArray.Length Then
            Throw New InvalidOperationException("The comma-separated values must have the same amount of tokens.")
        End If

        For i As Integer = 0 To cultureNamesArray.Length - 1
            Dim cultureName As String = cultureNamesArray(i).Trim()
            Dim trueString As String = trueStringsArray(i).Trim()
            Dim falseString As String = falseStringsArray(i).Trim()

            If cultureName.Length <> 2 Then
                Throw New InvalidOperationException("The culture name must be a ISO 639-1 two-letter code.")
            End If

            Dim localizedBoolean As New LocalizedBoolean(CultureInfo.GetCultureInfo(cultureName), trueString, falseString)
            If Me.Localizations.ContainsKey(cultureName) Then
                Me.Localizations(cultureName) = localizedBoolean
            Else
                Me.Localizations.Add(cultureName, localizedBoolean)
            End If
        Next
    End Sub

End Class

LocalizableBooleanConverter.vb
Código
''' <summary>
''' Provides conversion functionality between Boolean values and localized strings representing "True" and "False" boolean values.
''' </summary>
Public Class LocalizableBooleanConverter : Inherits TypeConverter
 
   ''' <summary>
   ''' The localized string representation for "True" boolean value.
   ''' </summary>
   Private trueString As String = "Yes"
 
   ''' <summary>
   ''' The localized string representation for "False" boolean value.
   ''' </summary>
   Private falseString As String = "No"
 
   ''' <summary>
   ''' Returns whether this converter can convert an object of the given type to the type of this converter,
   ''' using the specified context.
   ''' </summary>
   '''
   ''' <param name="context">
   ''' An <see cref="ITypeDescriptorContext"/> that provides a format context.
   ''' </param>
   '''
   ''' <param name="sourceType">
   ''' A <see cref="Type" /> that represents the type you want to convert from.
   ''' </param>
   '''
   ''' <returns>
   ''' <see langword="True"/> if this converter can perform the conversion; otherwise, <see langword="False"/>.
   ''' </returns>
   Public Overrides Function CanConvertFrom(context As ITypeDescriptorContext, sourceType As Type) As Boolean
 
       Return sourceType = GetType(String) OrElse MyBase.CanConvertFrom(context, sourceType)
 
   End Function
 
   ''' <summary>
   ''' Returns whether this converter can convert the object to the specified type, using the specified context.
   ''' </summary>
   '''
   ''' <param name="context">
   ''' An <see cref="ITypeDescriptorContext"/> that provides a format context.
   ''' </param>
   '''
   ''' <param name="destinationType">
   ''' A <see cref="Type"/> that represents the type you want to convert to.
   ''' </param>
   '''
   ''' <returns>
   ''' <see langword="True"/> if this converter can perform the conversion; otherwise, <see langword="False"/>.
   ''' </returns>
   Public Overrides Function CanConvertTo(context As ITypeDescriptorContext, destinationType As Type) As Boolean
 
       Return destinationType = GetType(String) OrElse MyBase.CanConvertTo(context, destinationType)
 
   End Function
 
   ''' <summary>
   ''' Converts the given object to the type of this converter, using the specified context and culture information.
   ''' </summary>
   '''
   ''' <param name="context">
   ''' An <see cref="ITypeDescriptorContext"/> that provides a format context.
   ''' </param>
   '''
   ''' <param name="culture">
   ''' The <see cref="CultureInfo"/> to use as the current culture.
   ''' </param>
   '''
   ''' <param name="value">
   ''' The <see cref="Object"/> to convert.
   ''' </param>
   '''
   ''' <returns>
   ''' An <see cref="Object"/> that represents the converted value.
   ''' </returns>
   <DebuggerStepperBoundary>
   Public Overrides Function ConvertFrom(context As ITypeDescriptorContext, culture As Globalization.CultureInfo, value As Object) As Object
 
       If TypeOf value Is String Then
           Dim stringValue As String = DirectCast(value, String)
           If String.Equals(stringValue, Me.trueString, StringComparison.OrdinalIgnoreCase) Then
               Return True
           ElseIf String.Equals(stringValue, Me.FalseString, StringComparison.OrdinalIgnoreCase) Then
               Return False
           End If
       End If
 
       Return MyBase.ConvertFrom(context, culture, value)
 
   End Function
 
   ''' <summary>
   ''' Converts the given value object to the specified type, using the specified context and culture information.
   ''' </summary>
   '''
   ''' <param name="context">
   ''' An <see cref="ITypeDescriptorContext"/> that provides a format context.
   ''' </param>
   '''
   ''' <param name="culture">
   ''' A <see cref="CultureInfo"/>. If null is passed, the current culture is assumed.
   ''' </param>
   '''
   ''' <param name="value">
   ''' The <see cref="Object"/> to convert.
   ''' </param>
   '''
   ''' <param name="destinationType">
   ''' The <see cref="Type"/> to convert the <paramref name="value"/> parameter to.
   ''' </param>
   '''
   ''' <returns>
   ''' An <see cref="Object"/> that represents the converted value.
   ''' </returns>
   <DebuggerStepperBoundary>
   Public Overrides Function ConvertTo(context As ITypeDescriptorContext, culture As Globalization.CultureInfo, value As Object, destinationType As Type) As Object
 
       If context IsNot Nothing Then
           Dim attributes As IEnumerable(Of LocalizableBooleanAttribute) =
               context.PropertyDescriptor.Attributes.OfType(Of LocalizableBooleanAttribute)
 
           For Each attr As LocalizableBooleanAttribute In attributes
               Dim uiCulture As CultureInfo = My.Application.UICulture
               Dim localizedBoolean As LocalizedBoolean = Nothing
               If attr.Localizations.ContainsKey(uiCulture.TwoLetterISOLanguageName) Then
                   localizedBoolean = attr.Localizations(uiCulture.TwoLetterISOLanguageName)
               End If
 
               If localizedBoolean IsNot Nothing Then
                   Me.trueString = localizedBoolean.True
                   Me.falseString = localizedBoolean.False
               End If
           Next
       End If
 
       If destinationType = GetType(String) Then
           If TypeOf value Is Boolean Then
               Dim boolValue As Boolean = value
               Return If(boolValue, Me.trueString, Me.falseString)
           End If
       End If
 
       Return MyBase.ConvertTo(context, culture, value, destinationType)
 
   End Function
 
   ''' <summary>
   ''' Returns a collection of standard values for the data type this type converter is designed for when provided with a format context.
   ''' </summary>
   '''
   ''' <param name="context">
   ''' An <see cref="ITypeDescriptorContext"/> that provides a format context that can be used to
   ''' extract additional information about the environment from which this converter is invoked.
   ''' <para></para>
   ''' This parameter or properties of this parameter can be null.
   ''' </param>
   '''
   ''' <returns>
   ''' A <see cref="StandardValuesCollection"/> that holds a standard set of valid values,
   ''' or <see langword="null" /> if the data type does not support a standard set of values.
   ''' </returns>
   Public Overrides Function GetStandardValues(context As ITypeDescriptorContext) As StandardValuesCollection
 
       Return New StandardValuesCollection(New Boolean() {True, False})
 
   End Function
 
   ''' <summary>
   ''' Returns whether this object supports a standard set of values that can be picked from a list, using the specified context.
   ''' </summary>
   '''
   ''' <param name="context">
   ''' An <see cref="ITypeDescriptorContext" /> that provides a format context.
   ''' </param>
   '''
   ''' <returns>
   ''' <see langword="True"/> if <see cref="TypeConverter.GetStandardValues"/> should be called to
   ''' find a common set of values the object supports; otherwise, <see langword="False"/>.
   ''' </returns>
   Public Overrides Function GetStandardValuesSupported(context As ITypeDescriptorContext) As Boolean
 
       Return True
 
   End Function
 
End Class

NOTA: El diccionario con los idiomas y sus equivalentes para "Sí" y "No", lo ha generado ChatGPT. Puede haber fallos en las traducciones, o en los códigos ISO 639-1 de dos letras. Además, faltaría añadir muchos más idiomas: https://en.wikipedia.org/wiki/List_of_ISO_639_language_codes

Eleкtro:
Comparto otro type converter, para convertir los nombres de los valores de una Enum, a una representación amistosa para mostrarlos, por ejemplo, en un propertygrid.

Este convertidor está optimizado para nombres de enumeración escritos en upper/lower snake case y upper/lower camel case.

Las palabras se separan con un espacio en blanco convencional, y los guiones bajos se reemplazan por un espacio en blanco unicode.

Ejemplo de uso:

Código
<TypeConverter(GetType(EnumNameFormatterConverter))>
Public Enum TestEnum
   MyUpperCamelCaseName
   myLowerCamelCaseName
   My_Upper_Snake_Case_Name
   my_lower_snake_case_name
 
   MyMixed_value123_WTF456wtf_
 
   ___rare_case_STRANGE_Name___________123_aZ_Az_4_5_6_
End Enum

Código
<DefaultValue(TestEnum.MyUpperCamelCaseName)>
Public Property Test As TestEnum = TestEnum.MyUpperCamelCaseName

Sin formato:


Con formato:



El código:

EnumNameFormatterConverter.vb
Código
Imports System.ComponentModel
Imports System.Globalization
Imports System.Runtime.InteropServices
Imports System.Text
 
''' <summary>
''' Provides conversion functionality between the value names of an <see cref="[Enum]"/> to a friendly string representation.
''' <para></para>
''' This converter is optimized for enum names written in either upper/lower snake case or upper/lower camel case:
''' <list type="bullet">
'''     <item><description>Snake case: Each word is separated by underscores (e.g.: "My_Value").</description></item>
'''     <item><description>Camel case: Each word is separated by a capitalized letter (e.g.: "MyValue").</description></item>
''' </list>
''' </summary>
Public NotInheritable Class EnumNameFormatterConverter : Inherits EnumConverter
 
   ''' <summary>
   ''' Initializes a new instance of the <see cref="EnumNameFormatterConverter"/> class.
   ''' </summary>
   ''' <param name="type">A <see cref="T:System.Type" /> that represents the type of enumeration to associate with this enumeration converter.</param>
   Public Sub New(type As Type)
       MyBase.New(type)
   End Sub
 
   ''' <summary>
   ''' Returns whether this converter can convert an object of the given type to the type of this converter,
   ''' using the specified context.
   ''' </summary>
   '''
   ''' <param name="context">
   ''' An <see cref="ITypeDescriptorContext"/> that provides a format context.
   ''' </param>
   '''
   ''' <param name="sourceType">
   ''' A <see cref="Type" /> that represents the type you want to convert from.
   ''' </param>
   '''
   ''' <returns>
   ''' <see langword="True"/> if this converter can perform the conversion; otherwise, <see langword="False"/>.
   ''' </returns>
   Public Overrides Function CanConvertFrom(context As ITypeDescriptorContext, sourceType As Type) As Boolean
 
       Return sourceType Is GetType(String) OrElse
              MyBase.CanConvertFrom(context, sourceType)
 
   End Function
 
   ''' <summary>
   ''' Returns whether this converter can convert the object to the specified type, using the specified context.
   ''' </summary>
   '''
   ''' <param name="context">
   ''' An <see cref="ITypeDescriptorContext"/> that provides a format context.
   ''' </param>
   '''
   ''' <param name="destinationType">
   ''' A <see cref="Type"/> that represents the type you want to convert to.
   ''' </param>
   '''
   ''' <returns>
   ''' <see langword="True"/> if this converter can perform the conversion; otherwise, <see langword="False"/>.
   ''' </returns>
   Public Overrides Function CanConvertTo(context As ITypeDescriptorContext, destinationType As Type) As Boolean
 
       Return destinationType Is GetType(String) OrElse
              MyBase.CanConvertTo(context, destinationType)
 
   End Function
 
   ''' <summary>
   ''' Converts the given object to the type of this converter, using the specified context and culture information.
   ''' </summary>
   '''
   ''' <param name="context">
   ''' An <see cref="ITypeDescriptorContext"/> that provides a format context.
   ''' </param>
   '''
   ''' <param name="culture">
   ''' The <see cref="CultureInfo"/> to use as the current culture.
   ''' </param>
   '''
   ''' <param name="value">
   ''' The <see cref="Object"/> to convert.
   ''' </param>
   '''
   ''' <returns>
   ''' An <see cref="Object"/> that represents the converted value.
   ''' </returns>
   <DebuggerStepThrough>
   Public Overrides Function ConvertFrom(context As ITypeDescriptorContext, culture As CultureInfo, value As Object) As Object
 
       If TypeOf value Is String Then
           value = DirectCast(value, String).Replace(" ", "").Replace(Convert.ToChar(&H205F), "_"c)
           Return [Enum].Parse(Me.EnumType, value, ignoreCase:=True)
       End If
 
       Return MyBase.ConvertFrom(context, culture, value)
 
   End Function
 
   ''' <summary>
   ''' Converts the given value object to the specified type, using the specified context and culture information.
   ''' </summary>
   '''
   ''' <param name="context">
   ''' An <see cref="ITypeDescriptorContext"/> that provides a format context.
   ''' </param>
   '''
   ''' <param name="culture">
   ''' A <see cref="CultureInfo"/>. If null is passed, the current culture is assumed.
   ''' </param>
   '''
   ''' <param name="value">
   ''' The <see cref="Object"/> to convert.
   ''' </param>
   '''
   ''' <param name="destinationType">
   ''' The <see cref="Type"/> to convert the <paramref name="value"/> parameter to.
   ''' </param>
   '''
   ''' <returns>
   ''' An <see cref="Object"/> that represents the converted value.
   ''' </returns>
   <DebuggerStepThrough>
   Public Overrides Function ConvertTo(context As ITypeDescriptorContext, culture As CultureInfo, value As Object, destinationType As Type) As Object
 
       If destinationType = GetType(String) Then
           Dim name As String = [Enum].GetName(value.GetType(), value)
           If Not String.IsNullOrEmpty(name) Then
               Return Me.FormatName(name)
           End If
       End If
 
       Return MyBase.ConvertTo(context, culture, value, destinationType)
 
   End Function
 
   ''' <summary>
   ''' Formats the name of a <see cref="[Enum]"/> value to a friendly name.
   ''' </summary>
   '''
   ''' <param name="name">
   ''' <see cref="[Enum]"/> value name.
   ''' </param>
   '''
   ''' <returns>
   ''' The resulting friendly name.
   ''' </returns>
   <DebuggerStepThrough>
   Private Function FormatName(name As String) As String
       Dim sb As New StringBuilder()
       Dim previousChar As Char
       Dim previousCharIsWhiteSpace As Boolean
       Dim previousCharIsUpperLetter As Boolean
       Dim previousCharIsDigit As Boolean
       Dim lastParsedCharIsUnderscore As Boolean
       Dim firstCapitalizedLetterIsAdded As Boolean
 
       For i As Integer = 0 To name.Length - 1
           Dim c As Char = name(i)
           If i = 0 Then
               If c.Equals("_"c) Then
                   sb.Append(Convert.ToChar(Convert.ToChar(&H205F)))
                   lastParsedCharIsUnderscore = True
               Else
                   sb.Append(Char.ToUpper(c))
                   firstCapitalizedLetterIsAdded = True
               End If
               Continue For
           End If
 
           previousChar = sb.Chars(sb.Length - 1)
           previousCharIsWhiteSpace = previousChar.Equals(" "c) OrElse previousChar.Equals(Convert.ToChar(&H205F))
           previousCharIsUpperLetter = Char.IsUpper(previousChar)
           previousCharIsDigit = Char.IsDigit(previousChar)
 
           If Char.IsLetter(c) Then
               If previousCharIsDigit AndAlso Not previousCharIsWhiteSpace Then
                   sb.Append(" "c)
               End If
 
               If Char.IsUpper(c) Then
                   If previousCharIsUpperLetter Then
                       sb.Append(c)
                   ElseIf Not previousCharIsWhiteSpace Then
                       sb.Append(" "c)
                       sb.Append(c)
                   Else
                       sb.Append(c)
                   End If
                   firstCapitalizedLetterIsAdded = True
 
               Else
                   If Not firstCapitalizedLetterIsAdded Then
                       sb.Append(Char.ToUpper(c))
                       firstCapitalizedLetterIsAdded = True
                   Else
                       sb.Append(c)
                   End If
 
               End If
 
           ElseIf Char.IsDigit(c) Then
               If Not previousCharIsDigit AndAlso Not previousCharIsWhiteSpace Then
                   sb.Append(" "c)
               End If
               sb.Append(c)
 
           ElseIf c.Equals("_"c) Then
               If lastParsedCharIsUnderscore OrElse Not previousCharIsWhiteSpace Then
                   sb.Append(Convert.ToChar(&H205F)) ' Unicode white-space: "&#8195;"
                   lastParsedCharIsUnderscore = True
               End If
 
           Else
               sb.Append(c)
               lastParsedCharIsUnderscore = False
 
           End If
 
       Next i
 
       Return sb.ToString()
   End Function
 
End Class
 

Eleкtro:
Comparto un enfoque y uso alternativo al código que he publicado arriba. Este enfoque nos permite atribuir nombres específicos a una enumeración para mostrarlos en un property grid.

Modo de empleo:

Código
Imports System.Componentmodel
 
<TypeConverter(GetType(EnumDescriptionConverter))>
Public Enum TestEnum
   <Description("My Upper Camel Case Name")> MyUpperCamelCaseName = 1
   <Description("My Lower Camel Case Name")> myLowerCamelCaseName = 2
   <Description("My Upper Snake Case Name")> My_Upper_Snake_Case_Name = 3
   <Description("My lower snake case Name")> my_lower_snake_case_Name = 4
   <Description("My Mixed value 123 QWERTY 456 wtf_")> MyMixed_value123_QWERTY456wtf_ = 5
   <Description("Rare case STRANGE Name 123 aZ Az 456")> ___rare_case_STRANGE_Name___________123_aZ_Az_4_5_6_ = 6
End Enum

Código
<DefaultValue(TestEnum.MyUpperCamelCaseName)>
Public Property Test As TestEnum = TestEnum.MyUpperCamelCaseName

El código:

Código
Imports System.ComponentModel
Imports System.Globalization
Imports System.Reflection
 
Public NotInheritable Class EnumDescriptionConverter : Inherits EnumConverter
 
''' <summary>
''' Initializes a new instance of the <see cref="EnumDescriptionConverter"/> class.
''' </summary>
''' <param name="type">A <see cref="T:System.Type" /> that represents the type of enumeration to associate with this enumeration converter.</param>
Public Sub New(type As Type)
MyBase.New(type)
End Sub
 
''' <summary>
''' Returns whether this converter can convert the object to the specified type, using the specified context.
''' </summary>
'''
''' <param name="context">
''' An <see cref="ITypeDescriptorContext"/> that provides a format context.
''' </param>
'''
''' <param name="destinationType">
''' A <see cref="Type"/> that represents the type you want to convert to.
''' </param>
'''
''' <returns>
''' <see langword="True"/> if this converter can perform the conversion; otherwise, <see langword="False"/>.
''' </returns>
Public Overrides Function CanConvertTo(context As ITypeDescriptorContext, destinationType As Type) As Boolean
 
Return destinationType Is GetType(String) OrElse
  MyBase.CanConvertTo(context, destinationType)
 
End Function
 
''' <summary>
''' Returns whether this converter can convert an object of the given type to the type of this converter,
''' using the specified context.
''' </summary>
'''
''' <param name="context">
''' An <see cref="ITypeDescriptorContext"/> that provides a format context.
''' </param>
'''
''' <param name="sourceType">
''' A <see cref="Type" /> that represents the type you want to convert from.
''' </param>
'''
''' <returns>
''' <see langword="True"/> if this converter can perform the conversion; otherwise, <see langword="False"/>.
''' </returns>
Public Overrides Function CanConvertFrom(context As ITypeDescriptorContext, sourceType As Type) As Boolean
 
Return sourceType Is GetType(String) OrElse
  MyBase.CanConvertFrom(context, sourceType)
 
End Function
 
''' <summary>
''' Converts the given value object to the specified type, using the specified context and culture information.
''' </summary>
'''
''' <param name="context">
''' An <see cref="ITypeDescriptorContext"/> that provides a format context.
''' </param>
'''
''' <param name="culture">
''' A <see cref="CultureInfo"/>. If null is passed, the current culture is assumed.
''' </param>
'''
''' <param name="value">
''' The <see cref="Object"/> to convert.
''' </param>
'''
''' <param name="destinationType">
''' The <see cref="Type"/> to convert the <paramref name="value"/> parameter to.
''' </param>
'''
''' <returns>
''' An <see cref="Object"/> that represents the converted value.
''' </returns>
<DebuggerStepThrough>
Public Overrides Function ConvertTo(context As ITypeDescriptorContext, culture As CultureInfo, value As Object, destinationType As Type) As Object
 
Dim fi As FieldInfo = Me.EnumType.GetField([Enum].GetName(Me.EnumType, value))
Dim dna As DescriptionAttribute = CType(Attribute.GetCustomAttribute(fi, GetType(DescriptionAttribute)), DescriptionAttribute)
Return If(dna IsNot Nothing, dna.Description, value.ToString())
 
End Function
 
''' <summary>
''' Converts the given object to the type of this converter, using the specified context and culture information.
''' </summary>
'''
''' <param name="context">
''' An <see cref="ITypeDescriptorContext"/> that provides a format context.
''' </param>
'''
''' <param name="culture">
''' The <see cref="CultureInfo"/> to use as the current culture.
''' </param>
'''
''' <param name="value">
''' The <see cref="Object"/> to convert.
''' </param>
'''
''' <returns>
''' An <see cref="Object"/> that represents the converted value.
''' </returns>
<DebuggerStepThrough>
Public Overrides Function ConvertFrom(context As ITypeDescriptorContext, culture As CultureInfo, value As Object) As Object
 
For Each fi As FieldInfo In Me.EnumType.GetFields()
Dim dna As DescriptionAttribute = CType(Attribute.GetCustomAttribute(fi, GetType(DescriptionAttribute)), DescriptionAttribute)
If (dna IsNot Nothing) AndAlso DirectCast(value, String) = dna.Description Then
Return [Enum].Parse(Me.EnumType, fi.Name, ignoreCase:=False)
End If
Next fi
 
Return [Enum].Parse(Me.EnumType, DirectCast(value, String))
 
End Function
 
End Class
 

Eleкtro:
Clase DllExportAttribute

La clase DllExportAttribute indica que un método puede ser exportado como una función de C, desde una biblioteca de enlace dinámico (DLL) de .NET, lo que hace que el método sea invocable desde código no administrado.

La clase DllExportAttribute está destinada únicamente a replicar y mejorar la clase de atributo DllExportAttribute de plugins como:

DllExport de 3FUnmanagedExports de HuajitechUnmanagedExports.Repack.Upgrade de StevenEngland
Permitiendo que un programador pueda utilizar esta clase de atributo sin necesidad de tener el plugin instalado en sus proyectos de Visual Studio.

Sigue siendo estrictamente necesario utilizar alguno de los proyectos mencionados para habilitar la exportación de funciones .NET.


Capturas de pantalla de la documentación interactiva:












El código fuente:

Código
' ***********************************************************************
' Author   : ElektroStudios
' Modified : 16-January-2025
' ***********************************************************************
 
#Region " Option Statements "
 
Option Strict On
Option Explicit On
Option Infer Off
 
#End Region
 
#Region " Usage Examples "
 
' VB.NET
' <DllExport(NameOf(MyStringFunction), CallingConvention.StdCall)>
' Public Shared Function MyStringFunction() As <MarshalAs(UnmanagedType.BStr)> String
'     Return "Hello World!"
' End Function
 
' C#
' [DllExport(nameof(MyStringFunction), CallingConvention.StdCall)]
' [return: MarshalAs(UnmanagedType.BStr)]
' public static string MyStringFunction() {
'     return "Hello World!";
' }
 
#End Region
 
#Region " Imports "
 
Imports System.Runtime.InteropServices
 
#End Region
 
' ReSharper disable once CheckNamespace
 
#Region " DllExportAttribute "
 
Namespace DevCase.Runtime.Attributes
 
   ''' <summary>
   ''' The <see cref="DllExportAttribute"/> class indicates that a method can be
   ''' exported as a C function from a .NET dynamic-link library (DLL) file,
   ''' making the method callable from unmanaged code.
   ''' <para></para>
   ''' The <see cref="DllExportAttribute"/> class is solely intended to replicate and improve the
   ''' <b>DllExportAttribute</b> attribute class from plugins like:
   ''' <list type="bullet">
   '''   <item><b>DllExport</b> by 3F
   '''     <para></para>
   '''     <see href="https://github.com/3F/DllExport"/>
   '''   </item>
   '''  
   '''   <item><b>UnmanagedExports</b> by Huajitech
   '''     <para></para>
   '''     <see href="https://github.com/huajitech/UnmanagedExports"/>
   '''   </item>
   '''  
   '''   <item><b>UnmanagedExports.Repack.Upgrade</b> by StevenEngland
   '''     <para></para>
   '''     <see href="https://github.com/stevenengland/UnmanagedExports.Repack.Upgrade"/>
   '''   </item>
   ''' </list>
   ''' Allowing a programmer to use this attribute class without having the plugin installed in their Visual Studio projects.
   ''' <para></para>
   ''' Be aware that it is still necessary to use one of the mentioned projects to enable .NET functions export.
   ''' </summary>
   <AttributeUsage(AttributeTargets.Method, AllowMultiple:=False, Inherited:=False)>
   Public NotInheritable Class DllExportAttribute : Inherits Attribute
 
#Region " Properties "
 
       ''' <summary>
       ''' Gets or sets the calling convention required to call this C-exported function from unmanaged code.
       ''' <para></para>
       ''' Default value is <see cref="System.Runtime.InteropServices.CallingConvention.Cdecl"/>,
       ''' like for other C/C++ programs (Microsoft Specific).
       ''' <para></para>
       ''' Value <see cref="System.Runtime.InteropServices.CallingConvention.StdCall"/> is mostly used with Windows API.
       ''' <para></para>
       ''' </summary>
       Public Property CallingConvention As CallingConvention = CallingConvention.Cdecl
 
       ''' <summary>
       ''' Gets or sets the optional name for this C-exported function.
       ''' </summary>
       ''' <remarks>
       ''' See also:
       ''' <seealso href="https://learn.microsoft.com/en-us/cpp/build/reference/decorated-names?view=msvc-170#FormatC">
       ''' Format of a C decorated name.
       ''' </seealso>
       ''' </remarks>
       Public Property ExportName As String
 
#End Region
 
#Region " Constructors "
 
       ''' <summary>
       ''' Initializes a new instance of the <see cref="DllExportAttribute"/> class.
       ''' <para></para>
       ''' Use this constructor only if you plan to use <b>DllExport</b> by 3F (<see href="https://github.com/3F/DllExport"/>),
       ''' <para></para>
       ''' otherwise, use <see cref="DllExportAttribute.New(String, CallingConvention)"/>
       ''' to specify the export name and calling convention.
       ''' </summary>
       '''
       ''' <param name="convention">
       ''' The calling convention required to call this C-exported function.
       ''' <para></para>
       ''' Default value is <see cref="System.Runtime.InteropServices.CallingConvention.Cdecl"/>,
       ''' like for other C/C++ programs (Microsoft Specific).
       ''' <para></para>
       ''' Value <see cref="System.Runtime.InteropServices.CallingConvention.StdCall"/> is mostly used with Windows API.
       ''' <para></para>
       ''' </param>
       '''
       ''' <param name="exportName">
       ''' The optional name for this C-exported function.
       ''' See also:
       ''' <seealso href="https://learn.microsoft.com/en-us/cpp/build/reference/decorated-names?view=msvc-170#FormatC">
       ''' Format of a C decorated name.
       ''' </seealso>
       ''' </param>
       Public Sub New(convention As CallingConvention, exportName As String)
 
           Me.CallingConvention = convention
           Me.ExportName = exportName
       End Sub
 
       ''' <summary>
       ''' Initializes a new instance of the <see cref="DllExportAttribute"/> class.
       ''' <para></para>
       ''' Do not use this constructor if you plan to use <b>DllExport</b> by 3F (<see href="https://github.com/3F/DllExport"/>),
       ''' <para></para>
       ''' in that case use <see cref="DllExportAttribute.New(CallingConvention, String)"/>  
       ''' to specify the export name and calling convention.
       ''' </summary>
       '''
       ''' <param name="exportName">
       ''' The optional name for this C-exported function.
       ''' See also:
       ''' <seealso href="https://learn.microsoft.com/en-us/cpp/build/reference/decorated-names?view=msvc-170#FormatC">
       ''' Format of a C decorated name.
       ''' </seealso>
       ''' </param>
       '''
       ''' <param name="convention">
       ''' The calling convention required to call this C-exported function.
       ''' <para></para>
       ''' Default value is <see cref="System.Runtime.InteropServices.CallingConvention.Cdecl"/>,
       ''' like for other C/C++ programs (Microsoft Specific).
       ''' <para></para>
       ''' Value <see cref="System.Runtime.InteropServices.CallingConvention.StdCall"/> is mostly used with Windows API.
       ''' <para></para>
       ''' </param>
       Public Sub New(exportName As String, convention As CallingConvention)
 
           Me.ExportName = exportName
           Me.CallingConvention = convention
       End Sub
 
       ''' <summary>
       ''' Initializes a new instance of the <see cref="DllExportAttribute"/> class.
       ''' </summary>
       '''
       ''' <param name="exportName">
       ''' The optional name for this C-exported function.
       ''' See also:
       ''' <seealso href="https://learn.microsoft.com/en-us/cpp/build/reference/decorated-names?view=msvc-170#FormatC">
       ''' Format of a C decorated name.
       ''' </seealso>
       ''' </param>
       Public Sub New(exportName As String)
 
           Me.New(exportName, CallingConvention.Cdecl)
       End Sub
 
       ''' <summary>
       ''' Initializes a new instance of the <see cref="DllExportAttribute"/> class.
       ''' </summary>
       '''
       ''' <param name="convention">
       ''' The calling convention required to call this C-exported function.
       ''' <para></para>
       ''' Default value is <see cref="System.Runtime.InteropServices.CallingConvention.Cdecl"/>,
       ''' like for other C/C++ programs (Microsoft Specific).
       ''' <para></para>
       ''' Value <see cref="System.Runtime.InteropServices.CallingConvention.StdCall"/> is mostly used with Windows API.
       ''' <para></para>
       ''' </param>
       Public Sub New(convention As CallingConvention)
 
           Me.New(String.Empty, convention)
       End Sub
 
       ''' <summary>
       ''' Initializes a new instance of the <see cref="DllExportAttribute"/> class.
       ''' </summary>
       Public Sub New()
       End Sub
 
#End Region
 
   End Class
 
#End Region
 
End Namespace


Ejemplos de modo de exportación:

VB.NET:
Código
<DllExport(NameOf(MyStringFunction), CallingConvention.StdCall)>
Public Shared Function MyStringFunction() As <MarshalAs(UnmanagedType.BStr)> String
   Return "Hello World!"
End Function

C#:
Código
[DllExport(nameof(MyStringFunction), CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.BStr)]
public static string MyStringFunction() {
   return "Hello World!";
}

Ejemplos de modo de importación:

Pascal Script:
Código:

function MyStringFunction(): Cardinal;
  external 'MyStringFunction@files:MyNetAPI.dll stdcall';

Eleкtro:
Le he dado un lavado de cara moderno a esta impresentable clase del año 2013:
  • Una nueva versión de mi Listview, que tiene muchas cosas interesantes como poder dibujar una barra de progreso en una celda...

He aislado prácticamente toda la lógica de la "barra" de progreso para poder utilizarlo por separado en cualquier tipo de herencia de la clase ListView, en lugar de depender exclusivamente de ese control personalizado ListView que publiqué.

Les presento la clase ListViewProgressBarSubItem que implementa por sí misma (la parte esencial de) el dibujado de la celda y la "barra" de progreso del subitem, proporcionando propiedades de personalización que lo vuelven un elemento flexible y versátil:



💡 Con un poco de mano e ingenio se podría adaptar relativamente fácil dicha clase para dibujar estrellitas (un ranking o puntuación) u otros menesteres varios.

Para ello primero necesitaremos esta simple interfaz:

ISelfDrawableListViewSubItem
Código
''' <summary>
''' Provides a contract for a <see cref="ListViewItem.ListViewSubItem"/> that is capable of drawing itself.
''' </summary>
'''
''' <remarks>
''' For this interface to take effect, the owning <see cref="ListView"/> must have its
''' <see cref="ListView.OwnerDraw"/> property set to <see langword="True"/>, and the
''' <see cref="ListView.OnDrawSubItem"/> method must be properly overridden to delegate
''' the drawing logic by calling the <see cref="ISelfDrawableListViewSubItem.Draw(Graphics, Rectangle)"/> method.
''' <para></para>
''' See the attached code example for a practical implementation of this functionality.
''' </remarks>
'''
''' <example> This is a code example.
''' <code language="VB">
''' Public Class CustomListView : Inherits ListView
'''
'''     Public Sub New()
'''
'''         MyBase.New()
'''
'''         Me.DoubleBuffered = True
'''         Me.OwnerDraw = True
'''     End Sub
'''
'''     Protected Overrides Sub OnDrawColumnHeader(e As DrawListViewColumnHeaderEventArgs)
'''
'''         e.DrawDefault = True
'''         MyBase.OnDrawColumnHeader(e)
'''     End Sub
'''
'''     Protected Overrides Sub OnDrawItem(e As DrawListViewItemEventArgs)
'''
'''         e.DrawDefault = False
'''         MyBase.OnDrawItem(e)
'''     End Sub
'''
'''     Protected Overrides Sub OnDrawSubItem(e As DrawListViewSubItemEventArgs)
'''
'''         Dim selfDrawableSubItem As ISelfDrawableListViewSubItem = TryCast(e.SubItem, ISelfDrawableListViewSubItem)
'''         If selfDrawableSubItem IsNot Nothing Then
'''             selfDrawableSubItem.Draw(e.Graphics, e.Bounds)
'''         Else
'''             e.DrawDefault = True
'''         End If
'''
'''         MyBase.OnDrawSubItem(e)
'''     End Sub
'''
''' End Class
''' </code>
''' </example>
Public Interface ISelfDrawableListViewSubItem
 
   ''' <summary>
   ''' Draws the subitem within the specified bounds using the provided <see cref="Graphics"/> surface.
   ''' <para></para>
   ''' This method must be called from the <see cref="ListView.OnDrawSubItem"/> method of the owning <see cref="ListView"/>.
   ''' </summary>
   '''
   ''' <param name="g">
   ''' The <see cref="Graphics"/> surface on which to render the subitem.
   ''' </param>
   '''
   ''' <param name="bounds">
   ''' The <see cref="Rectangle"/> that defines the drawing area for the subitem.
   ''' </param>
   Sub Draw(g As Graphics, bounds As Rectangle)
 
End Interface

Y por último, la clase:

ListViewProgressBarSubItem
Código
''' <summary>
''' Represents a custom <see cref="ListViewItem.ListViewSubItem"/> that visually
''' simulates a progress bar with personalizable text and appearance.
''' </summary>
'''
''' <remarks>
''' For this class to take effect, the owning <see cref="ListView"/> must have its
''' <see cref="ListView.OwnerDraw"/> property set to <see langword="True"/>, and the
''' <see cref="ListView.OnDrawSubItem"/> method must be properly overridden to delegate
''' the drawing logic by calling the <see cref="ListViewProgressBarSubItem.Draw(Graphics, Rectangle)"/> method.
''' <para></para>
''' See the attached code example for a practical implementation of this functionality.
''' </remarks>
'''
''' <example> This is a code example.
''' <code language="VB">
''' Public Class Form1
'''
'''     Private WithEvents CustomListView1 As New CustomListView()
'''
'''     Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'''
'''         Dim lv As ListView = Me.CustomListView1
'''         Dim item As New ListViewItem("My item")
'''         Dim subItem As New ListViewProgressBarSubItem(item) With {
'''             .DecimalPlaces = 2,
'''             .TextSuffix = Nothing,
'''             .BorderColor = Color.Empty,
'''             .BackColor = Color.Empty,
'''             .ForeColor = Color.Red,
'''             .FillGradientColorLeft = SystemColors.Highlight,
'''             .FillGradientColorRight = SystemColors.Highlight,
'''             .FillGradientAngle = 0
'''         }
'''
'''         item.SubItems.Add(subItem)
'''         lv.Items.Add(item)
'''     End Sub
'''
''' End Class
'''
''' Public Class CustomListView : Inherits ListView
'''
'''     Public Sub New()
'''
'''         MyBase.New()
'''
'''         Me.DoubleBuffered = True
'''         Me.OwnerDraw = True
'''     End Sub
'''
'''     Protected Overrides Sub OnDrawColumnHeader(e As DrawListViewColumnHeaderEventArgs)
'''
'''         e.DrawDefault = True
'''         MyBase.OnDrawColumnHeader(e)
'''     End Sub
'''
'''     Protected Overrides Sub OnDrawItem(e As DrawListViewItemEventArgs)
'''
'''         e.DrawDefault = False
'''         MyBase.OnDrawItem(e)
'''     End Sub
'''
'''     Protected Overrides Sub OnDrawSubItem(e As DrawListViewSubItemEventArgs)
'''
'''         Dim selfDrawableSubItem As ISelfDrawableListViewSubItem = TryCast(e.SubItem, ISelfDrawableListViewSubItem)
'''         If selfDrawableSubItem IsNot Nothing Then
'''             selfDrawableSubItem.Draw(e.Graphics, e.Bounds)
'''         Else
'''             e.DrawDefault = True
'''         End If
'''
'''         MyBase.OnDrawSubItem(e)
'''     End Sub
'''
''' End Class
''' </code>
''' </example>
<Serializable>
<TypeConverter(GetType(ExpandableObjectConverter))>
<ToolboxItem(False)>
<DesignTimeVisible(False)>
<DefaultProperty("Text")>
Public Class ListViewProgressBarSubItem : Inherits ListViewItem.ListViewSubItem : Implements ISelfDrawableListViewSubItem
 
#Region " Fields "
 
   ''' <summary>
   ''' The default font of the text displayed by the subitem.
   ''' </summary>
   <NonSerialized>
   Protected defaultFont As Font = MyBase.Font
 
   ''' <summary>
   ''' The default background color of the subitem's text.
   ''' </summary>
   <NonSerialized>
   Protected defaultBackColor As Color = MyBase.BackColor
 
   ''' <summary>
   ''' The default foreground color of the subitem's text.
   ''' </summary>
   <NonSerialized>
   Protected defaultForeColor As Color = MyBase.ForeColor
 
   ''' <summary>
   ''' The default angle to draw the linear gradient specified by  
   ''' <see cref="ListViewProgressBarSubItem.FillGradientColorLeft"/>
   ''' and <see cref="ListViewProgressBarSubItem.FillGradientColorRight"/> colors.
   ''' </summary>
   <NonSerialized>
   Protected defaultFillGradientAngle As Single = 0
 
   ''' <summary>
   ''' The default starting linear gradient color to fill the progress area.
   ''' </summary>
   <NonSerialized>
   Protected defaultFillGradientColorLeft As Color = SystemColors.HighlightText
 
   ''' <summary>
   ''' The default ending linear gradient color to fill the progress area.
   ''' </summary>
   <NonSerialized>
   Protected defaultFillGradientColorRight As Color = SystemColors.Highlight
 
   ''' <summary>
   ''' The default color of the progress bar border.
   ''' </summary>
   <NonSerialized>
   Protected defaultBorderColor As Color = SystemColors.InactiveBorder
 
   ''' <summary>
   ''' The default <see cref="System.Windows.Forms.TextFormatFlags"/> that determine
   ''' how the subitem text is rendered and aligned.
   ''' </summary>
   <NonSerialized>
   Protected defaultTextFormatFlags As TextFormatFlags =
       TextFormatFlags.HorizontalCenter Or
       TextFormatFlags.VerticalCenter Or
       TextFormatFlags.EndEllipsis Or
       TextFormatFlags.SingleLine
 
#End Region
 
#Region " Properties "
 
   ''' <summary>
   ''' Gets or sets the current progress percentage value to display in the progress bar.
   ''' <para></para>
   ''' The value should be between 0 to 100.
   ''' </summary>
   Public Property ProgressPercentage As Double
       Get
           Return Me.progressPercentage_
       End Get
       <DebuggerStepThrough>
       Set(value As Double)
           Me.SetFieldValue(Me.progressPercentage_, value)
       End Set
   End Property
   ''' <summary>
   ''' ( Backing Field of <see cref="ListViewProgressBarSubItem.ProgressPercentage"/> property )
   ''' <para></para>
   ''' The current progress percentage value to display in the progress bar.
   ''' </summary>
   Private progressPercentage_ As Double
 
   ''' <summary>
   ''' Gets or sets the number of decimal places displayed for the <see cref="ListViewProgressBarSubItem.ProgressPercentage"/> value.
   ''' <para></para>
   ''' Default value is zero.
   ''' </summary>
   Public Property DecimalPlaces As Short
       Get
           Return Me.decimalPlaces_
       End Get
       <DebuggerStepThrough>
       Set(value As Short)
           Me.SetFieldValue(Me.decimalPlaces_, value)
       End Set
   End Property
   ''' <summary>
   ''' ( Backing Field of <see cref="ListViewProgressBarSubItem.DecimalPlaces"/> property )
   ''' <para></para>
   ''' The number of decimal places displayed for the <see cref="ListViewProgressBarSubItem.ProgressPercentage"/> value.
   ''' </summary>
   Private decimalPlaces_ As Short
 
   ''' <summary>
   ''' Gets or sets the additional text displayed next to the <see cref="ListViewProgressBarSubItem.ProgressPercentage"/> value.
   ''' </summary>
   Public Property TextSuffix As String
       Get
           Return Me.textSuffix_
       End Get
       <DebuggerStepThrough>
       Set(value As String)
           Me.SetFieldValue(Me.textSuffix_, value)
       End Set
   End Property
   ''' <summary>
   ''' ( Backing Field of <see cref="ListViewProgressBarSubItem.TextSuffix"/> property )
   ''' <para></para>
   ''' The additional text displayed next to the <see cref="ListViewProgressBarSubItem.ProgressPercentage"/> value.
   ''' </summary>
   Private textSuffix_ As String
 
   ''' <summary>
   ''' Gets or sets the <see cref="System.Windows.Forms.TextFormatFlags"/> that determine
   ''' how the subitem text is rendered and aligned.
   ''' <para></para>
   ''' Default value is:
   ''' <see cref="System.Windows.Forms.TextFormatFlags.HorizontalCenter"/>,
   ''' <see cref="System.Windows.Forms.TextFormatFlags.VerticalCenter"/>,
   ''' <see cref="System.Windows.Forms.TextFormatFlags.EndEllipsis"/> and
   ''' <see cref="System.Windows.Forms.TextFormatFlags.SingleLine"/>
   ''' </summary>
   Public Property TextFormatFlags As TextFormatFlags
       Get
           Return Me.textFormatFlags_
       End Get
       <DebuggerStepThrough>
       Set(value As TextFormatFlags)
           Me.SetFieldValue(Me.textFormatFlags_, value)
       End Set
   End Property
   ''' <summary>
   ''' ( Backing Field of <see cref="ListViewProgressBarSubItem.TextFormatFlags"/> property )
   ''' <para></para>
   ''' The <see cref="System.Windows.Forms.TextFormatFlags"/> that determine how the subitem text is rendered.
   ''' </summary>
   Private textFormatFlags_ As TextFormatFlags
 
   ''' <summary>
   ''' Gets or sets the starting linear gradient color to fill the progress area.
   ''' <para></para>
   ''' Default value is <see cref="SystemColors.Control"/>.
   ''' </summary>
   Public Property FillGradientColorLeft As Color
       Get
           Return Me.fillGradientColorLeft_
       End Get
       <DebuggerStepThrough>
       Set(value As Color)
           Me.SetFieldValue(Me.fillGradientColorLeft_, value)
       End Set
   End Property
   ''' <summary>
   ''' ( Backing Field of <see cref="ListViewProgressBarSubItem.FillGradientColorLeft"/> property )
   ''' <para></para>
   ''' The starting linear gradient color to fill the progress area.
   ''' </summary>
   Private fillGradientColorLeft_ As Color
 
   ''' <summary>
   ''' Gets or sets the ending linear gradient color to fill the progress area.
   ''' <para></para>
   ''' Default value is <see cref="Color.LightGreen"/>.
   ''' </summary>
   Public Property FillGradientColorRight As Color
       Get
           Return Me.fillGradientColorRight_
       End Get
       <DebuggerStepThrough>
       Set(value As Color)
           Me.SetFieldValue(Me.fillGradientColorRight_, value)
       End Set
   End Property
   ''' <summary>
   ''' ( Backing Field of <see cref="ListViewProgressBarSubItem.FillGradientColorRight"/> property )
   ''' <para></para>
   ''' The ending linear gradient color to fill the progress area.
   ''' </summary>
   Private fillGradientColorRight_ As Color
 
   ''' <summary>
   ''' Gets or sets the angle to draw the linear gradient specified by  
   ''' <see cref="ListViewProgressBarSubItem.FillGradientColorLeft"/>
   ''' and <see cref="ListViewProgressBarSubItem.FillGradientColorRight"/> colors.
   ''' <para></para>
   ''' Default value is zero.
   ''' </summary>
   Public Property FillGradientAngle As Single
       Get
           Return Me.fillGradientAngle_
       End Get
       <DebuggerStepThrough>
       Set(value As Single)
           Me.SetFieldValue(Me.fillGradientAngle_, value)
       End Set
   End Property
   ''' <summary>
   ''' ( Backing Field of <see cref="ListViewProgressBarSubItem.FillGradientAngle"/> property )
   ''' <para></para>
   ''' The angle to draw the linear gradient specified by  
   ''' <see cref="ListViewProgressBarSubItem.FillGradientColorLeft"/>
   ''' and <see cref="ListViewProgressBarSubItem.FillGradientColorRight"/> colors.
   ''' </summary>
   Private fillGradientAngle_ As Single
 
   ''' <summary>
   ''' Gets or sets the color of the progress bar border.
   ''' <para></para>
   ''' Default value is <see cref="SystemColors.ActiveBorder"/>.
   ''' </summary>
   Public Property BorderColor As Color
       Get
           Return Me.borderColor_
       End Get
       <DebuggerStepThrough>
       Set(value As Color)
           Me.SetFieldValue(Me.borderColor_, value)
       End Set
   End Property
   ''' <summary>
   ''' ( Backing Field of <see cref="ListViewProgressBarSubItem.BorderColor"/> property )
   ''' <para></para>
   ''' The color of the progress bar border.
   ''' </summary>
   Private borderColor_ As Color
 
   ''' <summary>
   ''' Gets or sets the background color of the subitem cell.
   ''' </summary>
   Public Shadows Property BackColor As Color
       Get
           Return MyBase.BackColor
       End Get
       <DebuggerStepThrough>
       Set(value As Color)
           Me.SetFieldValue(MyBase.BackColor, value)
       End Set
   End Property
 
   ''' <summary>
   ''' Gets or sets the foreground color of the subitem's text.
   ''' </summary>
   Public Shadows Property ForeColor As Color
       Get
           Return MyBase.ForeColor
       End Get
       <DebuggerStepThrough>
       Set(value As Color)
           Me.SetFieldValue(MyBase.ForeColor, value)
       End Set
   End Property
 
   ''' <summary>
   ''' Gets or sets the font of the text displayed by the subitem.
   ''' </summary>
   Public Shadows Property Font As Font
       Get
           Return MyBase.Font
       End Get
       Set(value As Font)
           Me.SetFieldValue(MyBase.Font, value)
       End Set
   End Property
 
   ''' <summary>
   ''' Gets the text of the subitem.
   ''' </summary>
   Public Shadows ReadOnly Property Text As String
       Get
           Return Me.progressPercentage_.ToString("N" & Me.decimalPlaces_) & "%" &
                  If(String.IsNullOrEmpty(Me.textSuffix_), Nothing, " " & Me.textSuffix_)
       End Get
   End Property
 
#End Region
 
#Region " Constructors "
 
   ''' <summary>
   ''' Initializes a new instance of the <see cref="ListViewProgressBarSubItem"/> class
   ''' associated with the given <see cref="ListViewItem"/>.
   ''' </summary>
   '''
   ''' <param name="owner">
   ''' A <see cref="ListViewItem"/> that represents the item that owns this <see cref="ListViewProgressBarSubItem"/>.
   ''' </param>
   <DebuggerStepThrough>
   Public Sub New(owner As ListViewItem)
 
       MyBase.New(owner, String.Empty)
       Me.ResetStyle()
   End Sub
 
   ''' <summary>
   ''' Initializes a new instance of the <see cref="ListViewProgressBarSubItem"/> class.
   ''' </summary>
   <DebuggerStepThrough>
   Public Sub New()
 
       Me.ResetStyle()
   End Sub
 
#End Region
 
#Region " Public Methods "
 
   ''' <summary>
   ''' Resets the styles applied to the subitem to the default font and colors.
   ''' </summary>
   <DebuggerStepThrough>
   Public Shadows Sub ResetStyle()
 
       MyBase.Font = Me.defaultFont
       MyBase.BackColor = Me.defaultBackColor
       MyBase.ForeColor = Me.defaultForeColor
 
       Me.textFormatFlags_ = Me.defaulttextFormatFlags
       Me.fillGradientColorLeft_ = Me.defaultFillGradientColorLeft
       Me.fillGradientColorRight_ = Me.defaultFillGradientColorRight
       Me.fillGradientAngle_ = Me.defaultFillGradientAngle
       Me.borderColor_ = Me.defaultBorderColor
 
       Me.RefreshSubItem()
   End Sub
 
#End Region
 
#Region " Private Methods "
 
   ''' <summary>
   ''' Sets the value of the specified field using <see cref="EqualityComparer(Of T)"/> to check value equality.
   ''' <para></para>
   ''' If the new value is different from the current one,
   ''' it then calls <see cref="ListViewProgressBarSubItem.RefreshSubItem"/> method.
   ''' </summary>
   '''
   ''' <typeparam name="T">
   ''' The type of the property value.
   ''' </typeparam>
   '''
   ''' <param name="refField">
   ''' A reference to the backing field of the property.
   ''' </param>
   '''
   ''' <param name="newValue">
   ''' The new value to set.
   ''' </param>
   <DebuggerStepThrough>
   Private Sub SetFieldValue(Of T)(ByRef refField As T, newValue As T)
 
       If Not EqualityComparer(Of T).Default.Equals(refField, newValue) Then
           refField = newValue
           Me.RefreshSubItem()
       End If
   End Sub
 
   ''' <summary>
   ''' Refreshes the visual representation of this <see cref="ListViewProgressBarSubItem"/> subitem
   ''' in the owner <see cref="ListView"/> by invalidating its bounding rectangle.
   ''' </summary>
   '''
   ''' <remarks>
   ''' This method uses reflection to access the non-public "owner" field of the base
   ''' <see cref="ListViewItem.ListViewSubItem"/> class in order to identify the parent item and column index.
   ''' It then triggers a redraw of the specific subitem area within the parent ListView.
   ''' </remarks>
   <DebuggerStepThrough>
   Private Sub RefreshSubItem()
 
       Dim fieldInfo As FieldInfo =
           GetType(ListViewItem.ListViewSubItem).GetField("owner", BindingFlags.NonPublic Or BindingFlags.Instance)
 
       If fieldInfo IsNot Nothing Then
           Dim ownerObj As Object = fieldInfo.GetValue(Me)
 
           If ownerObj IsNot Nothing Then
               Dim lvi As ListViewItem = DirectCast(ownerObj, ListViewItem)
               Dim colIndex As Integer = lvi.SubItems.IndexOf(Me)
 
               If colIndex >= 0 AndAlso lvi.ListView IsNot Nothing Then
                   Dim subItemBounds As Rectangle = lvi.SubItems(colIndex).Bounds
                   lvi.ListView.Invalidate(subItemBounds, invalidateChildren:=False)
               End If
           End If
       End If
   End Sub
 
#End Region
 
#Region " ISelfDrawableListViewSubItem Implementation "
 
   ''' <summary>
   ''' Draws the subitem within the specified bounds using the provided <see cref="Graphics"/> surface.
   ''' <para></para>
   ''' This method must be called from the <see cref="ListView.OnDrawSubItem"/> method of the owning <see cref="ListView"/>.
   ''' </summary>
   '''
   ''' <param name="g">
   ''' The <see cref="Graphics"/> surface on which to render the subitem.
   ''' </param>
   '''
   ''' <param name="bounds">
   ''' The <see cref="Rectangle"/> that defines the drawing area for the subitem.
   ''' </param>
   <DebuggerStepThrough>
   Protected Sub Draw(g As Graphics, bounds As Rectangle) Implements ISelfDrawableListViewSubItem.Draw
 
       ' Draw subitem background (progress bar background).
       If Me.BackColor <> Color.Empty AndAlso Me.BackColor <> Color.Transparent Then
 
           Using backBrush As New SolidBrush(Me.BackColor)
               g.FillRectangle(backBrush, bounds)
           End Using
       End If
 
       ' Draw linear gradient to fill the current progress of the progress bar.
       If (Me.fillGradientColorLeft_ <> Color.Empty AndAlso Me.fillGradientColorLeft_ <> Color.Transparent) OrElse
          (Me.fillGradientColorRight_ <> Color.Empty AndAlso Me.fillGradientColorRight_ <> Color.Transparent) Then
 
           Using brGradient As New Drawing2D.LinearGradientBrush(
               New Rectangle(bounds.X, bounds.Y, bounds.Width, bounds.Height),
               Me.fillGradientColorLeft_, Me.fillGradientColorRight_,
               Me.fillGradientAngle_, isAngleScaleable:=True)
 
               Dim progressWidth As Integer = CInt((Me.progressPercentage_ / 100) * (bounds.Width - 2))
               g.FillRectangle(brGradient, bounds.X + 1, bounds.Y + 2, progressWidth, bounds.Height - 3)
           End Using
       End If
 
       ' Draw subitem text.
       Dim text As String = Me.Text
       If Not String.IsNullOrEmpty(text) Then
 
           TextRenderer.DrawText(g, text, Me.Font, bounds, Me.ForeColor, Me.TextFormatFlags)
       End If
 
       ' Draw border around the progress bar.
       If Me.borderColor_ <> Color.Empty AndAlso Me.borderColor_ <> Color.Transparent Then
           Using borderPen As New Pen(Me.borderColor_)
               g.DrawRectangle(borderPen, bounds.X, bounds.Y + 1, bounds.Width - 2, bounds.Height - 2)
           End Using
       End If
 
   End Sub
 
#End Region
 
End Class

Ejemplo de uso:
Código
Public Class Form1
 
    Private WithEvents CustomListView1 As New CustomListView()
 
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
 
        Dim lv As ListView = Me.CustomListView1
        Dim item As New ListViewItem("My item")
        Dim subItem As New ListViewProgressBarSubItem(item) With {
            .DecimalPlaces = 2,
            .TextSuffix = Nothing,
            .BorderColor = Color.Empty,
            .BackColor = Color.Empty,
            .ForeColor = Color.Red,
            .FillGradientColorLeft = SystemColors.Highlight,
            .FillGradientColorRight = SystemColors.Highlight,
            .FillGradientAngle = 0
        }
 
        item.SubItems.Add(subItem)
        lv.Items.Add(item)
    End Sub
 
End Class
 
Public Class CustomListView : Inherits ListView
 
    Public Sub New()
 
        MyBase.New()
 
        Me.DoubleBuffered = True
        Me.OwnerDraw = True
    End Sub
 
    Protected Overrides Sub OnDrawColumnHeader(e As DrawListViewColumnHeaderEventArgs)
 
        e.DrawDefault = True
        MyBase.OnDrawColumnHeader(e)
    End Sub
 
    Protected Overrides Sub OnDrawItem(e As DrawListViewItemEventArgs)
 
        e.DrawDefault = False
        MyBase.OnDrawItem(e)
    End Sub
 
    Protected Overrides Sub OnDrawSubItem(e As DrawListViewSubItemEventArgs)
 
        Dim selfDrawableSubItem As ISelfDrawableListViewSubItem = TryCast(e.SubItem, ISelfDrawableListViewSubItem)
        If selfDrawableSubItem IsNot Nothing Then
            selfDrawableSubItem.Draw(e.Graphics, e.Bounds)
        Else
            e.DrawDefault = True
        End If
 
        MyBase.OnDrawSubItem(e)
    End Sub
 
End Class

Navegación

[0] Índice de Mensajes

[#] Página Siguiente

[*] Página Anterior