jueves, mayo 11, 2006

Atributos para Tiempo de Diseño para Dispositivos Móviles

Está es la breve historia de cómo batallamos por la siguiente línea de código:
smartPartPlaceHolder.SmartPart = null;
Sin duda el diseñador de VS es una herramienta súper útil, pero en más de una ocasión (deberás no exagero, pero ahora sólo recuerdo esta), suele hacer cosas extrañas. Muchas veces no es por culpa de VS sino de quien programa los controles, pero al parecer en este caso fue un poco más allá y todo resulto ser problema de un
known bug que será atendido en el SP1 que será liberado el tercer cuarto de este año. En este caso el diseñador escribía la línea anterior cada vez que modificábamos la vista desde VS, por lo que dejaba de funcionar ya que esa propiedad no debía ser null.
Empecemos por quien diseña los controles. Con los atributos en tiempo de diseño tienes realmente mucha flexibilidad, le puedes especificar a una propiedad su valor de default, si quieres que se muestra en la lista de IntelliSense (no veo porque querríamos deshabilitarlo, pero está disponible) y lo más importante (al menos en este caso), le podemos decir si alguna propiedad deberá ser manejada por el diseñador y si no es una propiedad muy simple (por ejemplo un objeto, una Colección de Columnas e.g. DataGrid.Colums) muy seguramente querremos un diseñador personalizado.
El único detalle es que en Mobile no podemos usarlo, o al menos no todos. El CF (compact framework) no los incluye y no lo podemos compilar usando alguna clase del framework normal, sin embargo, VS si podría tomar Assemblys de ambos.
La solución más obvia, aunque no tan sencilla, era la que teníamos en 2003,
compilar un dll similar para el tiempo de diseño. Realmente no era tan sencillo, se resume en seis pasos:
Crear un proyecto de C# de Class Library (no de CF, para escritorio).
Agregamos como links todos los archivos del proyecto de Controles.
Agregamos un atributo de compilación al proyecto creado en el paso 1.
Ponemos en condionantes de compilación los atributos
#If NETDESIGNTIM
[System.ComponentModel.Browsable(false)
#endif
public object SmartPart
{
///
}
Este es un punto clave y no tan sencillo, necesitas agregar el siguiente atributo al AssemblyInfo.cs
#if NETCFDESIGNTIME
[assembly: System.CF.Design.RuntimeAssembly("ElDllQueTieneTodoEnRuntime ")]
#endif
Lo agregamos al GAC para que VS pueda reverenciarlo.

El atributo en el paso 5 le dice que es lo que deberá de usar en tiempo de ejecución, sin embargo, este dll de C# es lo que agregamos a la barra de herramientas y es con el que trabajaremos en VS.

Ok, es medio sencillo como para haber batallado tanto.
Ahora el nuevo modelo es más prometedor (Debería ser así de sencillo como en
este ejemplo). Que es lo que necesitamos otro dll, pero ahora se generará automáticamente por genasm.exe usando los atributos que se especifiquen en el archivo XML. Aunque XML es muy sencillo, su poder reside cuando no lo tenemos que escribir nosotros, es ahí donde entran los diagramas de clases. Desde el diagrama de clases le especificamos los atributos a cada propiedad y este genera el archivo que después es compilado por genasm.exe. Ver el ejemplo para más detalle.
Ok, pareció aún más sencillo, pero no termina aquí, genasm.exe tiene un
know bug aunque nos dan una solución de cómo darle la vuelta mientras lo corrigen, realmente está muy lejos de ser lo que queríamos. El proyecto sobre el cual lo apliques no puede usar Generics, así que tienes que mover tus clases a otros proyectos, heredar de clases generics para hacerlas específicas y usar estás en lugar de las generics y listo (esto último puede ser lo más difícil), esa es la solución que nos da Microsoft.
En este caso no podíamos hacer eso ya que todo el proyecto dependía muchísimo de generics, lo que tuvimos que hacer fue crear otro nuevo proyecto que contuviera exclusivamente lo que requeríamos, remover los pocos generics que quedaron dentro de esa clase y luego simplemente compilar. El único lugar donde el SmartPartPlaceHolder usaba generics era en el EventHandler<>, así que tuvimos que crear el delegado específico y usar ese en lugar del EventHandler genérico.
public delegate void SmartPartShownEventHandler(object o, SmartPartPlaceHolderEventArgs e);
public event SmartPartShownEventHandler SmartPartShown;
en lugar de
public event EventHandler SmartPartShown;
Hablo más sobre generics en
otro post.
Ok, ok, no queríamos tener otra clase con el mismo nombre (SmartPartPlaceHolder) que estuviera en otro nombre y luego cambiarla cuando corrigieran el problema, esto era precisamente lo que
sugería microsoft, aparte todos los EventHandlers tendrían que cambiar. Encontramos una maña:
Simplemente renombramos el dll generado:
Microsoft.Practices.Mobile.CompositeUI.WinForms.SmartPartPlaceHolder.PocketPC.asmmeta.dll
a:
Microsoft.Practices.Mobile.CompositeUI.WinForms.PocketPC.asmmeta.dll
Y copiamos esto al mismo folder donde se encuentra Microsoft.Practices.Mobile.CompositeUI.WinForms.dll
Para hacer automático todo agregamos el siguiente PostBuildEvent
copy /Y /B /V SmartPartPlaceHolder.PocketPC.asmmeta.dll

..\Microsoft.Practices.Mobile.CompositeUI.WinForms.PocketPC.asmmeta.dll

Fue todo, muy simple al final, pero batallamos mientras lo encontramos al igual que más gente que ha tenido el
mismo problema.

No hay comentarios.: