Como implementar TemplateSelector en el ListBox de Windows Phone 7

Si solo has trabajando con Silverlight nunca has conocido el TemplateSelector de WPF, que como su nombre indica permite hacer un selector por discriminador para las plantillas de datos. En el caso que nos atañe ListBox, tiene una propiedad llamada ItemTemplate en la que se establece la plantilla de datos para cada uno de los ítems.

¿Para qué se puede querer cambiar la plantilla?

Imaginaros el escenario de estar haciendo una aplicación para mostrar una lista de noticias provenientes de un rss, podemos tener una plantilla para las noticias con imágenes, otra plantilla para las noticias sin imágenes y además podemos querer una plantilla especial para una noticia destacada. Este tipo de escenario que es el más común es bastante difícil de conseguir con Silverlight puesto que no tiene TemplateSelector.

Para conseguir esta funcionalidad tenemos que implementarlo a mano.

Hay varias maneras de llegar hasta esta aproximación la que desde mi punto de vista es la más adecuada es crear un ListBox personalizado, porque nos permite tener toda la funcionalidad y aspecto existente del ListBox pero con el selector de plantillas.

El proceso de creación de un ListBox personalizado se hace en dos partes, la primera se tiene que hacer una clase que herede de ListBox. En esta clase, que llamaremos DataTemplateListBox, tenemos que sobrescribir dos métodos virtuales:

  • GetContainerForItemOverride: este método devuelve un objeto que será el ítem container que el ListBox usará para alojar los ítems que se establezcan en el ItemSource. En el caso del ListBox, la implementación predeterminada de este método devuelve un ListBoxItem que es el contenedor predeterminado del ListBox. En nuestro ejemplo tenemos que generar un ListBoxItem personalizado que tendrá la funcionalidad de establecer el DataTemplate.
  • PrepareContainerForItemOverride: Este método es llamado cuando el ListBox está a punto de empezar a hacer la pasada de Layout, y es el momento justo para establecer el código de nuestros discriminador. Este método acepta dos parámetros, uno llamado element de tipo DependencyObject que es el contenedor del ListBox, y el otro ítem de tipo object que es el objeto que nosotros estamos estableciendo al ListBox.
public class DataTemplateListBox : ListBox
{
    protected override DependencyObject GetContainerForItemOverride()
    {
        return new DataTemplateListBoxItem();
    }
    protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
    {
        base.PrepareContainerForItemOverride(element, item);
        DataTemplateListBoxItem lbi = (DataTemplateListBoxItem)element;
        if (item is News)
        {
            News newItem = (News)item;
            if (!newItem.IsHighLighted)
            {
                lbi.CustomTemplate = (DataTemplate)App.Current.Resources["NewsDataTemplate"];
            }
            else
            {
                lbi.CustomTemplate = (DataTemplate)App.Current.Resources["HighLightedNewsDataTemplate"];
            }
        }
    }
}

Como hemos dicho anteriormente en el proceso de creación del ListBox tenemos que crear también un ListBoxItem que será el contenedor neutro que tendrá una propiedad de tipo DataTemplate, que será el DataTemplate usado para dibujar ese elemento con una plantilla especifica.

[TemplatePart(Name = "DisplayContent", Type = typeof(ContentControl))]
public class DataTemplateListBoxItem : ListBoxItem
{
    #region Properties
    
    public DataTemplate CustomTemplate
    {
        get { return (DataTemplate)GetValue(CustomTemplateProperty); }
        set { SetValue(CustomTemplateProperty, value); }
    }

    public static readonly DependencyProperty CustomTemplateProperty =
        DependencyProperty.Register(
            "CustomTemplate", 
            typeof(DataTemplate), 
            typeof(DataTemplateListBoxItem), 
            new PropertyMetadata(null));

    #endregion

    #region Constructors
    public CategoryItemsListBoxItem()
    {
        DefaultStyleKey = typeof(DataTemplateListBoxItem);
    }
    #endregion
}

Este es el código de ejemplo de un DataTemplateListBoxItem, pero también necesitamos generar un estilo predeterminado que contenga un elemento de tipo ContentControl que será el elemento que tendrá la interfaz del usuario del elemento.

<Style TargetType="controls:DataTemplateListBoxItem" >
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="controls:DataTemplateListBoxItem">
                <ContentControl x:Name="DisplayContent" Content="{TemplateBinding DataContext}" 
                                ContentTemplate="{TemplateBinding CustomTemplate}"  />
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Como se puede apreciar en el código xaml lo que se hace es agregar a la plantilla un ControlTemplate y en ese ControlTemplate se establecen dos propiedades Content con un binding de plantilla (TemplateBinding) a la propiedad DataContext, con eso conseguirnos establecer el objeto como contenido y que se dibuje. La otra propiedad que falta por establecer es justamente la propiedad ContentTemplate que en este caso se hace lo mismo, hacer un binding de plantilla con la propiedad CustomTemplate que es de la clase DataTemplateListBoxItem.

Ahora lo que tenemos que hacer es insertar nuestro DataTemplateListBox en el control MainPage y enlazarle un ViewModel con datos para mostrar un par de noticias generadas por código:

<Grid x:Name="ContentPanel" Grid.Row="1">
   <DataTemplateDemo_Controls:DataTemplateListBox ItemsSource="{Binding Items}"/>
</Grid>

Con eso conseguimos tener un ListBox en el nosotros decidimos en cada uno de los elementos como queremos aplicarle una plantilla de datos. Este es el resultado:

image

La demo no es muy impresionante en sí, pero permite tener la flexibilidad de poder elegir elemento por elemento cual es la plantilla que vamos a usar de manera programática.

La demo completa la podéis descargar de aquí.

Luis Guerrero.

Gestión de proxies de WCF en Silverlight 4

Una de las cosas que más esfuerzo me cuestan cuanto estoy desarrollando aplicaciones en Silverlight es la gestión del ciclo de vida de un proxy de WCF, es decir, crear la instancia del proxy con la configuración del binding correcta, subscribirme a los eventos complete de las operaciones que quiero invocar y en caso de falta en el proxy volver a hacer todo ese trabajo, de-subscribirme de los eventos complete y volver a repetir el proceso de nuevo.

Es por eso que en el día a día del desarrollo de aplicaciones de Silverlight siempre intento hacer que las cosas sean muy fáciles para mí y que pueda manejar el ciclo de vida de un proxy de una manera muy cómoda. Viendo un poco cuales son las limitaciones que tengo con los proxy de wcf, me encuentro con las que he comentado anteriormente:

  • Creación del proxy con la configuración del binding adecuada.
  • Invocación asíncrona de las operaciones
  • Ciclo de vida del proxy

La primera de las limitaciones se soluciona de manera fácil, con una factoría. Una clase helper o factoría que me permita instancias los proxy de manera cómoda con un sistema centralizado de configuración de bindings y demás opciones. Normalmente en un proyecto de Silverlight casi todos los proxy, por no decir todos, serán del mismo tipo, así que no tenemos que preocuparnos mucho por dar soporte a varios tipos de configuraciones.

public class ProxyFactory
{
    public static T CreateInstance<T, K>(Uri uri)
        where K : class
        where T : ClientBase<K>
    {
        T result = default(T);

        CustomBinding custom = new CustomBinding();
        custom.Elements.Add(new BinaryMessageEncodingBindingElement());
        custom.Elements.Add(new HttpTransportBindingElement()
        {
            MaxReceivedMessageSize = 2147483647,
            MaxBufferSize = 2147483647
        });
        SetDebugTimeouts(custom);

        EndpointAddress address = new EndpointAddress(uri.ToString());

        result = (T)(object)Activator.CreateInstance(typeof(T), custom, address);
        
        return result;
    }

    private static void SetDebugTimeouts(CustomBinding custom)
    {
        if (custom != null)
        {
            TimeSpan timeout = TimeSpan.FromSeconds(60 * 3);
            timeout = TimeSpan.MaxValue;
            custom.CloseTimeout = timeout;
            custom.OpenTimeout = timeout;
            custom.ReceiveTimeout = timeout;
            custom.SendTimeout = timeout;
        }
    }
}

Aquí tenemos el código de la factoría de proxy.

Si nos fijamos acepta dos parámetros genéricos, que son: K es la interfaz del contrato del canal de comunicaciones es decir el contrato de operaciones y T es la clase que hereda de ServiceBase<K>, es decir la clase que implementa el contrato de operaciones en cliente.

Así de esta manera en el cuerpo de la creación podemos crear a mano un custombinding y añadir los elementos que necesitamos, como por ejemplo: BinaryMessageEncondingBindingElement y HttpTransportBindingElement, además de configurar parámetros de esas clases. Creamos también una instancia de la clase EndpointAddress que contiene la dirección url del servicio wcf.

Una vez que tenemos todo esto podemos utilizar la clase Activator para crear una instancia dinámicamente del proxy (a través del tipo de T) pasándoles los parámetros previamente establecidos.

Para consumir el proxy sería de esta manera:

KeyPlayerServiceClient proxy = 
ProxyFactory.CreateInstance<KeyPlayerServiceClient, IKeyPlayerService>(ProxyUrl.KeyPlayerUrl);

Así podemos tener un sistema centralizado de creación de todos los proxies que queramos consumir desde Silverlight.

Manejar el resultado y el ciclo de vida

Pero esta manera de crear las instancias no soluciona el problema que tenemos de manejar el ciclo de vida del proxy, subscribirnos al evento complete de la operación y obtener el resultado de la operación. Es por eso que necesitamos una clase que nos permita invocar una operación asíncrona de un servicio web y que de alguna manera esa clase helper, cree la instancia de la clase, se subscriba al evento de complete, obtenga el resultado de la operación y llame a un delegado que nosotros le hemos especificado. Esto sería lo ideal.

Pues bien podemos hacer todo eso de manera cómoda con esta clase ProxyManager. Antes de centrarnos en cuál es la implementación concreta de la clase y los detalles vamos a ver cómo podemos consumir una operación típica de un servicio de wcf con esta clase ProxyManager.

Si tenemos un servicio con esta operación:

[ServiceContract]
public interface IServiceDemo
{
   [OperationContract]
   DateTime GetDateTime();
}

Tenemos una única operación que se llama GetDateTime y que obtenemos un objeto de tipo DateTime.

Una vez que hemos agregado la referencia al proxy de WCF desde nuestro proyecto de Silverlight, podemos hacer esto:

// manera directa de invocar un servicio web y solo centrarse en la respuesta segun su tipo
ProxyManager<ServiceDemoClient, IServiceDemo> manager = new ProxyManager<ServiceDemoClient, IServiceDemo>(ProxyUrl.DemoService);
// como sabemos que el servicio web devuelve un resultado de tipo DateTime, podemos
// agregar un Action<DateTime> porque DAteTiem es el tipo de devuelve
manager.InvokeOperation<DateTime, GetDateTimeCompletedEventArgs>(
    "GetDateTime",
    new Action<DateTime>(OnGetDateTimeCompleted),
    null);
private void OnGetDateTimeCompleted(DateTime dateTime)
{
}

En este ejemplo fijaros que utilizando la genericidad y la reflexión podemos hacer que la invocación del servicio sea mucho más cómoda, podemos resumirlo a una simple línea de código.

Requisitos tiene la clase ProxyManager

Para crear una instancia de la clase ProxyManager tenemos que pasar los mismos argumentos de tipos que el ProxyFactory, de hecho internamente utiliza el mismo mecanismo, además de la uri de la dirección del servicio web.

Una vez que tenemos el objeto creado tenemos que llamar al único método de la clase ProxyManager, InvokeOperation.

InvokeOperation acepta dos parámetros de tipo que son el tipo que devuelve la operación del servicio web, si nos acordamos era un DateTime, y el otro parámetro genérico es la clase que hereda de AsyncCompletedEventArgs que contiene el resultado de la invocación, en nuestro ejemplo, GetDateTimeCompletedEventArgs. De esta clase es donde el ProxyManager obtendrá el resultado de la operación.

Como parámetros de la función tenemos que suplir el nombre de la operación como un string, en nuestro caso GetDateTime (pero sin Async al final), después un delegado de tipo Action<T> con el resultado, y al final de todos los parámetros un array de tipo object con la palabra params que son los argumentos de la invocación, en nuestro caso null.

Lo bueno que tiene esta clase ProxyManager es que automáticamente por nosotros crea la instancia del proxy, se subscribe al evento complete de la operación que queremos invocar y automáticamente invoca nuestro delegado de tipo Action<T> con el resultado, además de eso cuando termina la invocación del objeto de tipo Action<T> se de-subscribe del evento y cierra el proxy. Toda una gozada.

Además de eso tenemos la posibilidad de utilizar una sobrecarga de la función InvokeOperation que nos permite tener también acceso al objeto de UserState de la invocación por si invocamos más de una vez la operación y necesitamos acceder a un objeto de usuario en la devolución de llamada:

// manera directa de invocar un servicio web y solo centrarse en la respuesta segun su tipo
ProxyManager<ServiceDemoClient, IServiceDemo> manager = new ProxyManager<ServiceDemoClient, IServiceDemo>(ProxyUrl.DemoService);
// como sabemos que el servicio web devuelve un resultado de tipo DateTime, podemos
// agregar un Action<DateTime> porque DAteTiem es el tipo de devuelve
manager.InvokeOperation<DateTime, GetDateTimeCompletedEventArgs>(
    "GetDateTime",
    new Action<DateTime>(OnGetDateTimeCompleted),
    null);

manager.InvokeOperation<DateTime, object, GetDateTimeCompletedEventArgs>(
    "GetDateTime",
    new Action<DateTime, object>(OnGetDateTimeWithArgumentsCompleted),
    null);
private void OnGetDateTimeWithArgumentsCompleted(DateTime result, object userState)
{

}

Con esto podemos hacer que la gestión de los proxies de WCF sea mucho más eficiente que cómoda para el programador que antes.

Codigo fuente completo de la clase ProxyManager:

using System;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.ServiceModel;

namespace ViewModelBinding.Services
{
    public class ProxyManager<TService, KServiceInterface>
        where KServiceInterface : class
        where TService : ClientBase<KServiceInterface>
    {
        public ProxyManager(Uri url)
        {
            proxy = ProxyFactory.CreateInstance<TService, KServiceInterface>(url);
        }

        public void InvokeOperation<TResult, KEventArgs>(string operationName, Action<TResult> action, params object[] args)
            where KEventArgs : AsyncCompletedEventArgs
        {
            if (!string.IsNullOrWhiteSpace(operationName))
            {
                InternalActionOf<KEventArgs, TResult> internalAction = new InternalActionOf<KEventArgs, TResult>(action);
                internalAction.EventInfo = proxy.GetType().GetEvent(string.Format("{0}Completed", operationName));
                internalAction.Delegate = new EventHandler<KEventArgs>(internalAction.OnActionExecuted);
                internalAction.EventInfo.AddEventHandler(proxy, internalAction.Delegate);
                internalAction.Proxy = proxy;

                if (args == null)
                {
                    args = new object[] { };
                }

                MethodInfo[] methods = proxy.GetType().GetMethods();
                string methodName = string.Format("{0}Async", operationName);
                var found = (from p in methods
                             where p.Name == methodName && p.GetParameters().Length == args.Length
                             select p).FirstOrDefault();



                if (found != null)
                {
                    found.Invoke(proxy, args);
                }
            }
        }

        public void InvokeOperation<TResult, TUSerState, KEventArgs>(string operationName, Action<TResult, TUSerState> action, params object[] args)
            where KEventArgs : AsyncCompletedEventArgs
        {
            if (!string.IsNullOrWhiteSpace(operationName))
            {
                InternalActionOf<KEventArgs, TResult, TUSerState> internalAction = new InternalActionOf<KEventArgs, TResult, TUSerState>(action);
                internalAction.EventInfo = proxy.GetType().GetEvent(string.Format("{0}Completed", operationName));
                internalAction.Delegate = new EventHandler<KEventArgs>(internalAction.OnActionExecuted);
                internalAction.EventInfo.AddEventHandler(proxy, internalAction.Delegate);
                internalAction.Proxy = proxy;

                if (args == null)
                {
                    args = new object[] { };
                }

                MethodInfo[] methods = proxy.GetType().GetMethods();
                string methodName = string.Format("{0}Async", operationName);
                var found = (from p in methods
                             where p.Name == methodName && p.GetParameters().Length == args.Length
                             select p).FirstOrDefault();



                if (found != null)
                {
                    found.Invoke(proxy, args);
                }
            }
        }

        private TService proxy;
    }
}
internal class InternalActionOf<EventArgs, TResult, TUserState>
       where EventArgs : AsyncCompletedEventArgs
{
   public InternalActionOf(Action<TResult, TUserState> action)
   {
       this.action = action;
   }

   internal void OnActionExecuted(object args, EventArgs e)
   {
       PropertyInfo property = e.GetType().GetProperty("Result", BindingFlags.Public | BindingFlags.Instance);
       if (property != null)
       {
           object result = property.GetValue(e, null);
           if (result != null)
           {
               if (typeof(TResult).IsAssignableFrom(property.PropertyType))
               {
                   action((TResult)result, (TUserState)e.UserState);
                   EventInfo.RemoveEventHandler(Proxy, Delegate);
               }
           }
       }
   }

   public EventHandler<EventArgs> Delegate { get; set; }
   public EventInfo EventInfo { get; set; }
   public object Proxy { get; set; }

   private Action<TResult, TUserState> action;
}
internal class InternalActionOf<EventArgs, TResult>
            where EventArgs : AsyncCompletedEventArgs
{
    public InternalActionOf(Action<TResult> action)
    {
        this.action = action;
    }

    internal void OnActionExecuted(object args, EventArgs e)
    {
        PropertyInfo property = e.GetType().GetProperty("Result", BindingFlags.Public | BindingFlags.Instance);
        if (property != null)
        {
            object result = property.GetValue(e, null);
            if (result != null)
            {
                if (typeof(TResult).IsAssignableFrom(property.PropertyType))
                {
                    action((TResult)result);
                    EventInfo.RemoveEventHandler(Proxy, Delegate);
                }
            }
        }
    }

    public EventHandler<EventArgs> Delegate { get; set; }
    public EventInfo EventInfo { get; set; }
    public object Proxy { get; set; }

    private Action<TResult> action;
}

Luis Guerrero.

Silverlight 4 Metro Training (7/7) Dashboard de administración de eventos

Hola a todos!

El día 7-8 de abril se celebró una formación de Silverlight 4 del programa Metro de Microsoft que impartí, así que os dejo las grabaciones del evento y el material.

Silverlight 4 Metron Training,

Dashboard de administración de eventos

[sl-media: http://www.luisguerrero.net/Videos/Silverlight4LOB/Sesion07.wmv]

El resto del material:

Código fuente de los Labs aquí.

Saludos.

Luis Guerrero.

Silverlight 4 Metro Training (5/7) Programador de tareas

Hola a todos!

El día 7-8 de abril se celebró una formación de Silverlight 4 del programa Metro de Microsoft que impartí, así que os dejo las grabaciones del evento y el material.

Silverlight 4 Metron Training,

Programador de tareas

[sl-media: http://www.luisguerrero.net/Videos/Silverlight4LOB/Sesion05.wmv]

El resto del material:

Código fuente de los Labs aquí.

Saludos.

Luis Guerrero.

Silverlight 4 Metro Training (6/7) Impresión

Hola a todos!

El día 7-8 de abril se celebró una formación de Silverlight 4 del programa Metro de Microsoft que impartí, así que os dejo las grabaciones del evento y el material.

Silverlight 4 Metron Training,

Impresión

[sl-media: http://www.luisguerrero.net/Videos/Silverlight4LOB/Sesion06.wmv]

El resto del material:

Código fuente de los Labs aquí.

Saludos.

Luis Guerrero.

Silverlight 4 Metro Training (4/7) Perfil de usuario

Hola a todos!

El día 7-8 de abril se celebró una formación de Silverlight 4 del programa Metro de Microsoft que impartí, así que os dejo las grabaciones del evento y el material.

Silverlight 4 Metron Training,

Perfil de usuario

[sl-media: http://www.luisguerrero.net/Videos/Silverlight4LOB/Sesion04.wmv]

El resto del material:

Código fuente de los Labs aquí.

Saludos.

Luis Guerrero.

Silverlight 4 Metro Training (3/7) Registro de usuario

Hola a todos!

El día 7-8 de abril se celebró una formación de Silverlight 4 del programa Metro de Microsoft que impartí, así que os dejo las grabaciones del evento y el material.

Silverlight 4 Metron Training,

Registro de usuario

[sl-media: http://www.luisguerrero.net/Videos/Silverlight4LOB/Sesion03.wmv]

El resto del material:

Código fuente de los Labs aquí.

Saludos.

Luis Guerrero.

Silverlight 4 Metro Training (2/7) Gestor de eventos

Hola a todos!

El día 7-8 de abril se celebró una formación de Silverlight 4 del programa Metro de Microsoft que impartí, así que os dejo las grabaciones del evento y el material.

Silverlight 4 Metron Training,

Gestor de eventos

[sl-media: http://www.luisguerrero.net/Videos/Silverlight4LOB/Sesion02.wmv]

El resto del material:

Código fuente de los Labs aquí.

Saludos.

Luis Guerrero.

Silverlight 4 Metro Training (1/7) Que hay de nuevo en Silverlight 4

Hola a todos!

El día 7-8 de abril se celebró una formación de Silverlight 4 del programa Metro de Microsoft que impartí, así que os dejo las grabaciones del evento y el material.

Silverlight 4 Metron Training,

What’s New in Silverlight 4

[sl-media: http://www.luisguerrero.net/Videos/Silverlight4LOB/Sesion01.wmv]

El resto del material:

Saludos.

Luis Guerrero.

Detectando problemas de memoria con Silverlight

Hola a todos!

Hoy vamos a hablar sobre un memory leak encontrado en Silverlight 4 y reportado en el foro de silverilght.net por el usuario tsheflin. Como sabrás Silverlight está basado en tecnología .net lo que significa que puedes escribir tus aplicaciones en C# o en Visual Basic y tienes todas las cosas buenas de .net como gestión automática de memoria, recolección de elementos no utilizados y seguridad de tipos

Pero algunas veces algo sale más y aun teniendo la gestión automática de memoria de .net nos podemos encontrar con problemas, es por eso que hoy vamos a ver cómo podemos identificar estos problemas con WinDBG y sos para Silverlight.

Voy a usar como ejemplo el codigo subido por el usuario del foro de Silverlight.net. Puedes descargarlo desde aquí.

Lo primero de todo necesitamos ejecutar nuestra aplicación en modo release. Esto es importante porque hay pequeñas diferencias entre el modo debug y el release. Una vez que tenemos la aplicación ejecutándose es tiempo de WinDGB.

Si tu navegador es Windows Internet Explorer 8 y estás ejecutándolo sobre Windows 7 o Vista, sabrás que IE ejecuta cada tab en un proceso separado y por eso primero tienes que identificar cual es el proceso de IE que está ejecutando el código, en mi caso es el proceso 7958.

Con WinDBG abierto (puedes descargarlo de aquí) tienes que ir a File -> Attach to process (F6), y selecciona la id de tu proceso (en mi maquina es 7958), inmediatamente aparecerá en la ventana de salida toda la información básica de depuración. Teniendo en cuenta que nuestra aplicación es una aplicación administrada y WinDBG está diseñado para depurar aplicaciones nativas necesitamos una extensión del depurador para trabajar con aplicaciones administradas. Lo que necesitamos es SOS (Son of Strike) que está localizado en C:\Program Files (x86)\Microsoft Silverlight\4.0.50401.0\sos.dll. Para cada version de silverligh hay una versión de SOS, así que depende de la versión de Silveright que estés depurando tendrás que usar una u otra.

Para cargar SOS escribimos

.load C:\Program Files (x86)\Microsoft Silverlight\4.0.50401.0\sos.dll

Lo siguiente que necesitamos encontrar es una direccion valida del tipo que estamos buscando SilverlightVisualTreeRemovalFail.SilverlightControl1 y para eso tenemos que usar el comando !dumpheap para volcar todos los tipos filtrados por este tipo.

!dumpheap -type SilverlightVisualTreeRemovalFail.SilverlightControl1

0:005> !dumpheap -type SilverlightVisualTreeRemovalFail.SilverlightControl1

Address MT Size

087aad6c 04a45230 92

087ab460 04a45230 92

087ab6f0 04a45230 92

087ac01c 04a45230 92

087ac710 04a45230 92

087ace04 04a45230 92

087ad4f8 04a45230 92

087adbec 04a45230 92

087ae2e0 04a45230 92

087ae9d4 04a45230 92

087af0c8 04a45230 92

087af7bc 04a45230 92

087afeb0 04a45230 92

087b05a4 04a45230 92

087b0c98 04a45230 92

087b138c 04a45230 92

087b1a80 04a45230 92

087b2174 04a45230 92

087b2868 04a45230 92

087b2f5c 04a45230 92

087b3650 04a45230 92

087b3d44 04a45230 92

087b4438 04a45230 92

087b4b2c 04a45230 92

087b5220 04a45230 92

087b5914 04a45230 92

087b6008 04a45230 92

087b66fc 04a45230 92

087b6df0 04a45230 92

087b74e4 04a45230 92

087b7bd8 04a45230 92

087b82cc 04a45230 92

087b89c0 04a45230 92

087b90b4 04a45230 92

087b97a8 04a45230 92

087b9e9c 04a45230 92

087ba590 04a45230 92

087bac84 04a45230 92

087bb378 04a45230 92

087bba6c 04a45230 92

087bc160 04a45230 92

087bc854 04a45230 92

087bcf48 04a45230 92

087bd63c 04a45230 92

087bdd30 04a45230 92

087be424 04a45230 92

087beb18 04a45230 92

087bf20c 04a45230 92

087bf900 04a45230 92

087bfff4 04a45230 92

En la ventana de salida encontramos direcciones del tipo que hemos solicitado, seleccionamos una 087b1a80 y buscamos que objetos están referenciando este objeto (esto es lo que está causando que el objeto no sea recolectado) escribiendo esto:

!gcroot 087b1a80

0:005> !gcroot 087b1a80

Note: Roots found on stacks may be false positives. Run "!help gcroot" for

more info.

Scan Thread 5 OSTHread 10e8

Scan Thread 22 OSTHread 1f8

Scan Thread 23 OSTHread 1588

DOMAIN(07134BE0):HANDLE(Pinned):4a912f8:Root: 09784260(System.Object[])->

08796c78(System.Collections.Generic.Dictionary`2[[System.IntPtr, mscorlib],[System.Object, mscorlib]])->

09788260(System.Collections.Generic.Dictionary`2+Entry[[System.IntPtr, mscorlib],[System.Object, mscorlib]][])->

087b1e90(System.Windows.Controls.ControlTemplate)->

087b1a80(SilverlightVisualTreeRemovalFail.SilverlightControl1)

Encontramos que el objeto (087b1a80) está referenciado por otro objeto y en lo alto de la lista podemos encontrar cual es el objeto que lo está referenciando.

Ahora que hemos identificado que el objeto está referenciado por un GCHandle pineado el recolector siempre encontrará un camino hasta este objeto haciendo que nunca sea recolectado. Como el código que controla esta funcionalidad es de Microsoft no podemos hacer nada al respecto, pero en caso de que sea nuestro lo que podemos hacer es simplemente eliminar las referencias para que el objeto sea recolectado.

Además podemos volcar todos los tipos que hay en el heap y abrirlo con el CLRProfiler usando !traverseheap.

Saludos.

Luis Guerrero.