Hard things with Expressions (property selector)

It has been hard at least for me.

This method applies the value to the specified property of the target items:

 private static void SetPropertyToAll<T, TValue>(IEnumerable<T> targetItems, Expression<Func<T, TValue>> propertyExpression, TValue value)
        {
            if (propertyExpression.Body is MemberExpression)
            {
                var memberExpression = (MemberExpression)propertyExpression.Body;

                var propInfo = (PropertyInfo)memberExpression.Member;

                foreach (var item in targetItems)
                {
                    propInfo.SetValue(item, value, null);
                }
            }
            else
            {
                throw new InvalidOperationException("See link to the Stack Overflow question below!!");
            }
        }

And this is the original question in which I found all the information I needed in order for this to work like fine cinnamon 😉

http://stackoverflow.com/a/2789606/1025407

Good luck, Expression Boy!

Anuncios

¿Objetos dinámicos en una DataGrid?

¿Qué broma es esta? ¡Enlazar una lista de elementos con un número indeterminado de propiedades es un fucking inferno!

Pero por otra parte, nadie dijo que representar cosas con un número variable de propiedades fuese a representarse fácilmente. Más bien es todo un reto.

Estoy investigando cómo hacerlo y parece ser que hay gente que lo ha conseguido haciendo uso del DynamicObject de .NET 4.0. Al parecer hay que lidiar con algún problema morrocotudo, ya que el DataGrid con “AutoGenerateColumns” obtiene las propiedades mediante el Type no mediante cada instancia. Golpe bajo.

A ver cómo me las apaño, que me he puesto en contacto con algunos lobos de mar que controlan del tema, pero parece que casi se esconden bajo las setas.

¡Cada problema es un mundo, chavalis!

Duck Typing FTW

Para que no se te olvide, golferas, si quieres programar como Dios manda deberías hacerlo siempre intentando programar “contra” interfaces, aunque no es una expresión que me guste mucho eso de “contra”, pero así todo el mundo lo entiende.

Para facilitarnos la tarea existe una técnica llamada Duck Typing.

“Si anda como un pato, nada como un pato y vuela como un pato, entonces es un pato”.

En pocas palabras Duck Typing es conseguir tratar como un objeto fuese algo que realmente no lo es, aunque cumple con sus requisitos. Esto se traduce a tener tener un tipo que no implementa cierta interfaz, pero lo tratamos como si la implementase.

El gran Juan María Hernández (@gulnor) publica en su blog publica un artículo especialmente interesante donde explica muy bien qué es y cómo funciona, además de exponer una pequeña implementación de andar por casa ayudarnos a entender cómo funciona por dentro.

¿Cómo lo consigue? Con reflexión, invocando atributos y métodos como si realmente estuviésemos trabajando con el tipo que queremos.

Sin duda, un concepto revolucionario con lo que podemos crear código más abstracto  y menos acoplado, que es de lo que se trata.

Por cierto, si no quieres devanarte la sesera para hacerte tu propia implementación de esta , échale un vistazo al DynamicProxy o al aparentemente bueno ImpromptuInterface (http://code.google.com/p/impromptu-interface/).

Venga, a probarlo ya, ¡jodebles!

Más localización a base de atributos

El localizar una aplicación con los tiempos que corren es necesidad imperiosa. Hacerlo de forma elegante (el único modo en el que tú entiendes las cosas, ¡truhán!) puede ser algo menos que strightforward. Una forma que dista de ser mi preferida (me consta que es mejorable), es haciendo uso de atributos.

Como hace poco me tocó vérmelas con la PropertyGrid del famoso y altamente recomendable WPF Extended Toolkit, he tenido que ponerme a fuego lento con atributos que realmente funcionan, aunque tengo que confesar que no les he prestado demasiado tiempo a dominarlos a la perfección (maldito tiempo, siempre acuciando).

Pues aquí los dejo, sin más dilación. Su uso es bien sencillo.

PropertyGrid usa propiedades de objetos por reflexión. Esas propiedades tienen los nombres y descripciones como Dios las trajo al mundo, es decir, en su mayoría en inglés. Y para qué nos vamos a engañar, ¡muchos programamos todo en inglés! Así es que, sin toquetear demasiado el código y aplicando atributos (attributed programming model, que me pone verraco), he forzado la maquinaria.

Todo se basa en la LocalizableString (un invento que he sacado de las tripas de .NET)

// Type: System.ComponentModel.DataAnnotations.LocalizableString
// Assembly: System.ComponentModel.DataAnnotations, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
// Assembly location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.ComponentModel.DataAnnotations.dll

using System;
using System.Globalization;
using System.Reflection;
using System.Runtime;
using WPF.Common.Properties;

namespace WPF.Common.Attributes
{
    internal class LocalizableString
    {
        private readonly string propertyName;
        private string propertyValue;
        private Type resourceType;
        private Func&lt;string&gt; cachedResult;

        public string Value
        {
            [TargetedPatchingOptOut(&quot;Performance critical to inline this type of method across NGen image boundaries&quot;)]
            get
            {
                return this.propertyValue;
            }
            set
            {
                if (!(this.propertyValue != value))
                    return;
                this.ClearCache();
                this.propertyValue = value;
            }
        }

        public Type ResourceType
        {
            [TargetedPatchingOptOut(&quot;Performance critical to inline this type of method across NGen image boundaries&quot;)]
            get
            {
                return this.resourceType;
            }
            set
            {
                if (!(this.resourceType != value))
                    return;
                this.ClearCache();
                this.resourceType = value;
            }
        }

        [TargetedPatchingOptOut(&quot;Performance critical to inline this type of method across NGen image boundaries&quot;)]
        public LocalizableString(string propertyName)
        {
            this.propertyName = propertyName;
        }

        public string GetLocalizableValue()
        {
            if (this.cachedResult == null)
            {
                if (this.propertyValue == null || this.resourceType == (Type)null)
                {
                    this.cachedResult = (Func&lt;string&gt;)(() =&gt; this.propertyValue);
                }
                else
                {
                    PropertyInfo property = this.resourceType.GetProperty(this.propertyValue);
                    bool flag = false;
                    if (!this.resourceType.IsVisible || property == (PropertyInfo)null || property.PropertyType != typeof(string))
                    {
                        flag = true;
                    }
                    else
                    {
                        MethodInfo getMethod = property.GetGetMethod();
                        if (getMethod == (MethodInfo)null || !getMethod.IsPublic || !getMethod.IsStatic)
                            flag = true;
                    }
                    if (flag)
                    {
                        string exceptionMessage = string.Format(CultureInfo.CurrentCulture, Resources.LocalizableString_LocalizationFailed, (object)this.propertyName, (object)this.resourceType.FullName, (object)this.propertyValue);
                        this.cachedResult = (Func&lt;string&gt;)(() =&gt;
                        {
                            throw new InvalidOperationException(string.Format(&quot;{0}: {1}&quot;, exceptionMessage, propertyValue));
                        });
                    }
                    else
                        this.cachedResult = (Func&lt;string&gt;)(() =&gt; (string)property.GetValue((object)null, (object[])null));
                }
            }
            return this.cachedResult();
        }

        private void ClearCache()
        {
            this.cachedResult = (Func&lt;string&gt;)null;
        }
    }
}

Ahora los atributos mágicos:

Para categoría, CategoryAttribute.

using System;
using System.ComponentModel;

namespace WPF.Common.Attributes
{
    [AttributeUsage(AttributeTargets.All)]
    public sealed class LocalizedCategoryAttribute : CategoryAttribute
    {
        private Type resourceType;
        private readonly LocalizableString category = new LocalizableString(&quot;Category&quot;);

        protected override string GetLocalizedString(string value)
        {
            return category.GetLocalizableValue();
        }

        public string Name
        {
            get { return category.Value; }
            set
            {
                if (category.Value == value)
                    return;
                category.Value = value;
            }
        }

        public Type ResourceType
        {
            get { return resourceType; }
            set
            {
                if (resourceType == value)
                    return;

                resourceType = value;
                category.ResourceType = value;
            }
        } 
    }
}

Y para la descripción, DescriptionAttribute.

using System;
using System.ComponentModel;

namespace WPF.Common.Attributes
{
    [AttributeUsage(AttributeTargets.All)]
    public sealed class LocalizedDescriptionAttribute : DescriptionAttribute
    {
        private Type resourceType;
        private readonly LocalizableString description = new LocalizableString(&quot;Description&quot;);

        public string Name
        {
            get { return description.Value; }
            set
            {
                if (description.Value == value)
                    return;
                description.Value=value;
            }
        }

        public override string Description
        {
            get { return description.GetLocalizableValue(); }
        }

        public Type ResourceType {
            get { return resourceType; }
            set
            {
                if (resourceType == value)
                    return;

                resourceType = value;
                description.ResourceType = value;
            }
        } 
            
    }
}

Atributos que se ponen tercos. MethodAccessException!

RESUELTO: El problema es que el ensamblado donde se intentaba recuperar el atributo Display estaba declarado con AllowPartiallyTrustedCallers (otro atributo). No tenía ni idea de que estaba puesto, porque se trataba del código fuente de la Extended WPF Toolkit. Según pone en la descripción estaba puesto para “permitir XBAP”, cosa que no me interesa para nada, jejeje. El codiguillo en cuestion que he tenido que borrar era éste (AssemblyInfo.cs):

// Needed to enable xbap scenarios
[assembly: AllowPartiallyTrustedCallers]

Llega un momento de la vida de un programador en la que la mezcla de la complejidad del Framework, la plataforma y la ignorancia propia tornan una situación trivial en algo difícilmente abordable. Cuando ese momento llega es cuando uno dice “¡copón bendito!”.

Un ejemplo real: En tu PC la cosa se ejecuta fina, pero en otra máquina la misma cosa dice que naranjas de la China. Pega un flete y todo el mundo mira al responsable; es decir, a ti.

Cuando pasa, te pones a hacer pruebas, depuras como un zorro astuto (o eso es lo que tú piensas) y te das cuenta de que una excepción que nunca en tu jodida vida habías encontrado, aparece chillando.

image

Publicas una pregunta en los foros de MSDN tal que así: http://social.msdn.microsoft.com/Forums/en-US/clr/thread/bad796fc-e67d-420b-bbdd-cb3b5d24e006

Y un tipo que te llama “Desde” en vez de José Manuel, te suelta un trocete de documentación. ¿Qué cojones haces?

¡Llorar y suplicar que no hay que solucionarlo a las bravas, con un chorricode que destile cutrería en vez de elegancia!

Mientras la solución se vislumbra y para dejar constancia de ello, aparte de escribir un libro cuando podría haber escrito un comentario guarrero, pongo el código que fue la pesadilla de ayer:

 return propertyDescriptor.Attributes.OfType&amp;lt;DisplayAttribute&amp;gt;().FirstOrDefault(); 

Parece ser que el atributo Display es un mamón que no se deja coger, ¿por qué? Ni zorra. A mí me funciona y a los demás no, ¿será porque los demás están en un dominio y yo, como soy tan chulo, no?

Pues ni idea.

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

Crear llamada a método genérico

¡Es una jodida locura!

 

Para hacer una llamada a este método, que como observarás es un método de extensión…

public static T GetTransform<T>(this UIElement uiElement) where T : Transform

{

    var transform = uiElement.RenderTransform;

    if (transform is T)

        return (T)transform;

 

    if (transform is TransformGroup)

    {

        var group = (TransformGroup)transform;

        return group.Children.OfType<T>().SingleOrDefault();

    }

 

    return null;

}

… hay que poner esto:

 

MethodInfo method = typeof(UIElementExtensions).GetMethod("GetTransform");

             var genericMethod = method.MakeGenericMethod(new Type[] { transform.GetType() });

             var existingTranform = genericMethod.Invoke(uiElement, new object[] { uiElement });