Etiqueta: Visual Studio

Posts relacionados con el entorno de desarrollo (IDE) de Visual Studio

  • Crear un fichero de estilos XAML y acceder a estilos definidos en otro fichero

     

    Pues eso… que estaba yo haciendo modificaciones en los estilos que vienen en el fichero StandardStyles.xaml (incluido en las plantillas de aplicaciones para Windows Store), y como no tenía muy claro cómo definir esas modificaciones en otro fichero diferente del que se incluye en las plantillas de los proyectos para Windows Store, al final acabé dejándolos en ese mismo fichero.

    Pero hoy (o ayer, ya no llevo la cuenta de los días y las noches) me puse a hacerlo (de nuevo), es decir, me fui a agregar un nuevo fichero del tipo ResourceDictionary (Dictionary1.xaml) y ahí pegué los estilos que yo definí para usar en otra aplicación y que lo mismo me podrán ser de utilidad en esta nueva.

    Una vez que has creado un nuevo "diccionario de recursos", debes añadirlo al elemento ResourceDictionary de App.xaml, concretamente en ResourceDictionary.MergedDictionaries, que uno sabe esto, entre otras cosas porque ahí es donde está indicado el fichero de estilos estándar.

    Como mis nuevos estilos (algunos de ellos) están basados en los que se incluyen en StandardStyles.xaml, lo que hice es agregarlo después del StandardStyles (por aquello de que así referencie primero el estándar y después el mío). Pero no… así no vale.

    El problema es que además el Visual Studio no te dice nada de que eso está mal, la cuestión es que la aplicación no funciona y, lo más frustrante es que no sabes por qué.
    Ahora sé que es porque estaba haciendo referencia a estilos que no estaban definidos (al menos en mi fichero).
    Pero el problema es que yo "sabía" que sí, que esos estilos estaban definidos, pero en otro fichero.
    Y ese era el problema, que estaban en otro fichero.

    Busqué en la ayuda a ver… y curiosamente vi que en uno de los ejemplos había dos ficheros definidos en MergedDictionaries de App.xaml. Y en realidad eso hay que hacerlo así:

     

    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
    
                <!-- 
                    Styles that define common aspects of the platform look and feel
                    Required by Visual Studio project and item templates
                 -->
                <ResourceDictionary Source="Common/StandardStyles.xaml"/>
                <ResourceDictionary Source="Dictionary1.xaml"/>
                
            </ResourceDictionary.MergedDictionaries>
    
            <!-- Application-specific resources -->
    
            <x:String x:Key="AppName">Libros SplitApp</x:String>
        </ResourceDictionary>
    </Application.Resources>
    
    

     

    Pero lo que debes saber (todo este rollo para contarte esto) es que si tu fichero va a usar estilos definidos en otro fichero (al menos si tu intención es crear nuevos estilos que se basen en algunos de los definidos en ese fichero) debes indicarlo de forma explícita en tu fichero, y sería incluyéndolo en un elemento del tipo MergedDictionaries, pero en tu fichero.

    Por ejemplo, si desde mi fichero Dictionary1.xaml quiero utilizar estilos definidos en StandardStyles debo añadir este código al principio del fichero (o en la parte superior, después de las definiciones de ResourceDictionary):

     

    <ResourceDictionary
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:Libros_SplitApp">
    
        <ResourceDictionary.MergedDictionaries>
            <!-- 
                        Styles that define common aspects of the platform look and feel
                        Required by Visual Studio project and item templates
                     -->
            <ResourceDictionary Source="Common/StandardStyles.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    
        <!-- Mis estilos basados en otros existentes en StandardStyles.xaml -->
      
    

     

    Para que nos entendamos:

    1.- Si quiero que en mi aplicación los recursos definidos en mi fichero estén accesibles, debo indicarlo en MergedDictionary de App.xaml.

    2.- Si quiero que mi fichero utilice estilos definidos en otro fichero de estilos XAML debo crear un elemento MergedDictionary y añadirlo de la misma forma que el mío está indicado en App.xaml.

     

    Y eso es todo…

    Pero habrá más, ya que el XAML no se lleva muy bien que digamos con el "depurador" de Visual Studio y hay más cosas que pueden pasar sin que sepas por qué pasan… aunque para eso estoy yo aquí, par contártelo 😉

     

    Nos vemos.

    Guillermo

  • Las plantillas de Windows Store o la dejadez para con Visual Basic

     

    Pues eso… que "trasteando" con las plantillas (tipos de aplicaciones) para Windows Store usando Visual Basic como lenguaje de programación, me he topado con un par de "chorradas" que aunque no son importantes creo que son muestra de, siendo benévolo (por aquello de las fechas en que estamos), un poco de dejadez por parte de "a quién corresponda".

    Nota del 29/Dic/12:
    He agregado un extra…

    Me estoy refiriendo a las plantillas de proyectos para Windows Store de más de una página es decir: GridApp y SplitApp (ver la figura 1).

     

    proyectos_appstore
    Figura 1. Crear un nuevo proyecto para Windows Store con Visual Basic

     

    Si tienes Option Strict On, es decir: ser estricto con las declaraciones y sobre todo con las asignaciones, lo primero que te encuentras es con esta asignación en App.xaml.vb:

    Dim rootFrame As Frame = Window.Current.Content
    
    

    Y la comprobación estricta te indica que esa conversión implícita no es correcta, por suerte, Visual Basic nos ofrece una solución a ese error, tal como vemos en la figura 2, en la que propone que hagamos la conversión con CType.

     

    error de conversion implicita

    Figura 2. Error de conversión implícita y la solución

    Como vemos, la conversión de tipos propuesta es usando CType aunque yo prefiero usar TryCast que es más liviano sobre todo si sabemos que esa conversión es correcta y no producirá un error en tiempo de ejecución.

     

    TryCast devuelve un valor nulo si no puede hacer la conversión, mientras que CType producirá una excepción.

     

    Otra de esas "chorradillas" que te comento que me he encontrado es en el método ItemsCollectionChanged de la clase SampleDataGroup que está en la carpeta DataNodel de estos dos proyectos.

    En dos de los "Case" que realiza al hacer una doble comprobación, es decir, comprueba si esto y aquello está ocurriendo, utilizar And en lugar de AndAlso.

    ¡¡¡ En este código vemos las dos líneas que contienen los operadores And fatídicos !!!

     

    If e.NewStartingIndex < 12 And e.OldStartingIndex < 12 Then
    
    
    While TopItems.Count < Items.Count And TopItems.Count < 12

     

    Puede que creas que no es para tanto, pero si de verdad conoces lo que hace cada uno de esos dos operadores… La cuestión es que yo no utilizo And para una comparación desde que salió la primera versión de Visual Basic para .NET, de hecho antes casi tampoco la usaba y prefería hacer una doble comprobación: usar dos Ifs en lugar de usar And.

    Además la documentación de Visual Studio sobre el operador And te indica la diferencia entre And y AndAlso:

    En una comparación booleana, el operador And evalúa siempre las dos expresiones, lo que podría incluir llamadas a procedimientos. AndAlso realiza un cortocircuito, lo que significa que si expression1 es False, no se evalúa expression2.

     

    Lo curioso de esos And es que si el código está convertido de C# es extraño que no conviertan adecuadamente el operador usado en C#: &&, pero bueno… como And también puede ser un solo ampersand: & pues… es fácil que el conversor de código se equivoque.

     

    Ya te dije que eran cosas triviales, pero en ocasiones ver que no se cuidan esos pequeños detalles te pueden hacer pensar si habrá algo más que también pueda fallar… ¡esperemos que no! 😉

     

    Nos vemos.

    Guillermo

     

    Más de lo mismo (addendum del 29 de diciembre de 2012):

    Efectivamente, tiene toda la pinta de ser una traducción de C# a Visual Basic, pero "presuntamente" sin mucho cuidado en el resultado final (o casi), ya que en la clase SampleDataSource, concretamente en el constructor se asignan ciertos valores de prueba y una cadena con el contenido, que no es más que la misma cadena repetida varias veces, pero en C# usan \n\n para crear cambios de líneas y en VB lo han dejado con esos mismos "retornos de línea" que no hacen más que mostrar esos caracteres en la cadena en lugar de hacer los cambios de línea.

    La posible solución a esta "chorradilla" de fallo sería algo así:

    Dim ITEM_CONTENT As String = String.Format("Item Content: {0}{1}{0}{1}{0}{1}{0}{1}{0}{1}{0}{1}{0}",
                "Curabitur class aliquam vestibulum nam curae maecen ..."
                vbCrLf)
    
    

     

    Pues eso… en fin… es que me hierve la sangre… si no quieren que usemos el Visual Basic, ¡que lo quiten! pero si lo dejan que esté donde tiene que estar: en lugar preferente.

  • 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.