ComboBoxes editables y Bindings

Si tienes un ComboBox que es editable y quieres que tenga algún tipo de Binding con un ViewModel, hazte a la cuenta de que lo que debes enlazar es la propiedad Text, porque si no, se te va a ir de madre todo. El valor que seguramente quieras es precisamente eso, lo que queda en la propiedad Text. Luego si hace falta le aplicas un Converter o lo que sea. Si usas el SelectedValue y el valor escrito no coincide, te van a dar errores muy chungos de validación y el combo se te pondrá en rojo con mensajitos del tipo:

Value ” could not be converted

Así es que ya sabes, ¡cuidado con los ComboBoxes, que los carga el diablo!

Leyendo imágenes desde streams

Imagina que te da por leer un array de bytes que sabes que es un PNG. Vale, estupendo. Ahora pones todo lo que sabes sobre la mesa y dices “esto está tirado, tío”. Que sí, que me lo creo, pero hay una cosa que fijo no sabes si cuando decodificas la imagen la estás leyendo desde un flujo:

Si el decoder de la imagen lo creas con la opción de BitmapCacheOption Default, la carga de la imagen será “lazy”, o sea, perezosa. No se cargará inmediatamente y si, como yo, eres un programador que suele cerrar los flujos cuando ya no los necesita (stream.Close o con un bloque “using”), en el momento en que va a cargar la imagen porque la necesita, el flujo está cerrado y la imagen no carga ni a la de 3.

En el ejemplo está bien claro, ¡majuelo!

 public static ImageSource FromArray(byte[] bytes)
        {
            using (var memoryStream = new MemoryStream(bytes))
            {
                var decoder = new PngBitmapDecoder(memoryStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
                BitmapSource bitmapFrame = decoder.Frames[0];                            
                return bitmapFrame;
            }
        }

Observa el BitmapCacheOption.OnLoad Winking smile Ahí flipamos en 16,7 millones de colores. NECESARIO SI EL STREAM SE CIERRAAAAAAAAA.

Tentaciones: llamar a this() y base() al mismo tiempo

Como estás muy loco, a veces se te pasa por la cabeza hacer historias descabelladas. Llamar a this() y a base() no se puede, jodío. Aquí lo que tienes que hacer es diseñar los constructores como es debido, no a ciegas, ¡que vas ciego por la vida!

Ejemplo. Tienes una clase Base y otra Derivada.

La clase Derivada tiene un constructor de copia que se basa en un argumento para hacer la copia:

public Derivada(Base toCopyFrom) : base(toCopyFrom)
{
}

Le proporcionamos un objeto del que copiar propiedades. El objeto se lo pasamos a la Base y esta se encarga del resto.

Ahora echamos un ojo al constructor por defecto:

public DynamicTable()
{
	atributoQueNecesitaInicialización = "bla bla bla";
	otroAtributoQueNecesitaInicialización = "bla bla bla";
}

¿Qué ocurre? que si esos atributos necesitan inicializarse, solamente se inicializan llamando al constructor por defecto.

En otro constructor que llama a la base (el de más arriba), los atributos no se inicializan. Puede que tengamos la tentación de modificar el constructor que llama a la base y añadirle esto:

public Derivada(Base toCopyFrom) : base(toCopyFrom), this()
{
}

Pero ¡DÓNDE VAS, LOCO! Eso no puede hacerse y es por una buena razón Pointing up El orden se establece arbitrariamente (primero base, luego this), por lo que si el compilador nos dejara hacer esto, sería una porquerida. Así es que pensamos ¿y si copio las líneas que inicializan las variables también en el otro constructor?

GUARRADA X2!! ¿Duplicar? Chaval, ¡eso es el principio del fin! Nada de duplicar, ¡cojones ya!

Como no somos unos guarretes, decidimos solucionar la papeleta con el estoque fino.

Sencillo, o no tanto:

El constructor por defecto debe llamar al específico con el objeto a null, por ejemplo:

public Derivada() : this(null)
{
}

Aunque lo de meter un null no es una práctica bella (ni aconsejable) lo pongo para que se entienda el concepto. Luego podemos aplicar el patrón NullObject y hacer las comprobaciones necesarias si se trata de ese caso).

De esta manera, el constructor por defecto llama al específico con un null. Finalmente, este constructor específico SÍ llama a la base. Pero además, en él será donde debemos inicializar las variables que teníamos antes. Por lo tanto quedaría así.

public Derivada(Base toCopyFrom) : base(toCopyFrom)
{
	atributoQueNecesitaInicialización = "bla bla bla";
	otroAtributoQueNecesitaInicialización = "bla bla bla";
}

Así, la clase Base construye la copia (le pasamos un nulazo enorme que en estos momento actuaría de indicador. La clase base en este caso debería saber que un null significa un caso especial. Si estamos copiando atributos (constructor de copia) lo más lógico es que no copiara nada.

Si a alguien se le ocurre algo mejor, ¡ahí están los comentarios, muertos de risa! Thinking smile

Ensamblados furiosos

CUIDADO. Los ensamblados de diseño y extensibilidad

  • Microsoft.Windows.Design.Interaction
  • Microsoft.Windows.Design.Extensibility

Usandos para crear inicializadores de tiempo de diseño (clase DefaultInitializer) SON MUY JODIDOS.

He acabado quitándolo porque no había manera de usarlos en 64-bits y .NET 4.5. ¡Muere al arrancar!

BadImageFormatException

SelectedItem, SelectedValue

Los ComboBox, las ListBox y toda esa recua de controles tan útiles son unas verdaderas bestias. En realidad, todos los ItemsControl son tan bellos como complejos.

No entender qué cojopios hacen es una de las razones por la que creamos chorricode totalmente vomitivo. “ItemsControl”, quédate con el nombre. ¡Hay que estudiárselos bien!

A nivel de usuario hay básico entender este par de propiedades:

  • SelectedItem: El elemento seleccionado. Si tenemos un Binding TwoWay a esta propiedad debemos cuidar muy mucho ciertas cosas. Lo más importante:

    El elemento enlazado con el “binding” puede no existir en la lista. Si esto se produce, visualmente no se seleccionará NADA. Es decir, ningún elemento se marcará.

  • SelectedValue: Valor seleccionado. No importa si el elemento no está en la lista. Se selecciona y punto. Queda más claro si digo que en un combo box, el Popup desplegable lleva la lista de elementos, pero luego tiene un elemento especial, que es el que aparece en la cajita del combo cuando está cerrado. El SelectedValue se pondrá ahí. Lo malo es que no siempre es posible representar el elemento (puede ser un elemento no “renderizable”.

IMPORTANTE: Para que la cosa funcione es imprescindible sobreescribir los métodos Equals en los elementos que vayan a ser representados en el ItemsControl. Es lógico, puesto que, ¿cómo va a saber sino cuál elemento se selecciona de la lista si no existe una manera de comprobar cuándo son el mismo elemento?

WARNING: El método Equals por defecto compara referencias, por lo que si son la misma referencia, el SelectedItem furrula fairly good, pero cuando la instancia te la sacas tú de la manga, haces un clone y vete tú a saber qué historias, el Equals es obligatorio si quieres que todo se sincronice solito.

¡Esos Equals tan impopulares a los que nadie les hace caso son importantísimos! Úsalos o tendré que sacar el látigo de dar.

Propiedades (y atributos) de estructuras en Bindings

// La instancia de BorderSides se tiene que establecer a un valor predeterminado.
// Esto antes se hacía con el DefaultValue de la propiedad de dependencia BorderSides.
// El problema es que la instancia era compartida entre todos los ColumnsTextDocPath
// Tampoco puede ser una estructura, porque si no, los Bindings no actualizan nada en la UI.
BorderSides = new BorderSides
{
    Left = true,
    Top = true,
    Right = true,
    Bottom = true,
};

¿Te suena esto?

Resulta que si tú tienes una propiedad de un objeto y esta propiedad tiene un valor que es una estructura en vez de una clase, los bindings a esa propiedad crean una copia de esa estructura (no son referencias, sino valores). Por ese motivo, no es posible “enlazar” instancias como se haría con una referencia a objeto, sino que son 2 objetos distintos.

Por eso en la PropertyGrid si una propiedad es una estructura, los editores de dicha propiedad (siendo compuesta), no son capaces de trazar los cambios de las propiedades de dicha propiedad.

Y si no te enteras de lo que te estoy diciendo, te jodes como Herodes Smile with tongue out