Duda metafísica sobre “contravarianza” en delegates

Hola… hoy voy a poner un post sobre una dudilla metafísica que me ha surgido, concretamente relativa a los delegates. Y he pensado… que mejor sitio que ponerla que aquí??? 😉

Los delegates en C# 2.0 son contravariantes, es decir un delegate a un método que espera un parámetro tipo X aceptará cualquier método que espere un parámetro de cualquier tipo base de X.

Es decir, el siguiente código funciona bien:

delegate void Foo(Derived d);
public class Base { }
public class Derived : Base { }
public class SomeCode
{
    SomeCode()
    {
        Foo foo = new Foo(this.SomeMethod);
    }
    private void SomeMethod(Base b) { }
}

Todos entendemos la lógica que hay tras ello: al ser todos los objetos Derived, objetos Base, es evidente que cualquier método que trate con objetos Base, lo podrá hacer con objetos Derived, y por ello el método SomeMethod es un destino bueno para el delegate Foo.

Ahora bien, imaginemos que tenemos el siguiente código:

delegate void Foo(Derived d);
public class Base { }
public class Derived 
{
    public static implicit operator Base(Derived d) { return new Base(); }
}
public class SomeCode
{
    SomeCode()
    {
        Foo foo = new Foo(this.SomeMethod);
    }
    private void SomeMethod(Base b) { }
}

En este caso el código no compila, el compilador se queja con un No overload for ‘SomeCode.SomeMethod(Base)’ matches delegate ‘Foo’.

La duda metafísica es… creeis que debería compilar? En cierto (lo pongo en cursiva) todos los objetos Derived tambien son objetos Base, puesto que hay definida una conversión implícita de Derived a Base… con lo que podríamos entender que hay una cierta contravarianza.

O creeis que no? Que ya está bien que no compile puesto que el operador de conversión no puede representar nunca una relación is-a y por lo tanto la contravarianza no tiene sentido…

MMmmm… yo reconozco que no estoy 100% posicionado a favor de ninguna opción…

Saludos!

El valor de una certificación.

Bueno, aviso: estoy cabreado… y este post será polémico. Quien avisa no es traidor.

Estas últimas semanas me he sacado dos MCPs, en concreto el 70-529 y el 70-549, es decir el MCTS y el MCPD de aplicaciones distribuídas. No voy a hablar sobre si son fáciles o difíciles o si se ajustan a lo que realmente uno se encuentra en el mundo real, no… quiero exponer algo que ya hace tiempo me preocupa y me mosquea a partes iguales. Antes de nada he de decir que no tenía muy claro si escribir este post. Por hastío básicamente… lo digo porque lo que escribiré ahora es, lamentablemente, muy parecido a lo que escribí en mi antiguo blog en la casi-extinta clearscreen (un tick de silencio). Aquí dejo el enlace al post que escribí hará casi tres años, por si a alguien le apetece leerlo.

Antes de hacer ambos exámenes, eché una ojeada a esos pdfs que tienen preguntas parecidas a las del exámen, junto con sus respuestas (¿no hace falta poner nombres, verdad?). Lo que vi fue descorazonador… bueno, lo hubiese sido si no supiera de que va el tema, vamos… ahora ya empieza a resbalarme todo…

El caso del 70-529 es flagrante: todas (y todas significa todas) las preguntas exámen estaban en el pdf. Además éste estaba bastante bien (había algunas preguntas que creo que estaban mal y algunas otras que estaban mal seguro) o sea que alguien simplemente empollándose el pdf se sacaba perfectamente el exámen.

El caso del 70-549 no fue tan brutal. De las 60 preguntas que tenia el exámen había unas 45 que estaban en el pdf (ya veis, aunque no tan brutal sigue siendo lamentable). Eso sí, al menos el pdf del 70-549 estaba un poco peor (tenia más respuestas incorrectas)… aun así, empollándoselo uno aprueba el exámen.

No quiero ni voy a criticar a quien se empolla estos pdfs para sacarse MCPs… quiero criticar a quien permite explícitamente que esto siga así. A quien en mi opinión es el máximo culpable (aunque sea por inacción). Es decir, a Microsoft. Creía que la certificación servía para acreditar unos determinados conocimientos, pero veo que simplemente es otra patética herramienta para hacer dinero. Da igual que la certificación esté tan prostituída que los exámenes se encuentren en internet, mientras la gente siga pagando para hacerlos (donde digo gente también se puede leer empresas que quieran ser pon-aquí-tu-material-preferido certified partner). Anda que no sería fácil evitar estos pdfs teniendo un pool de centenares de preguntas por exámen en lugar de menos de 80 (el pdf del 70-529 tenia menos de 80 preguntas y repito: todas me salieron en el exámen). Y también ir cambiando las preguntas cada cierto tiempo ayudaría. ¿Cuanto le costaría hacer todo esto a Microsoft? ¿Por que no lo hace? Y más preguntas… ¿cómo es posible que determinadas academias ofrezcan un cursillo de pongamos 50 horas tras el cual garanticen tener MCPD? ¿No se suponía que estas certificaciones eran para gente con determinada experiencia? ¿50 horas en un cursillo es esta experiencia?

De ahí el título del post: ¿cuál es el valor de una certificación cuyos exámenes se encuentran (con respuestas incluídas) en internet? ¿Porque debo dedicar esfuerzo y dinero (esos exámenes no son gratis) en sacarme estas certificaciones? ¿Debo seguir jugando a este juego? ¿Hará algo al respecto Microsoft? (esta última pregunta es retórica, lo aclaro por si acaso).

¿Alguna respuesta convincente?

[Depuración] Generación programática de mini dumps

Bueno… No voy a hablar sobre como fue el DevCamp de este pasado fin de semana en puesto que ya lo han hecho Jordi Nuñez y José Manuel Alarcon. Compartimos buenos momentos, buenos cubatas y buenas charlas.

De todas las charlas que se dieron, me interesa comentar especialmente la que dio Pablo Alvarez, una charla impresionante bajo el título de depurando hasta la saciedad. Aunque contó con apenas 45 minutos destripó a base de bien WinDbg y comentó los distintos escenarios de depuración, especialmente la depuración post-mortem (esto es, cuando el proceso ya ha reventado). Leeros su post y el ppt porque es imprescindible.

¿Y qué quiero comentar yo de la charla de Pablo? Pues bien, en la charla quedó claro que antes de meterse a depurar uno debe tener unos buenos… símbolos. Y no sólo eso sino que evidentemente es necesario tener el dump de la aplicación que ha reventado. Comentó distintas herramientas para generar este dump, pero a mí me interesa comentar un escenario que suele ser muy habitual cuando ponemos aplicaciones en producción: que sea la propia aplicación quien genere este dump cuando se produce un error. En un proyecto donde estoy trabajando tenemos nuestra aplicación desplegada en distintos clientes, y aunque simplemente con el log de excepciones managed cubrimos la mayoría de errores, en algunos casos necesitamos más información. Es ahí donde tener un dump de la aplicación nos es realmente útil.

Bueno… vamos a ver dos maneras de generar un dump para depuración post-mortem. La fácil y la no tan fácil.

La fácil

Os vais a la página de ClrDump y os lo descargáis. ClrDump es básicamente una DLL que podéis llamar desde vuestras aplicaciones .NET para generar dumps. En la propia página encontrareis la información de los métodos y sus firmas para P/Invoke.

P.ej. el siguiente código genera un dump completo cuando se produzca cualquier excepción (managed o nativa) no capturada:

SetFilterOptions(CLRDMP_OPT_CALLDEFAULTHANDLER);

string minidumpFile = string.Format(@”c:temppostmortem-{0}.dmp”, DateTime.Now.ToString(“dd-MM-yyyy”));

RegisterFilter(minidumpFile, MiniDumpWithFullMemory);

 

Las constantes y las firmas P/Invoke serian:

[DllImport(“clrdump.dll”, CharSet = CharSet.Unicode, SetLastError = true)]

private
static
extern
int RegisterFilter(string FileName, int DumpType);

[DllImport(“clrdump.dll”)]

static
extern
Int32 SetFilterOptions(int Options);

private
const
int CLRDMP_OPT_CALLDEFAULTHANDLER = 0x1;

private
const
int MiniDumpWithFullMemory = 0x2;

 

Cuando vuestra aplicación reviente tendréis vuestro minidump (en este caso será bastante grande puesto que volcará toda la memoria del proceso lo que puede dar lugar con facilidad a dumps de más de 100 ó 200 MB).

La no tan fácil

Bueno… ClrDump funciona realmente bien y no se me ocurre ninguna razón para no usarlo, excepto que os guste investigar un poco como funciona la generación de dumps… Si queréis podéis implementaros vuestra propia librería para generar dumps. Para ello nada mejor que C++, la librería dbghelp.dll y para adelante!

Dbghelp.dll es la librería que contiene, entre otras, las funciones de generación de dumps. Aunque Windows tiene una, lo mejor es usar la que viene con las Debugging Tools for Windows. Dado que es una librería unmanaged os recomiendo el uso de C++ (supongo que puede invocarse via p/invoke pero ni me lo he planteado, algunas funciones tienen firmas un poco complejas).

La función clave se llama MiniDumpWriteDump y si veis su información en la msdn podréis comprobar que tiene bastantes parámetros. Esta función es la que hace todo el trabajo de generar un minidump, ahora sólo nos queda saber cuándo llamarla: cuando se produzca cualquier excepción (managed o nativa) no controlada. Para ello debemos usar el método SetUnhandledExceptionFilter. Este método espera un puntero a una función que será la que deba ejecutarse cuando se produzca cualquier excepción no controlada. Es en este método cuando llamaremos a MiniDumpWriteDump.

El método SetUnhandledExceptionFilter espera un puntero a una función que reciba un LPEXCEPTION_POINTERS, eso es un puntero con los datos de la excepción que ha ocurrido. Esos datos se los debermos pasar a MiniDumpWriteDump para que pueda procesar la información.

A modo de ejemplo, he creado una clase DumpHelper, en C++ CLI que permite generar minidumps cuando se produzca una excepción no controlada. La cabecera sería así:

namespace DumpHelper {

 

    delegate LONG UnhandledExceptionFilterHandler(LPEXCEPTION_POINTERS pException);

 

    public
ref
class DumpHelper

    {

        private:

            UnhandledExceptionFilterHandler^ pManagedHandler;

            LPTOP_LEVEL_EXCEPTION_FILTER pFilter;

            LONG UnhandledExceptionFilter(LPEXCEPTION_POINTERS pException);

            void CreateMiniDump( EXCEPTION_POINTERS* pep );

            String^ name;

        public:

            BOOL SetExceptionHandler ();

            DumpHelper(String^ name);

    };

}

 

Defino un delegate (que contendrá el puntero a función) y simplemente dos funciones públicas: el constructor que espera una cadena (con el nombre de fichero a generar) y el método SetExceptionHandler para activar la generación de minidumps cuando el proceso reviente.

En la parte privada, tenemos la instancia del delegate (pManagedHandler), el puntero a función (pFilter), y dos funciones adicionales: UnhandledExceptionFilter y CreateMiniDump.

La implementación del método SetExceptionHandler es tal como sigue:

BOOL DumpHelper::SetExceptionHandler()

{

    pManagedHandler = gcnew UnhandledExceptionFilterHandler(this, &DumpHelper::UnhandledExceptionFilter);

    pFilter = reinterpret_cast<LPTOP_LEVEL_EXCEPTION_FILTER>(

        Marshal::GetFunctionPointerForDelegate (pManagedHandler).ToPointer());

    SetUnhandledExceptionFilter(pFilter);

    return TRUE;

}

 

Creamos el puntero pFilter para que apunte a la función UnhandledExceptionFilter del propio objeto DumpHelper. Para ello tenemos que hacerlo usando un delegate y conviertiendo luego el delegate a un puntero nativo usando la clase Marshal. Una vez tenemos el puntero, lo pasamos como parámetro al método de Windows SetUnhandledExceptionFilter. En este punto cuando se produzca una excepción no controlada en nuestro programa se nos llamará al método UnhandledExceptionFilter de nuestra clase DumpHelper. Este método es muy simplemente y simplemente llama a CreateMiniDump:

LONG DumpHelper::UnhandledExceptionFilter(LPEXCEPTION_POINTERS pException)

{

    CreateMiniDump(pException);           

// 0: Que se llame al manejador por defecto de Windows al finalizar

    return 0;

}

 

Finalmente el método CreateMiniDump es el que realiza todo el trabajo. Para ello llama a MiniDumpWriteDump:

void DumpHelper::CreateMiniDump( EXCEPTION_POINTERS* pep)

{


pin_ptr<const
wchar_t> fname = PtrToStringChars(name);

    HANDLE hFile = CreateFile(fname, GENERIC_READ | GENERIC_WRITE,

        FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);       

    if( ( hFile != NULL ) && ( hFile != INVALID_HANDLE_VALUE ) )

    {   

        MINIDUMP_EXCEPTION_INFORMATION mdei;

        mdei.ThreadId = GetCurrentThreadId();

        mdei.ExceptionPointers = pep;

        mdei.ClientPointers = FALSE;

        MINIDUMP_TYPE mdt = (MINIDUMP_TYPE)(MiniDumpWithFullMemory |

                                                 MiniDumpWithFullMemoryInfo |

                                                 MiniDumpWithHandleData |

                                                 MiniDumpWithThreadInfo |

                                                 MiniDumpWithUnloadedModules );

        BOOL rv = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),

            hFile, mdt, (pep != NULL) ? &mdei : NULL , NULL, NULL );

        CloseHandle( hFile );

    }
}

 

La función simplemente abre un fichero (usando CreateFile) y luego rellena los distintos parámetros de MiniDumpWriteDump para generar el minidump.

El uso de esta librería es muy simple. En vuestra aplicación simplemente añadís una referencia a ella, creáis un objeto DumpHelper y llamáis acto seguido a SetExceptionHandler. El mejor sitio para hacer esto es en vuestro método Main:

class Program

{

static void Main(string[] args)

{

DumpHelper h = new DumpHelper(“C:\temp\test.dmp”);

h.SetExceptionHandler();

// Resto de código…

}

}

 

Listos! Con esto vuestras aplicaciones ya generarán dumps cuando revienten!

Un saludo… y nos vamos viendo!

IoC o el poder de ceder el control

Hablando con colegas de profesión, me he dado cuenta de que muchos de ellos no terminan de comprender el patrón IoC o las ventajas que su uso comporta… Así que sin ánimo de sentar cátedra he decidido escribir este post, por si le sirve a alguien… J

IoC, que corresponde a las siglas de Inversion Of Control, agrupa a varios patrones que tienen en común que el flujo de ejecución del programa se invierte respecto a los métodos de programación tradicionales (wikipedia dixit). Generalmente el programador especifica las acciones (métodos) que se van llamando, pero en IoC lo que se especifican son las respuestas a determinados eventos o sucesos, dejando en manos de un elemento externo todas las acciones de control necesarias cuando lleguen estos sucesos.

Un claro ejemplo de IoC se encuentra en el modelo de eventos de Windows Forms. Cuando vinculamos una función gestora a un evento (p.ej. el Click de un Button), estamos definiendo la respuesta a un determinado evento y nos despreocupamos cuando se llamará a nuestra función gestora. Es decir, cedemos el control del flujo al framework para que sea él que llame cuando sea necesario a nuestro método.

Una de las principales ventajas de usar patrones IoC es que reducimos el acople entre una clase y las clases de las cuales depende, y eso cuando queremos utilizar por ejemplo tests unitarios es muy importante (no seré yo quien hable ahora de las ventajas de TDD cuando ya lo ha hecho Rodrigo (http://geeks.ms/blogs/rcorral/archive/2006/12/02/beneficios-y-carater-iacute-sticas-de-un-buen-test-unitario.aspx)). De los distintos patrones IoC me interesa comentar específicamente dos: Service Locator y cómo podemos usarlo en .NET. Para más adelante dejo Dependency Injection, otra forma de IoC que también es extremadamente útil.

Id a la nevera, coged una Voll-Damm o cualquier otra cervecilla y sentaos que este post es un poco largo… J

Service Locator

El objetivo de Service Locator es reducir las dependencias que tenga una clase. Imaginemos una clase, que depende de otras dos clases (p.ej. ServicioSeguridad y ServicioLogin). Podríamos tener un código tal como:

class
Client

{

static
void Main(string[] args)

{

new
Client().Run();

}

private
void Run()

{

// En este punto necesitamos objetos de las clases ServicioSeguridad y ServicioLogger


ServicioSeguridad ss = new
ServicioSeguridad();


ServicioLogger sl = new
ServicioLogger();


//…

}

}

En este punto tenemos un fuerte acople entre la clase Client y las clases ServicioSeguridad y ServicioLoggger. Seguiríamos teniendo este mismo acople incluso aunque utilizáramos interfaces porque deberíamos hacer el “new”:

IServicioSeguridad ss = new
ServicioSeguridad();

IServicioLogger sl = new
ServicioLogger();

 

Las principales desventajas de esta situación son:

  1. Las clases que implementan las dependencias (en nuestro caso ServicioSeguridad y ServicioLogger) deben estar disponibles en tiempo de compilación (no nos basta con tener solo las interfaces).
  2. El fuerte acople de la clase con sus dependencias dificulta de sobremanera su testing. Si queremos utilizar Mocks para de ServicioSeguridad o ServicioLogger vamos a tener dificultades

El patrón de ServiceLocator soluciona estos dos puntos, sustituyendo las dependencias de la clase Client por dependencias a los interfaces y a un elemento externo, que llamaremos Contenedor encargado de devolver las referencias que se le piden.

Cuando la clase necesita un objeto en concreto, lo pide al contenedor:

IServicioSeguridad ss = container.Resolve<IServicioSeguridad>(“servicioseguridad”);

IServicioLogger sl = container.Resolve<IServicioLogger>(“serviciologger”);

El método Resolve devolvería una referencia del tipo especificado en el parámetro genérico de acuerdo con un identificador. Evidentemente falta alguien que cree el contenedor y que agregue los servicios a él. Es decir, en algún sitio habrá algo como:

container = new
IoCContainer();

container.Add<IServicioSeguridad, ServicioSeguridad>(new
ServicioSeguridad(), “servicioseguridad”);

container.Add<IServicioLogger, ServicioLogger>(new
ServicioLogger(), “serviciologger”);

La gran diferencia es que esto no tiene porque estar en la clase Client. Simplemente pasándole a la clase Client una referencia al contenedor, eliminamos todas las dependencias de la clase Client con las clases que implementan los servicios. Y donde creamos el contendor? Pues depende… si estamos en nuestra aplicación, lo podemos crear en el método que inicialice la aplicación, pero si queremos probar la clase Client con tests unitarios podemos crear el contenedor en la inicialización del test…. Y lo que es mejor: rellenarlo con Mocks de los servicios!

Así, nuestro programa podría tener una clase Bootstrapper que crea el contenedor de IoC y lo inicializa con los objetos necesarios:

class
Bootstrapper

{

static
void Main(string[] args)

{

IIoCContainer container = new
IoCContainer();

container.Add<IServicioSeguridad, ServicioSeguridad>(new
ServicioSeguridad(), “servicioseguridad”);

container.Add<IServicioLogger, ServicioLogger>(new
ServicioLogger(), “serviciologger”);


new
Client(container).Run();

}

}

Pero si queremos usar tests unitarios de la clase Client, podemos cambiar los objetos por Mocks fácilmente:

[ClassInitialize]

public
static
void Init(TestContext context)

{

container = new
IoCContainer();

container.Add<IServicioSeguridad, ServicioSeguridadMock>(new
ServicioSeguridadMock(), “servicioseguridad”);

container.Add<IServicioLogger, ServicioLoggerMock>(new
ServicioLoggerMock(), “serviciologger”);

}

[TestMethod]

public
void TestMethod1()

{

Client c = new
Client(container);

c.Run(); // Este método Run usará los Mocks!

}

 

Fijaos que podemos lanzar tests unitarios sobre la clase Client, sin necesidad alguna de cambiar su código y utilizando Mocks. Además, la clase Client no tiene ninguna dependencia con las implementaciones de los servicios que utiliza, así que no es necesario ni que existan para poder crear la clase Client (sólo necesitamos las interfaces).

Unity

Aunque existen varios contenedores IoC open source para .NET, Microsoft tiene el suyo, también open source, llamado Unity (http://www.codeplex.com/unity/). Unity proporciona soporte para los patrones Service Locator y Dependency Injection.

Para que veais un poco su uso he dejado un proyecto de herramienta de línea de comandos para listar los ficheros de un directorio (vamos un dir, jejejee…. J).

La solución está dividida en varios proyectos:

  1. DemoIoC: Contiene el Bootstrapper, la clase que inicializa el contenedor de Unity, agrega la clase llamada ServicioFicheros y utiliza un objeto Cliente para realizar las acciones de la línea de comandos.
  2. Cliente: Contiene la implementación de la clase Cliente.
  3. Interfaces: Contiene las interfaces del servicio (en este caso sólo una)
  4. Implementations: Contiene las implementaciones del servicio (en este caso sólo una)
  5. Mocks: Contiene las implementaciones de los Mocks (en este caso sólo una)
  6. UnitTests: Contiene tests sobre la clase Cliente, usando un Mock del servicio ServicioFicheros.

El ejecutable (proyecto DemoIoC, assembly DemoIoC.exe) es un archivo de línea de comandos que acepta dos parámetros:

DemoIoC –l para listar todos los ficheros (del directorio actual)

DemoIoC –e:fichero.ext Indica si el fichero “fichero.ext” existe (en el directorio actual).

Si lo ejecutáis veréis que NO funciona bien: lista los ficheros correctamente, pero devuelve que un fichero existe cuando no es cierto y viceversa. Si miráis el código de la clase Cliente, encontrareis el error, ya que es obvio, pero lo bueno es que el proyecto UnitTest, testea este método de la clase Cliente, usando un Mock del ServicioFicheros y el UnitTest detecta el error. Observad lo fácil que ha sido sustituir el ServicioFicheros por un Mock sin alterar la clase Cliente, gracias al uso del patrón Service Locator!

PD: Estooooo… que me he dejado de adjuntar el código de ejemplo… Lo teneis aquí!!!

Saludos!!!

Referencias

Os dejo algunas referencias para su lectura por si os interesa profundizar un poco en el tema tratado:

[ALM 08] El efecto mariposa en el desarrollo del Software…

… o como la falta de comunicación es una de las principales causas del fracaso de muchos proyectos.

Pero un momento, que me embalo. Esta es la charla que dieron en las ALM Sessions 08, mis compañeros en raona, Jorge Ramo y Juan Carlos Viñas (bueno, el título “oficial” era: Integración entre EPM y TFS). En la charla comentaron las principales causas del fracaso de muchos proyectos haciendo especial incapié en la falta de comunicación. Luego analizaron distintos modelos de comunicación empresarial entre los distintos departamentos con responsabilidad en un proyecto (como dirección, marketing y técnico). Y para finalizar explicaron las herramientas que Microsoft ofrece para ayudar a mantener un mayor control y mejorar la comunicación en la gestión de proyectos. Así comentaron como Project Portfolio Server, Project Server y TFS ayudan a mejorar la comunicación y el seguimiento del conjunto de proyectos de una empresa, permitiendo la toma de decisiones basandose en informaciones concretas y obtenidas en tiempo real.

Aquí teneis la presentación y os recuerdo que como todas las sesiones de este ALM’08 estará grabada y se podrá ver “en diferido”.

Y para que veais un poquillo el ambiente que se respiraba, os dejo un par de fotillos…

Por un lado a Jorge Ramo presentando la charla:

José Luis Montes en el ALM08

y por otro una vista general de la sala:

 Vista general de la presentación de raona en el ALM'08

Ah! Sí!!! Os recuerdo que, como es habitual en este ALM’08, habrá un workshop (en Madrid y en Barcelona) donde se profundizará con más detalle en todos los temas tratados en la charla!!

Saludos!

[WPF] Library project file cannot specify ApplicationDefinition element

Imagina la siguiente situación: Tienes un proyecto en WPF, con varias ventanas o controles WPF creados, y de repente te da por reorganizarlo todo un poco. Así, que añades un proyecto de tipo “Class Library” a la solución, y luego arrastras desde el Solution Explorer, algunas de las ventanas y/o controles al nuevo proyecto.

Cuando más o menos lo tienes todo, le das a compilar y Visual Studio se queja con dos errores:

  • error MC1002: Library project file cannot specify ApplicationDefinition element.
  • error BG1003: The project file contains a property value that is not valid.

Además aunque le des doble-click en la ventana de errores, Visual Studio no está dispuesto a decirte en que línea o al menos que fichero es el causante de los dos errores.

El error se produce cuando al arrastrar los controles xaml al nuevo proyecto, Visual Studio cambia la “Build Action” de los controles que hayas arrastrado de “Page” a “ApplicationDefinition”, y una librería no puede tener ningún control o ventana xaml con “ApplicationDefinition”. Así pues, seleccionas en el “Solution Explorer” los ficheros xaml que hayas arrastrado (si arrastras más de un archivo te los cambia todos) y en propiedades, pones “Build Action” a “Page”… y listos!

Saludos!

PD: El fichero que tiene la Build Action como “ApplicationDefinition” es aquel que proporciona el punto de entrada de la aplicación y por lo tanto solo es válido en ejecutables (suele ser el App.xaml).

El Guille en Igualada

Bueno… ya pasó El Guille por Igualada, en la sexta etapa de la Guille Community Tour.

La verdad  es que para un grupo de usuarios como CatDotNet fue un honor que un ponente de su categoría, se acercara a hacernos una visita, y deleitarnos a todos con su magnífica presentación sobre las novedades que trae Visual Basic 2008 (o VB9), amenizada con numerosas bromas y simpáticos ejemplos. Nos contó la anécdota de “MiAmiguito” con el fondo rosa que menciona Jose Luis Latorre, pero nuestro proyector funcionaba bien, así que no pudimos verla, jejejeee… 😛

Como no podía ser menos batimos todos los récords de asistencia, con unas 35 personas más o menos, y El Guille demostró porque es uno de los mejores ponentes de toda España.

En fin, fue una magnífica presentación (como no podía ser menos)… solo aprovechar para decir que si hay alguien de Igualada o cerquita, o simplemente interesado en saber qué movidas vamos organizando, que se ponga en contacto conmigo o con Jose Luis Torres y vamos hablando!!!!!

Nos vemos!!!!

PD: Uuuuups…. no tengo las fotos a mano ahora, pero tan buen punto las tenga, colgaré algunas para que podais verlas 😉

[WPF] ItemsControl y Canvas

Comentemos la jugada: Tenemos un ItemsControl (o un derivado de él como una ListBox) y queremos posicionar sus elementos dentro de un Canvas.

La definición del ItemsControl puede ser algo parecido a:

<ItemsControl Margin=”5″ Name=”battleField”>

    <ItemsControl.Template>

        <ControlTemplate TargetType=”ItemsControl”>

            <Border BorderBrush=”Aqua” BorderThickness=”1″ CornerRadius=”15″>

                <ItemsPresenter />

            </Border>

        </ControlTemplate>

    </ItemsControl.Template>

    <ItemsControl.ItemsPanel>

        <ItemsPanelTemplate>                      

            <Canvas Width=”500″ Height=”300″ Background=”Yellow”></Canvas>

        </ItemsPanelTemplate>

    </ItemsControl.ItemsPanel>

</ItemsControl>

 Hasta aquí nada especial:

  • Definimos el ControlTemplate para definir que forma tendrá todo el control en sí. En este caso un borde redondeado de un color azul tirando a cyan…
  • Definimos el ItemsPanel, que nos permite especificar que panel usará el control para posicionar sus elementos. En este caso queremos posicionar los elementos en coordenadas X e Y absolutas, por lo que ponemos un Canvas de un amarillo chillón que echa para atrás.

Ahora especificamos el ItemTemplate, que indica como renderizar cada uno de los elementos que contiene el control:

 

<ItemsControl.ItemTemplate >

    <DataTemplate>

        <Button Width=”60″ Height=”25″ Content=”{Binding Path=UnitName}”

               Canvas.Left=”{Binding Path=PosX}” Canvas.Top=”{Binding Path=PosY}”>

            <Button.RenderTransform>

                <SkewTransform AngleX=”-45″ AngleY=”0″ />

            </Button.RenderTransform>

        </Button>

    </DataTemplate>

</ItemsControl.ItemTemplate>

En este caso especificamos que cada elemento que se añada, se renderizará con un botón, al cual se le aplicará una transformacion (una SkewTransform en este caso). El contenido del botón será el valor de la propiedad “UnitName” y, aquí viene lo bueno, posicionaremos el botón donde indiquen los valores de las propiedades PosX y PosY.

En el code-behind tenemos el código para rellenar nuestro ItemsControl. Simplemente le añadimos objetos de cualquier clase que tenga las propiedades necesarias (una llamada UnitName, otra llamada PosX y otra llamada PosY).

 

for (int row = 1; row <= 9; row++)

{

    for (int col = 1; col <= 9; col++)

    {

        CasellaInfo ci = new CasellaInfo();

        ci.UnitName = string.Format(“{0}-{1}”, row, col);

        ci.PosX = col * 40;

        ci.PosY = row * 20;

        battleField.Items.Add(ci);

    }

}

 

battleField.Items.Refresh();

Siendo la clase “CasellaInfo” algo parecido a: 

class CasellaInfo

{

    public string UnitName { get; set; }

    public int PosX { get; set; }

    public int PosY { get; set; }

}

Ok! Todo listo! Ejecutamos y…

Aparecen los 81 botones… uno encima del otro, en las coordenadas 0,0 del Canvas (bueno, supongo que aparecen los 81 porque claro, yo sólo veo el último :P). Es decir, los valores de Canvas.Top y Canvas.Left son ignorados.

¿Entonces? Bueno, el truco está en que NO debemos aplicar los valores de Canvas.Top y Canvas.Left al ItemTemplate. En un ItemsControl, cada uno de los elementos (items) se coloca dentro de un control que se genera por cada elemento. Por defecto en un ItemsControl este control es del tipo ContentPresenter. El ItemsControl proporciona una propiedad, llamada ItemContainerStyle que permite especificar un estilo a aplicar a cada uno de esos contenedores:

 

<ItemsControl.ItemContainerStyle>

    <Style TargetType=”{x:Type ContentPresenter}”>                       

        <Setter Property=”Canvas.Left” Value=”{Binding Path=PosX}”/>

        <Setter Property=”Canvas.Top” Value=”{Binding Path=PosY}”/>

    </Style>

</ItemsControl.ItemContainerStyle>

Ahora sí! El ItemsControl, por cada elemento que tenga, creará un ContentPresenter, añadirá dentro de él todos los controles que hayamos especificado en el ItemTemplate (en nuestro caso simplemente un botón) y añadirá este ContentPresenter al ItemsPanel (que en nuestro caso era el Canvas). Además aplicará el estilo especificado en ItemContainerStyle a cada uno de los ContentPresenter creados, lo que los posicionará en las coordenadas que nosotros queremos.

Una vez visto esto está claro que son los ContentPresenter generados lo que tenemos que posicionar en el canvas (y no los ItemTemplate) pero hasta que uno no se da cuenta…. buf!!!

Saludos! 😉

VS se queja con un "Could not retrieve the current project" en un archivo .dbml

Síntoma: tienes una solución que ayer (o hace algunos días, da igual) funcionaba y compilaba bien. Hoy lo abres y aparece un error que dice:

Build failed due to validation errors in C:edu.tmpWofClientServerWoFServerWoFData.dbml.  Open the file and resolve the issues in the Error List, then try rebuilding the project.

Recompilar la solución no sirve para nada. Entonces si intentas abrir el archivo .dmbl, VS se queja con el “Could not retrieve the current project”.

La solución? Invocar VS desde una línea de comandos con:

devenv /ResetSkipPkgs  y listos, todo volverá a funcionar!

La causa de esto es que en alguna carga previa de VS, el paquete (en este caso el diseñador de LINQ) no se carga bien por alguna razón y VS “lo desactiva” para el futuro. Aunque VS avisa de ello (aparece un warning en la ventana de output) es fácil no verlo, o incluso si es un paquete que usamos raramente, no acordarnos.

Evidentemente, en mi caso, el problema se dió con el diseñador de LINQ, pero se puede dar con cualquier paquete de VS que haya tenido algún error de carga.

Nos leemos 😉

Más info sobre ResetSkipPkgs: http://msdn.microsoft.com/en-us/library/ms241276.aspx

Acropolis, VS2008 SP1 y el diseñador de WPF parece que no se entienden

MMmm… pues eso 🙂

Los síntomas eran los siguientes: En un proyecto WPF, al cargar un archivo xaml, el diseñador se quejaba con el mensaje: “Index was out of range: Must be non-negative  and less than the size of the collection”. Luego daba un número de línea y posición que no decían nada en absoluto.

El proyecto compilaba y se ejecutaba correctamente, simplemente el diseñador se negaba a mostrar la clase. He de decir que yo había cargado antes este proyecto, sin ningún problema!

Tras intentar entender que podía estar pasando, deducí que el error estaba en el ItemTemplate de un ListBox que había en el XAML. Bueno… deducí esto básicamente porque VS2008 me lo subrayaba todo en azul claro 😛

En concreto había varias lineas que parecian no gustarle a VS2008, p.ej:

 

<Border Style=”{StaticResource RacePitBorderStyle}”>

Siendo  RacePitBorderStyle un estilo definido usando <Style> en el propio XAML (quito el código dentro de <Style> para hacerlo más claro).

    <Control.Resources>

        <Style x:Key=”RacePitBorderStyle” TargetType=”Border”>

        </Style>

        <wofconverters:RetratSourceConverter x:Key=”RetratSourceConverter”/>

    </Control.Resources>

Lo primero que observé, fue que si modificaba el “StaticResource” del Border para usar DynamicResource, este error desaparecía (ni idea de por que), pero luego me daba otro en la línea:

 

<Image Source=”{Binding Path=Retrat,Converter={StaticResource RetratSourceConverter}}” Width=”32″ Height=”32″></Image>

Ahora se quejaba de un error de casting (sorry, no tengo el mensaje exacto).

Ya con la mosca tras la oreja, me puse a googlear y por suerte encontré a alguien a quien le pasaba lo mismo. El Comentaba que el error se lo ocasionaba el tener instalado Acropolis… tal y como yo lo tenía!

Así pues desinstalé Acropolis, y ole! El diseñador ya cargaba de nuevo mi proyecto (incluso usando el StaticResource, tal y como estaba antes).

En fin… 😉

Saludos!

PD: Y SP1 que pinta aquí? Seguramente nada, aunque juraría que antes, con VS2008 sin SP1 y con Acropolis el proyecto me funcionaba… pero hacía bastantes días que no cargaba este proyecto y no lo puedo asegurar. En todo caso, con SP1 sin Acropolis funciona bien 🙂