Los métodos extendidos (o extensores) según la definición de MSDN nos permiten “agregar” métodos a los tipos existentes sin necesidad de crear un nuevo tipo derivado y volver a compilar o sin necesidad de modificar el tipo original. En otras palabras, si queremos “personalizar” la funcionalidad que un método no nos puede dar, esta es la opción indicada.
Para comenzar, primero creo que es mejor en esta ocasión dar primero el contexto para después poder realizar el ejercicio ¿Te parece bien? El escenario es sencillo, deber leer un XML que tiene ciertos atributos, y después, debes leer otro con CASI los mismos atributos pero uno falta, ese detalle hará que al compilar la aplicación te aparezca un bello error como el siguiente. (Cualquier parecido con los lectores de feeds es una mera coincidencia )
Entonces la solución será crear un método extendido, que verificará que en el caso de que tu XML contenga algún elemento ausente reemplazará ese valor por uno preestablecido. Checa las dos opciones de XML posibles.
Este es el XML correcto
<?xml version="1.0" encoding="UTF-8"?> <elementos> <elemento> <titulo>Gráfica de barras</titulo> <descripcion>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</descripcion> <icono direccion="Barras.png" /> </elemento> <elemento> <titulo>Gráfica de lineas</titulo> <descripcion>Cras vehicula vitae. Vivamus id erat euismod sit amet lectus.</descripcion> <icono direccion="Lineas.png" /> </elemento> <elemento> <titulo>Gráfica de pie</titulo> <descripcion>Class aptent taciti litora torquent nostra, inceptos himenaeos.</descripcion> <icono direccion="Pie.png" /> </elemento> </elementos> |
Y este es el XML sin el atributo “icono”.
<?xml version="1.0" encoding="UTF-8"?> <elementos> <elemento> <titulo>Gráfica de barras</titulo> <descripcion>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</descripcion> </elemento> <elemento> <titulo>Gráfica de lineas</titulo> <descripcion>Cras vehicula vitae. Vivamus id erat euismod sit amet lectus.</descripcion> </elemento> <elemento> <titulo>Gráfica de pie</titulo> <descripcion>Class aptent taciti litora torquent nostra, inceptos himenaeos.</descripcion> </elemento> </elementos> |
Bien, ahora si, con el escenario ya planteado, vamos a crear una solución de tipo Windows Phone. Agrega dos botones y un ListBox a tu interfaz, para dejarlos de la siguiente forma.
<Grid x:Name="LayoutRoot" Background="Transparent"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28"> <TextBlock x:Name="ApplicationTitle" Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}"/> <TextBlock x:Name="PageTitle" Text="page name" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/> </StackPanel> <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <Button x:Name="btnConImagen" Content="Con imagen" Height="85" VerticalAlignment="Top" HorizontalAlignment="Left" Width="226" Click="btnConImagen_Click"/> <Button x:Name="btnSinImagen" Content="Sin imagen" Height="85" VerticalAlignment="Top" HorizontalAlignment="Right" Width="226" Click="btnSinImagen_Click"/> <ListBox x:Name="lstContenido" Margin="8,89,8,8" > </Grid> </Grid> |
Agrega como recurso de la página un DataTemplate para el ListBox, con el siguiente XAML.
<phone:PhoneApplicationPage.Resources> <DataTemplate x:Key="ResultadoTemplate"> <Grid Height="86" Margin="5" Width="450"> <Image HorizontalAlignment="Left" Margin="8,8,0,11" Width="70" Height="70" Source="{Binding RutaImagen}"/> <TextBlock Margin="82,8,8,0" TextWrapping="Wrap" Text="{Binding Titulo}" Height="30" VerticalAlignment="Top" FontSize="21.333"/> <TextBlock Margin="82,38,8,8" TextWrapping="Wrap" Text="{Binding Descripcion}"/> </Grid> </DataTemplate> </phone:PhoneApplicationPage.Resources> |
Y por último, entonces ya puedes enlazar los datos de tu ListBox y además personalizar su item template.
<ListBox x:Name="lstContenido" Margin="8,89,8,8" ItemsSource="{Binding}" ItemTemplate="{StaticResource ResultadoTemplate}"/> |
Ahora vamos con los manejadores de eventos, vas a establecer una sola línea para ellos.
private void btnConImagen_Click(object sender, System.Windows.RoutedEventArgs e) { LeerXML(@"./Recursos/Archivos/archivoConImagen.xml"); } private void btnSinImagen_Click(object sender, System.Windows.RoutedEventArgs e) { LeerXML(@"./Recursos/Archivos/archivoSinImagen.xml"); } |
El método LeerXML es el siguiente.
private void LeerXML(string direccion) { XDocument doc = XDocument.Load(direccion); List<Resultado> listaResultados = new List<Resultado>(); var notas = from texto in doc.Descendants("elemento") select new { titulo = texto.Element("titulo").Value.ToString(), descripcion = texto.Element("descripcion").Value.ToString(), imagen = texto.Element("icono").Attribute("direccion")Value.ToString() }; foreach (var item in notas) { Resultado resultado = new Resultado(); resultado.Titulo = item.titulo; resultado.Descripcion = item.descripcion; resultado.RutaImagen = "Recursos/Imagenes" + item.imagen; listaResultados.Add(resultado); } lstContenido.ItemsSource = listaResultados; } |
La clase que estás usando en la lista, la que se llama Resultado, tiene las siguientes propiedades.
public class Resultado { public string Titulo { get; set; } public string Descripcion { get; set; } public string RutaImagen { get; set; } } |
Hasta aquí, puedes ejecutar la aplicación, el resultado del primer botón te dará lo esperado.
Presiona el segundo botón y resultará el error del inicio y el que justamente estamos buscando intencionalmente en esta aplicación.
Crea una clase (en mi caso la llamaré «Validador”), la que contendrá lo siguiente.
using System.Xml.Linq; namespace Metodos_extendidos { public static class Valedor { public static XAttribute ValorDefault(this XElement elemento, XName nombre, string valorDefault) { var valor = elemento; return valor == null ? new XAttribute(nombre, valorDefault) : valor.Attribute(nombre); } } } |
Lo primero que debes considerar es que debes hacer tu clase estática, dado que el método extendido será de este tipo, lo segundo que debes considerar es que el primer parámetro que requieres usar deberá de ser del tipo de elemento al cual le quieres “extender” el método, los demás parámetros serán los que necesitarás para tu operación. Después todo es sencillo, solo evalúa el parámetro recibido por medio de un condicional (si quieres saber un poco mas de operadores ternarios, este post es genial). Ya que cuentas con tu método extendido, regresa a tu MainPage y modifica tu método LeerXML, al leer el nodo elemento con la consulta LINQ, ahora podrás ver el método extendido que creaste por medio del IntelliSense de Visual Studio.
Con el uso del método extendido, el método LeerXML quedará de la siguiente manera.
private void LeerXML(string direccion) { XDocument doc = XDocument.Load(direccion); List<Resultado> listaResultados = new List<Resultado>(); var notas = from texto in doc.Descendants("elemento") select new { titulo = texto.Element("titulo").Value, descripcion = texto.Element("descripcion").Value, imagen = texto.Element("icono").ValorDefault("direccion", "N/A").Value }; foreach (var item in notas) { Resultado resultado = new Resultado(); resultado.Titulo = item.titulo; resultado.Descripcion = item.descripcion; AsignarImagen(resultado, item.imagen); listaResultados.Add(resultado); } lstContenido.ItemsSource = listaResultados; } |
Como puedes ver al ir agregando los elementos en el ciclo, debes utilizar un método llamado AsignarImagen, este método es opcional y meramente complementario.
private void AsignarImagen(Resultado elemento, string direccionImagen) { if (direccionImagen == "N/A") elemento.RutaImagen = "Recursos/Imagenes/Borrar.png"; else elemento.RutaImagen = "Recursos/Imagenes/" + direccionImagen; } |
Simplemente deberás ver que el parámetro direccionImagen tenga un valor, y si este es uno que no corresponda a la dirección de una imagen, puedes asignarle una diferente.
Listo!!!! Así de sencillo es poder crear métodos extendidos, una práctica que he visto poco habitual, pero sumamente útil para ahorrar mucho tiempo.
Puedes descargar el código de ejemplo aquí.