Etiqueta: .NET

Temas relacionados con .NET Framework (todas las versiones)

  • Presuntamente algunas funciones de Microsoft.VisualBasic no son compatibles con las aplicaciones para Windows Store

     

    Pues eso… que en un proyecto DLL (biblioteca de clases) para Windows Store no me ha reconocido ciertas funciones de manipulación de cadenas que están definidas en el módulo Strings del espacio de nombres Microsoft.VisualBasic (que es como si dijéramos el "runtime" de Visual Basic para .NET).

    Lo de presuntamente es por si he hecho algo mal y por eso no me reconocía algunas funciones… al menos en un proyecto del tipo biblioteca de clases (class library), en la figura 1 tienes las cosas que se pueden usar de ese módulo con las funciones de cadena.

     

    Microsoft.VisualBasic.Strings en app store class library
    Figura 1. Las funciones que muestra Strings en un proyecto del tipo Class library para Windows store

     

    Nota:
    En una aplicación para Windows Store (no una librería de clases) también muestra los mismos métodos. En una aplicación/librería para el escritorio (incluso de Windows 8) muestra las que estamos habituados a usar.

     

    Así que… me las he tenido que fabricar, al menos las que me han hecho falta para este caso concreto, que no es otro que una actualización de mi librería (perdón, biblioteca) para colorear código: gsColorearCodigo.

    Aquí te dejo un módulo de Visual Basic para hacer las conversiones… lo mismo ya tenía algo publicado sobre algo parecido, pero…

     

    '------------------------------------------------------------------------------
    ' VBStrings                                                         (01/Ene/13)
    '
    ' Para simular las funciones de cadena de Visual Basic
    ' ya que parece que el runtime para Windows Store no incluye algunas funciones
    '
    ' ©Guillermo 'guille' Som, 2013
    '------------------------------------------------------------------------------
    Option Strict On
    Option Infer On
    
    
    Public Module VBStrings
        ''' <summary>
        ''' Devuelve la posición (en base 1) de cadena1 en cadena2
        ''' </summary>
        ''' <remarks></remarks>
        Public Function InStr(cadena1 As String, cadena2 As String) As Integer
            Try
                Dim i = cadena1.IndexOf(cadena2)
                Return i + 1
            Catch ex As Exception
                Return 0
            End Try
        End Function
    
    
        ''' <summary>
        ''' Devuelve la posición en base 1 de cadena1 en cadena2
        ''' se comprueba a partir de la posición desde
        ''' </summary>
        ''' <remarks></remarks>
        Public Function InStr(desde As Integer, cadena1 As String, cadena2 As String) As Integer
            Try
                Dim i = cadena1.IndexOf(cadena2, desde - 1)
                Return i + 1
            Catch ex As Exception
                Return 0
            End Try
        End Function
    
        ''' <summary>
        ''' Devuelve una cadena con los cuantos primeros caracteres de cadena1
        ''' El primer caracter es el 1 y se devuelve en base 1
        ''' </summary>
        ''' <remarks></remarks>
        Public Function Left(cadena1 As String, cuantos As Integer) As String
            If String.IsNullOrWhiteSpace(cadena1) OrElse cuantos < 1 Then
                Return ""
            End If
    
            Dim s = cadena1.Substring(0, cuantos - 1)
            Return s
        End Function
    
        ''' <summary>
        ''' Devuelve la cantidad de caracteres que tiene cadena1
        ''' </summary>
        ''' <remarks></remarks>
        Public Function Len(cadena1 As String) As Integer
            If String.IsNullOrWhiteSpace(cadena1) Then
                Return 0
            End If
    
            Return cadena1.Length
        End Function
    
        ''' <summary>
        ''' Devuelve una cadena nueva después de quitarle
        ''' los espacios del principio y del final
        ''' </summary>
        ''' <remarks></remarks>
        Public Function Trim(cadena1 As String) As String
            If String.IsNullOrWhiteSpace(cadena1) Then
                Return ""
            End If
    
            Return cadena1.Trim()
        End Function
    
        ''' <summary>
        ''' Devuelve una cadena a partir de la posición desde
        ''' La pirmera posición es la 1
        ''' </summary>
        ''' <remarks></remarks>
        Public Function Mid(cadena1 As String, desde As Integer) As String
            If String.IsNullOrWhiteSpace(cadena1) OrElse desde < 1 Then
                Return ""
            End If
    
            Return cadena1.Substring(desde - 1)
        End Function
    
        ''' <summary>
        ''' Devuelve una cadena con cuantos caracteres de cadena1 
        '''  a partir de desde
        ''' La primera posición es la 1
        ''' </summary>
        ''' <remarks></remarks>
        Public Function Mid(cadena1 As String, desde As Integer, 
                                                cuantos As Integer) As String
            If String.IsNullOrWhiteSpace(cadena1) OrElse desde < 1 Then
                Return ""
            End If
    
            Return cadena1.Substring(desde - 1, cuantos)
        End Function
    
        ''' <summary>
        ''' Trocea una cadena usando los caracteres indicados
        ''' </summary>
        ''' <remarks></remarks>
        Public Function Split(cadena1 As String, caracteres As String) As String()
            Return cadena1.Split(caracteres.ToCharArray, 
                                  StringSplitOptions.RemoveEmptyEntries)
        End Function
    End Module
    
    
    

     

    Espero que te sea de utilidad.

     

    Nos vemos.

    Guillermo

  • Como usar GetFileFromApplicationUriAsync (para que no se te cuelgue la aplicación)

     

    Pues eso… que este post iba a ser una especie de declaración de «mi» incapacidad a la hora de trabajar de forma asíncrona con los ficheros de una aplicación de Windows Store, y al final será una especie de truco o consejo de cómo hacer las cosas con todo esto de el acceso a ficheros de forma asíncrona (la única que conozco para Windows Store).

    Y la solución no es porque «yo» haya dado con la respuesta, bueno, un poco sí, ya que si no hago un par de búsquedas en Google lo mismo no hubiese dado con la respuesta.

    Y digo «un par» de búsquedas por no decir tres, ya que es complicado algunas veces encontrar respuestas, sobre todo porque la mayoría de las preguntas con respuestas están en inglés y después porque no sabes con certeza cómo «plantear» la búsqueda…

    Y lo curioso es que al tercer intento con esto: «GetFileFromApplicationUriAsync don’t» es cuando ha salido la respuesta, y precisamente en el primer lugar:

     

    getfilefromapplicationuriasync don t  Buscar con Google

     

    Y eso que en la búsqueda anterior lo puse un poco más concreto, en fin… estos buscadores y/o los usuarios de los mismos… ¡habrá que apañarlos! 😉

     

    Te explico de qué va la cosa:

    Estoy haciendo una aplicación para Windows 8 (Windows Store) en la que quiero mostrar el contenido de unos ficheros de textos. Para ello estoy usando una versión adaptada del tipo de proyecto Split App en el que todo el contenido está basado en «bindings», pero ese no es el problema, el problema es que en la clase SampleDataSource estoy haciendo una serie de cambios para que se adapte a lo que yo quiero, y como el texto a mostrar es bastante grande como para «pegarlo» en la propia clase (tal como hacen en el proyecto de ejemplo) me puse a crear un código que leyera el contenido del fichero y lo asignara adecuadamente a una propiedad de la clase usada como «binding» que es la que muestra el texto final.

    Así que… se me ocurrió usar este código (y algún otro con distintas pruebas):

     

    Dim ficUri = New Uri("ms-appx:///contenido/" & fic)
    
    ' aquí se queda colgado (algunas veces)
    ' las veces que pasa de aquí es en modo debug y haciendo un break
    ' (pero no siempre)
    Dim file = Await StorageFile.GetFileFromApplicationUriAsync(ficUri)
    
    ' Aquí también se para, y pasa si hay un breakpoint
    Dim sf = Await file.OpenStreamForReadAsync
    
    Using sr As New StreamReader(sf, Encoding.UTF8, True)
        sBody = sr.ReadToEnd()
    End Using
    
    Return sBody
    
    
    

     

    Y tal como comento en los comentarios (valga la redundancia) cuando estaba en funcionamiento «natural» no pasaba de ahí y se quedaba colgada la aplicación.

    Ya estaba por desistir cuando me ha dado el punto de buscar en Google ya que suponía que no sería cosa mía y que seguramente a alguien más le habrá ocurrido… ¡y así es!

    Nota: antes de buscar en Google ya busqué información en la documentación de Visual Studio, pero no encontré nada, aparte de los ejemplos triviales que suelen poner que que dan por hecho de que prácticamente te lo sabes todo, todo, todo… en fin…

    No me voy a enrollar más de la cuenta y te pongo el código correcto para hacer esa tarea de leer un fichero de forma asíncrona y que no se quede colgada la aplicación de Windows Store.

     

    También te pondré el enlace a esa pregunta y un artículo del autor de la respuesta donde explica porqué pasa eso, no, mejor dicho: «porqué nos pasa eso a los principiantes» del acceso asíncrono, o eso he entendido yo de este párrafo:

    «I think it’s the most-asked question by async newcomers once they’ve learned the basics.»

    Y tiene razón ya que reconozco que soy un newcomer en esto de el acceso asíncrono, al menos para los ficheros en el directorio de la aplicación, ya que antes he estado accediendo de forma asíncrona a otros ficheros (del Local storage) y ha funcionado… en fin…

     

    Dim ficUri = New Uri("ms-appx:///contenido/" & fic)
    
    Dim file = Await StorageFile.GetFileFromApplicationUriAsync(ficUri).AsTask().ConfigureAwait(False)
    
    Dim sf = Await file.OpenStreamForReadAsync().ConfigureAwait(False)
    
    Using sr As New StreamReader(sf, Encoding.UTF8, True)
        sBody = sr.ReadToEnd()
    End Using
    
    
    Return sBody
    

     

    Como puedes ver, la solución es agregar .AsTask().ConfigureAwait(False) al final del método GetFileFromApplicationUriAsync que es el que se encarga de obtener el fichero desde el directorio de la aplicación, y de ConfigureAwait(False) al método OpenStreamForReadAsync que es el que se encarga de convertir dicho fichero en un Stream de lectura.

     

    La explicación del porqué del uso de esos métodos está en el comentario que hizo Nito al preguntarle el «buscador de respuestas» porqué era necesario hacer eso:

    (te lo pego en inglés que la traducción automática como que no me convence)

    Explanation is quite simple: when you use task.Result ortask.Wait() on GUI thread in conjunction with await keyword you’re causing deadlock. This happens because after awaiting code is resumed on the same context it was invoked (in your case – GUI thread). And because GUI thread currently waiting for task to complete (via Result or Wait()) deadlock arises and code after await keyword will never be invoked. ConfigureAwait(false)specifies that current context can be ignored and thus allows your code to complete successfully. More details on this here: http://nitoprograms.blogspot.com/2012/07/dont-block-on-async-code.html

     

    Este es el enlace a la pregunta/respuesta esa que te comentaba y este otro es al artículo de Nito Programming.

     

    Espero que te sea de utilidad.

     

    Nos vemos.

    Guillermo

  • Ejemplo de ListView y equivalencia a subitems

     

    Pues eso… si has intentado usar el control ListView en una aplicación de tipo WPF (Windows Presentation Foundation), seguramente te habrás dado cuenta que no tiene nada que ver con el control de las aplicaciones de Windows Forms (WinForms), sobre todo a la hora de agregar y recuperar los elementos, de hecho la propiedad SubItems no existe.
    Sí, es más complicado.

     

    WPF = .NET + XAML (Windows Presentation Foundation) WPF = .NET + XAML

     

    Hace unos años (más de 5) ya te comentaba algunos de esos cambios en los puntos 9, 10 y 11 de Equivalencias entre las clases de WPF y las de .NET (1).

    Aquí te voy a explicar lo que yo creo que se debe hacer para acceder a los elementos de un ListView, que en realidad no ha cambiado mucho, pero lo que sí ha cambiado es la forma de acceder a los "sub elementos", es decir ya no existe una propiedad / colección subitems que es la forma que tenemos en WinForms para acceder a las diferentes columnas de cada elemento.

    Cómo se hace en WinForms

    Por ejemplo, si tenemos un ListView con más de una columna, la forma de acceder a la segunda columna de la primera fila es: LisView1.Items(0).SubItems(1).Text

    Pero eso no lo podemos hacer en WPF ya que SubItems no existe.

    Cómo se hace en WPF

    Los controles ListView de WPF para definir columnas deben tener un control del tipo GridView como contenido de la propiedad ListView.View.

    Ese GridView define tantas columnas como sean necesarias, esas columnas son del tipo GridViewColumn y en la definición de las propiedades de ese objeto-columna, concretamente en DisplayMemberBinding es donde está "la magia" de cómo acceder a cada uno de los "sub-elementos" de cada fila de datos.

    Por ejemplo, si tenemos un formulario tal como el mostrado en la figura 1, ese ListView lo podemos definir tal como vemos en el listado 1.

    El formulario WPF
    Figura 1. El formulario de WPF

     

    Este es el código XAML para crear ese ListView (más abajo te mostraré el código completo):

    <ListView Name="lvDatos" Margin="0" MinHeight="240"
          VerticalAlignment="Stretch" HorizontalAlignment="Stretch" >
    <ListView.View>
        <GridView AllowsColumnReorder="True">
            <!-- Utilizo valores en Binding distintos a los nombres de las cabeceras
                 para que se vea que no hay relación entre el nombre del Header
                 y el nombre de Binding -->
                <GridViewColumn Header="Nombre y apellidos" 
                                DisplayMemberBinding="{Binding Nombre}" Width="150"/>
                <GridViewColumn Header="Correo" 
                                DisplayMemberBinding="{Binding email}" Width="190"/>
                <GridViewColumn Header="How old" 
                                DisplayMemberBinding="{Binding Edad}" Width="70"/>
        </GridView>
    </ListView.View>
    </ListView>
    

    Listado 1. El código XAML del control ListView

    Como puedes comprobar en cada columna hay tres asignaciones a otras tantas propiedades:

    Width que es para indicar el ancho, Header que es para indicr el texto a mostrar en la cabecera de la columna y DisplayMemberBinding que es la propiedad que enlaza con el dato a mostrarse en esa columna.

    Como puedes comprobar el valor que le estoy asignando a esa propiedad "enlazada" es: {Binding Nombre}.

    ¿Qué significa eso?

    Eso significa que lo que se muestre en esa columna será un "campo" (o propiedad) del dato que se agregue a una fila.

    Y tu dirás… ¡pos vale! pero no me entero…

    Eso quiere decir que cada dato que añadas a cada fila del ListView debe ser un objeto que defina una propiedad con el mismo nombre que indicamos después de Binding. En ese ejemplo, será una propiedad llamada Nombre.

    Es decir, en WPF el ListView no funciona "tan fácil" como lo hace en WinForms, en WPF los elementos agregados deben ser (o deberían ser) de un tipo concreto y las columnas (o cabeceras) deben hacer referencia a las propiedades que queremos mostrar.

    En el ejemplo que he puesto del ListView para WPF podemos usar una clase que defina al menos tres propiedades (no campos públicos) con los nombres usados después de Binding, en este ejemplo esas propiedades deben ser: Nombre, email y Edad.

    Nota:

    Los nombres usados en Binding deben coincidir "exactamente" con los nombres de las propiedades, es decir, no deben diferenciarse en las mayúsculas/minúsculas… ¡ni siquiera en Visual Basic!

    Además deben estar definidos como propiedades, no como métodos ni campos públicos.

     

    Código de cómo agregar y recuperar los elementos

    Veamos cómo agregar elementos al ListView y después veremos cómo recuperar esos elementos.

    Te voy a mostrar dos formas de hacerlo:

    1.- usando tipos anónimos (no es recomendable, pero si sabes lo que haces, es fácil de utilizar, aunque a la hora de recuperar los datos en Visual Basic deberías hacerlo usando Option Strict Off al menos en el fichero de código en el que estés usando el tipo anónimo. En C# tendrás que acceder a ese dato usando dynamic.

    2.- usando un tipo de datos previamente definido con esas tres propiedades.

     

    Agregar datos usando un tipo anónimo

    Para agregar los datos en este ejemplo, voy a usar un bucle que agregue 10 objetos al ListView. Como son objetos de tipo anónimo usamos un tipo anónimo que defina las propiedades que necesitamos.

    VB:

    ' Rellenar el listview con datos
    lvDatos.Items.Clear()
    For i = 1 To 10
    
        ' Asignamos un tipo anónimo
        ' Si las columnas (GridViewColumn) tienen un Binding a las propiedades
        ' los valores se asignarán a cada columna, si no están enlazadas
        ' se agregará el valor de ToString a cada columna (estará el texto repetido)
        lvDatos.Items.Add(New With 
                          {.Nombre = "Nombre anónimo" & i.ToString,
                           .email = "correo" & i.ToString & "@outlook.com",
                           .Edad = 17 + i
                          })
    
    Next
    

    C#:

    // Rellenar el listview con datos
    lvDatos.Items.Clear();
    for (var i = 1; i <= 10; i++)
    {
    
        // Asignamos un tipo anónimo
        // Si las columnas (GridViewColumn) tienen un Binding a las propiedades
        // los valores se asignarán a cada columna, si no están enlazadas
        // se agregará el valor de ToString a cada columna (estará el texto repetido)
        lvDatos.Items.Add(new
        {
            Nombre = "Nombre anónimo" + i.ToString(),
            email = "correo" + i.ToString() + "@outlook.com",
            Edad = 17 + i
        });
    
    }
    

     

    Recuperar los datos usando un tipo anónimo

    La forma de recuperar esos datos es usando una variable de tipo Object en Visual Basic y en el caso de C# esa variable debe ser de tipo dynamic, ya que en ambos lenguajes vamos a usar late-binding o enlace tardío, es decir, hasta que no se ejecute el código no se sabrá si esas propiedades están definidas o no en el objeto recuperado del ListView.

    Es importante que leas y pruebes el comentario que te hago al final del código sobre qué ocurriría si quisiéramos acceder a una propiedad que no está definida en el "tipo anónimo".

    VB:

    ' Si tenemos Option Strict Off podemos hacer esto:
    Dim v = lvDatos.SelectedItem
    
    txtMostar.Text = v.Nombre & " (" & v.email & ")"
    
    ' El problema es que queramos acceder a una propiedad que no exista
    ' (esto dará error)
    'txtMostar.Text = v.Nombre & " (" & v.cagonto & ")"
    

    C#:

    // En C# debemos definir la variable como dynamic:
    dynamic v = lvDatos.SelectedItem;
    
    txtMostar.Text = v.Nombre + " (" + v.email + ")";
    
    // El problema es que queramos acceder a una propiedad que no exista
    // (esto dará error)
    //txtMostar.Text = v.Nombre + " (" + v.cagonto + ")";
    

     

    Agregar datos usando un tipo definido (Colega)

    Como podrás comprobar, el código de agregar los datos es muy parecido al anterior, la diferencia está en que después de "NEW" usamos el tipo Colega en vez de no indicar qué tipo de datos estamos usando.

    VB:

    ' Rellenar el listview con datos
    lvDatos.Items.Clear()
    For i = 1 To 10
    
        ' Asignamos un valor de un tipo definido (Colega)
        ' que tiene como mínimo las propiedades que vamos a usar
        ' en el listiView
    
         lvDatos.Items.Add(New Colega With
                          {.Nombre = "Nombre " & i.ToString,
                           .email = "email" & i.ToString & "@outlook.com",
                           .Edad = 17 + i
                          })
    
    Next
    

    C#:

    // Rellenar el listview con datos
    lvDatos.Items.Clear();
    for (var i = 1; i <= 10; i++)
    {
    
        // Asignamos un valor de un tipo definido (Colega)
        // que tiene como mínimo las propiedades que vamos a usar
        // en el listiView
    
        lvDatos.Items.Add(new Colega
        {
            Nombre = "Nombre " + i.ToString(), 
            email = "email" + i.ToString() + "@outlook.com", 
            Edad = 17 + i
        });
    
    }
    

     

    Recuperar los datos usando un tipo definido (Colega)

    En este caso, como estamos usando un tipo definido previamente, lo único que tenemos que hacer a la hora de recuperar el elemento seleccionado es hacer un "cast", es decir, una conversión al tipo Colega, ya que el valor devuelto por SelectedItem es del tipo object, ya que los elementos de un ListView pueden ser de cualquier tipo, incluso tipos mezclados… aunque esto último no lo he probado, pero hacer se puede hacer… otra cosa es hacerlo bien, jeje.

    VB:

    Dim v = TryCast(lvDatos.SelectedItem, Colega)
    
    txtMostar.Text = v.Nombre & " (" & v.email & ")"
    
    ' como ahora usamos un tipo previamente definido
    ' si usamos esto:
    '   txtMostar.Text = v.Nombre & " (" & v.cagonto & ")"
    ' nos dirá que "cagonto" no es un miembro de Colega
    

    C#:

    var v = ((Colega)lvDatos.SelectedItem);
    
    txtMostar.Text = v.Nombre + " (" + v.email + ")";
    
    // como ahora usamos un tipo previamente definido
    // si usamos esto:
    //   txtMostar.Text = v.Nombre + " (" + v.cagonto + ")";
    // nos dirá que "cagonto" no es un miembro de Colega
    

     

    Y esto es todo… espero que te haya aclarado algunos conceptos y dudas… y si quieres saber más sobre los ListView, los GridView y cómo hacer maravillas con el contenido de los ListView, te recomiendo que le eches un vistazo a los enlaces que te he puesto en cada una de esos tipos de datos.

     

    Un poco más abajo tienes el código compelto tanto del XAML como el de Visual Basic y C#.

    Ese código lo he hecho con Visual Studio 2012, pero supongo que será válido en otras versiones anteriores: en el caso de VB desde el VS2008, y en el caso de C# desde Visual Studio 2010.

     

    Nota:

    En ese código hay comentadas un par de pruebas que puedes hacer.

    Entre ellas está la de no asignar ningún valor a la propiedad Binding de los GridViewColumn y asignar cadenas normales a los elementos del ListView, después al ejecutar el programa y al recuperar el contenido del elemento seleccionado verás que… no te lo cuento… así lo tendrás que probar 😉

     

     

    Espero que te sea de utilidad.

    Nos vemos.

    Guillermo

     


    Código Xaml El código Xaml

    En VB usa esta definición de la ventana:

    <Window x:Class="MainWindow"

    En C# usa esta definición de la ventana (o pon el nombre del espacio de nombres del proyecto delante de MainWindows):

    <Window x:Class="WpfListView_cs.MainWindow"

     

    <Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" 
        Height="380" Width="525">
        <Grid>
            <StackPanel Orientation="Vertical" Margin="10,10">
                <ListView Name="lvDatos" Margin="0" MinHeight="240"
                      VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
                      SelectionChanged="lvDatos_SelectionChanged" >
                <ListView.View>
                    <GridView AllowsColumnReorder="True">
                        <!-- Utilizo valores en Binding distintos a los nombres de las cabeceras
                             para que se vea que no hay relación entre el nombre del Header
                             y el nombre de Binding -->
                            <GridViewColumn Header="Nombre y apellidos" 
                                            DisplayMemberBinding="{Binding Nombre}" Width="150"/>
                            <GridViewColumn Header="Correo" 
                                            DisplayMemberBinding="{Binding email}" Width="190"/>
                            <GridViewColumn Header="How old" 
                                            DisplayMemberBinding="{Binding Edad}" Width="70"/>
                    </GridView>
                </ListView.View>
                </ListView>
                <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="10">
                    <Button Content="Rellenar" Click="Button_Click_1" />
                    <Label />
                    <Button Name="btnMostrar" Content="Mostrar seleccionado" 
                            IsEnabled="False" Click="btnMostrar_Click" />
                    <Label />
                    <CheckBox Name="chkAnonimo" IsChecked="True" 
                              Content="Usar tipo anónimo (si no, tipo definido)" />
                </StackPanel>
                <TextBlock Name="txtMostar" Text="Una fila"/>
                <TextBlock Name="txtMostar2" Text="Una fila"/>
            </StackPanel>
        </Grid>
    </Window>
    

     

    Código para Visual Basic.NET (VB.NET) El código para Visual Basic .NET
    '------------------------------------------------------------------------------
    ' Ejemplo de ListView usando WPF para Desktop                       (27/Dic/12)
    ' y cómo acceder a las columnas (sin usar SubItems)
    '
    ' ©Guillermo 'guille' Som, 2012
    '------------------------------------------------------------------------------
    
    Option Strict Off
    Option Infer On
    
    Class MainWindow
    
        Private Sub Button_Click_1(sender As Object, e As RoutedEventArgs)
            ' Rellenar el listview con datos
            lvDatos.Items.Clear()
            For i = 1 To 10
    
                '' Esto añadirá el mismo texto a todas las columnas
                '' siempre y cuando no hayamos usado ningún Binding
                '' si no, esto no añade nada de nada...
                '.Items.Add("Item número " & i.ToString("00"))
    
    
                If chkAnonimo.IsChecked Then
                    ' Asignamos un tipo anónimo
                    ' Si las columnas (GridViewColumn) tienen un Binding a las propiedades
                    ' los valores se asignarán a cada columna, si no están enlazadas
                    ' se agregará el valor de ToString a cada columna (estará el texto repetido)
                    lvDatos.Items.Add(New With
                                      {.Nombre = "Nombre anónimo" & i.ToString,
                                       .email = "correo" & i.ToString & "@outlook.com",
                                       .Edad = 17 + i
                                      })
    
                Else
                    ' Asignamos un valor de un tipo definido (Colega)
                    ' que tiene como mínimo las propiedades que vamos a usar
                    ' en el listiView
    
                    lvDatos.Items.Add(New Colega With
                                      {.Nombre = "Nombre " & i.ToString,
                                       .email = "email" & i.ToString & "@outlook.com",
                                       .Edad = 17 + i
                                      })
    
                End If
    
            Next
    
        End Sub
    
        Private Sub btnMostrar_Click(sender As Object, e As RoutedEventArgs)
            ' Mostar en el TextBlock el elemento seleccionado
    
            ' Dependiendo de que sea el tipo anónimo o el definido
            ' el valor mostrado será diferente.
            ' En el caso de la clase Colega, si no tenemos sobrecargado el método ToString
            ' mostrará el nombre de la clase: WpfListView_vb.Colega
            txtMostar2.Text = lvDatos.SelectedItem.ToString
    
            If chkAnonimo.IsChecked Then
                ' Si tenemos Option Strict Off podemos hacer esto:
                Dim v = lvDatos.SelectedItem
    
                txtMostar.Text = v.Nombre & " (" & v.email & ")"
    
                ' El problema es que queramos acceder a una propiedad que no exista
                ' (esto dará error)
                'txtMostar.Text = v.Nombre & " (" & v.cagonto & ")"
    
            Else
    
                Dim v = TryCast(lvDatos.SelectedItem, Colega)
    
                txtMostar.Text = v.Nombre & " (" & v.email & ")"
    
                ' como ahora usamos un tipo previamente definido
                ' si usamos esto:
                '   txtMostar.Text = v.Nombre & " (" & v.cagonto & ")"
                ' nos dirá que "cagonto" no es un miembro de Colega
    
            End If
    
        End Sub
    
        Private Sub lvDatos_SelectionChanged(sender As Object, e As SelectionChangedEventArgs)
            ' habilitar según haya o no elementos seleccionados
            btnMostrar.IsEnabled = (lvDatos.SelectedItems.Count > 0)
        End Sub
    
    End Class
    
    ''' <summary>
    ''' Tipo Colega, con las popiedades (o campos)
    ''' que tendrán las columnas del ListView.
    ''' Aunque puede tener más propiedades
    ''' aunque no estén enlazadas con las columnas
    ''' </summary>
    Class Colega
        Public Property Nombre As String
        Public Property email As String
        Public Property Edad As Integer
    
        ' Podemos tener más propiedadas
        Public Property Apellidos As String
    End Class
    
    

     

    Código para C Sharp (C#) El código para C#
    //-----------------------------------------------------------------------------
    // Ejemplo de ListView usando WPF para Desktop                      (27/Dic/12)
    // y cómo acceder a las columnas (sin usar SubItems)
    //
    // ©Guillermo 'guille' Som, 2012
    //-----------------------------------------------------------------------------
    
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    
    namespace WpfListView_cs
    {
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
    
            private void Button_Click_1(object sender, RoutedEventArgs e)
            {
                // Rellenar el listview con datos
                lvDatos.Items.Clear();
                for (var i = 1; i <= 10; i++)
                {
    
                    //' Esto añadirá el mismo texto a todas las columnas
                    //' siempre y cuando no hayamos usado ningún Binding
                    //' si no, esto no añade nada de nada...
                    //.Items.Add("Item número " + i.ToString("00"));
    
    
                    if ((bool)chkAnonimo.IsChecked)
                    {
                        // Asignamos un tipo anónimo
                        // Si las columnas (GridViewColumn) tienen un Binding a las propiedades
                        // los valores se asignarán a cada columna, si no están enlazadas
                        // se agregará el valor de ToString a cada columna (estará el texto repetido)
                        lvDatos.Items.Add(new
                        {
                            Nombre = "Nombre anónimo" + i.ToString(),
                            email = "correo" + i.ToString() + "@outlook.com",
                            Edad = 17 + i
                        });
    
                    }
                    else
                    {
                        // Asignamos un valor de un tipo definido (Colega)
                        // que tiene como mínimo las propiedades que vamos a usar
                        // en el listiView
    
                        lvDatos.Items.Add(new Colega
                        {
                            Nombre = "Nombre " + i.ToString(), 
                            email = "email" + i.ToString() + "@outlook.com", 
                            Edad = 17 + i
                        });
                    }
                }
    
            }
    
            private void btnMostrar_Click(object sender, RoutedEventArgs e)
            {
                // Mostar en el TextBlock el elemento seleccionado
    
                // Dependiendo de que sea el tipo anónimo o el definido
                // el valor mostrado será diferente.
                // En el caso de la clase Colega, si no tenemos sobrecargado el método ToString
                // mostrará el nombre de la clase: WpfListView_vb.Colega
                txtMostar2.Text = lvDatos.SelectedItem.ToString();
    
                if ((bool)chkAnonimo.IsChecked)
                {
                    // En C# debemos definir la variable como dynamic:
                    dynamic v = lvDatos.SelectedItem;
    
                    txtMostar.Text = v.Nombre + " (" + v.email + ")";
    
                    // El problema es que queramos acceder a una propiedad que no exista
                    // (esto dará error)
                    //txtMostar.Text = v.Nombre + " (" + v.cagonto + ")";
    
                }
                else
                {
                    
                    var v = ((Colega)lvDatos.SelectedItem);
    
                    txtMostar.Text = v.Nombre + " (" + v.email + ")";
    
                    // como ahora usamos un tipo previamente definido
                    // si usamos esto:
                    //   txtMostar.Text = v.Nombre + " (" + v.cagonto + ")";
                    // nos dirá que "cagonto" no es un miembro de Colega
    
                }
            }
    
            private void lvDatos_SelectionChanged(object sender, SelectionChangedEventArgs e)
            {
                // habilitar según haya o no elementos seleccionados
                btnMostrar.IsEnabled = (lvDatos.SelectedItems.Count > 0);
            }        
    
        }
    
        /// <summary>
        /// Tipo Colega, con las popiedades (o campos)
        /// que tendrán las columnas del ListView.
        /// Aunque puede tener más propiedades
        /// aunque no estén enlazadas con las columnas
        /// </summary>
        class Colega
        {
            public string Nombre { get; set; }
            public string email { get; set; }
            public int Edad { get; set; }
    
            // Podemos tener más propiedadas
            public string Apellidos { get; set; }
        }
    }
    
    

    Espacios de nombres usados en el código de este artículo:

    System.Windows

    System.Windows.Controls

     

     

    El artículo original está en mi sitio, este de mi blog sólo es por si quieres hacer comentarios sobre lo aquí explicado.