How to setup a mapping for a simple hierarchy with AutoMapper?

Hi everyone! I decided to publish this because I have just made a very basic toy application to clear my mind about the mapping hierarchies (using the Include method). I finally got it and to celebrate I’m posting the code. Of course, it uses AutoMapper, by Jimmy Bogard (thanks for your fantastic mapping library!).

It uses an extension method to ignore not mapped members in one of the levels of the hierarchy.

namespace MapperTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Mapper.CreateMap<GraphicsBase, BaseObject>()
                .ForMember(o => o.X1, expression => expression.MapFrom(b => b.Left))
                .Include<GraphicsRectBase, RectangleBase>()
                ;

            Mapper.CreateMap<GraphicsRectBase, RectangleBase>()
                .ForMember(o => o.Rx, expression => expression.MapFrom(b => b.CornerRadius))
                .Include<GraphicsField, Field>()
                .IgnoreAllNonExisting()
                ;

            Mapper.CreateMap<GraphicsField, Field>()
               .ForMember(o => o.Name, expression => expression.MapFrom(b => b.FieldName));

            var graphicsField = new GraphicsField { Left = 1, CornerRadius = 2, FieldName = "Field" };

            var baseObject = Mapper.Map(graphicsField);

            Mapper.AssertConfigurationIsValid();
        }
    }

    public static class AutoMapperExtensions
    {
        public static IMappingExpression<TSource, TDestination> IgnoreAllNonExisting<TSource, TDestination>(
            this IMappingExpression<TSource, TDestination> expression)
        {
            var sourceType = typeof(TSource); var destinationType = typeof(TDestination);
            var existingMaps = Mapper.GetAllTypeMaps().First(x => x.SourceType == sourceType && x.DestinationType == destinationType);
            foreach (var property in existingMaps.GetUnmappedPropertyNames())
            {
                expression.ForMember(property, opt => opt.Ignore());
            }
            return expression;
        }
    }

    public class GraphicsBase
    {
        public double Left { get; set; }
    }

    public class GraphicsRectBase : GraphicsBase
    {
        public double CornerRadius { get; set; }
    }

    public class GraphicsField : GraphicsRectBase
    {
        public string FieldName { get; set; }
    }

    public class BaseObject
    {
        public double X1 { get; set; }
    }

    public class RectangleBase : BaseObject
    {
        public double Rx { get; set; }
        public double NonExisting { get; set; }
    }

    public class Field : RectangleBase
    {
        public string Name { get; set; }
    }
}
Anuncios

Progresos en Visual Designer

Pues sí, peña, he conseguido aplicar snapping a las operaciones de redimensión. Ha requerido revisar y rediseñar gran parte del código.

También le he metido unas buenas features que parecían fáciles y no lo han sido tanto. y Snapping en todos los lados (4 por rectángulo) y la alineación, que ha sido un mini infierno XD

Lo de la alineación me ha costado horrores, pero ¿por qué? Pues realmente alinear es lo más chorra y sencillo del mundo. Lo difícil ha sido que la selección adapte su tamaño después de los cambios de los elementos contenidos en ella.

Repecto a esto, hay una clase que a día de hoy se llama ChildrenExpandableCanvasItem cuyo tamaño depende de sus hijos. Resulta que los hijos pueden cambiar de posición o tamaño. El padre (ChildrenExpandableCanvasItem) debe responder a estos cambios y adaptar sus propias dimensiones cuando cambia un hijo. Ahí ha estado lo jorobado. Por el momento funciona, aunque no estoy contento con el código. Pero todo se andará.

Por el momento, ahí tenéis el vídeo 😉

VisualDesigner: Bloqueado con la redimensión :S

Chavales, me he quedado frito cuando he llegado al snapping con la redimensión de elementos en el lienzo. Resulta que el snapping (alineación y ajuste) conforme vas moviendo o redimensionando los elementos es más chungo de lo que pensaba. El problema está en que el uso de deltas para movimiento y tamaño es complejísimo. Me explico: si un sistema de snapping se basa en movimientos relativos en vez de absolutos es muy difícil de programar, incluso mentalmente requiere una complejidad adicional bastante dura. ¿Cómo sabríamos si un objeto está junto a otro si solamente sabemos que la posición se ha incrementado en, por ejemplo, 3 píxeles?

Además, la dificultad añadida de tomar un punto de referencia para calcular los desplazamientos (tanto al mover como al redimensionar). Se me daba la situación de que al realizar un snap, la posición se fuerza, con lo que si tomamos como referencia el elemento al que vamos a forzar, la siguiente medida que hagamos será falsa. Para que me entendáis (aunque es chungo), si la posición de entrada real (deseada) es x=7, y=4 y forzamos a que la posición sean múltiplo de 5 para ambas coordenadas, la posición final (efectiva) sería x=5, y=5. Por tanto, si estamos usando la posición efectiva (5, 5), que va a saltos, en vez de la deseada, las medidas también fluctúan bruscamente debido a los saltos. Es decir, deberíamos tener por un lado la medida real intacta de la posición y basándonos en ella, posicionar el objeto.

He terminando decidiendo que todas las transformaciones usarán coordenadas absolutas (bueno, en realidad relativas al padre que contiene al objeto que está siendo modificado). De esta manera, al mover un objeto, la posición inicial queda almacenada durante la operación y se va calculando la final conforme se mueve el ratón.

De esta manera, hacer snapping es mucho más fácil. Creo que podré avanzar un poco más.

Otro tema que me ha tenido muy entretenido ha sido la redimensión. Quería hacerla sin condicionales y al final me ha salido más o menos, ¡pero telita lo que me ha costado! Llegué a plantear el uso de elipses para calcular las posiciones de los puntos de redimensión (los puntos que salen en las esquinas para cambiar el tamaño de los objetos en casi todos los programas de dibujo y diseño).

Así es que voy al lío, que todavía me falta un poquín para que se quede aceptable.

Stay tuned!