Aplanar propiedades de dependencia

Muy útil para evitar tener propiedades redundantes cuando hay herencia (recuerda que las propiedades de dependencia pueden ser heredables). Here I go with the explanation!

Problema:

  1. Tenemos una estructura en árbol de padres e hijos, todos ellos DependencyObjects.
  2. Tenemos una propiedad de dependencia heredable
  • Queremos que el valor de la propiedad de dependencia más repetido en los hijos pase a ser el valor del padre.
  • A partir de ese momento, la propiedad en el padre es el valor más repetido en sus hijos.

¿Qué podemos hacer ahora?

Puesto que por el mecanismo de herencia, el valor del padre se hereda en los hijos, lo ideal sería que si el padre tiene el valor más repetido, en todos los hijos que tengan el valor más repetido se “reseteara” la propiedad. Haciendo esto, efectivamente, el hijo hereda el valor del padre automáticamente, que además será el valor óptimo (el más repetido). Además, esto es recursivo, por lo que el padre de todos los padres tendrá los valores más comunes dentro de sus hijos.

Esto puede ser útil en varias ocasiones (a mí ya me ha sido útil Winking smile)

Aquí dejo el código:

 private static void FlattenProperty(IParentOfDependencyObjectChildren parent,
            DependencyProperty dependencyProperty)
        {
            var childrenList = parent.Children.ToList();

            if (childrenList.Any())
            {
                foreach (IParentOfDependencyObjectChildren child in childrenList)
                {
                    FlattenProperty(child, dependencyProperty);
                }

                var values = childrenList.Select(o => o.GetValue(dependencyProperty)).ToList();
                var mostRepeated = values.GetMostRepeatedItem();
                foreach (var child in childrenList)
                {
                    if (child.GetValue(dependencyProperty).Equals(mostRepeated))
                    {
                        child.ClearValue(dependencyProperty);
                    }
                }
                parent.SetValue(dependencyProperty, mostRepeated);
            }
        }
 public static class EnumerableExtensions
    {
        public static T GetMostRepeatedItem<T>(this IEnumerable<T> list)
        {
            // group by value and count frequency
            var query = from i in list
                        group i by i into g
                        select new { g.Key, Count = g.Count() };



            // compute the maximum frequency
            var ordenada = (from c in query orderby c.Count descending select c);

            return ordenada.First().Key;
        }
    }
    public interface IParentOfDependencyObjectChildren
    {
        IEnumerable<DependencyObject> Children { get; }
        void SetValue(DependencyProperty dependencyProperty, object value);
    }

Herencia en Dependency Properties

El temita jodido del otro día, cuando estuve dándole caña a las propiedades de dependencia heredables (FrameworkPropertyMetadata.Inherits)

Yo tenía una magggnífica propiedad llamada Relleno compuesta de:

  • Color (Color)
  • Id de patrón (int)
  • Tint (double)

Esta propiedad se hereda por por lo que los elementos del árbol visual. Hasta ahí, bien.

El asunto se puso marrón oscuro cuando en un hijo me daba por modificar el miembro “Color”. ¿Qué ocurría?

Resultado esperado:

image

Resultado obtenido:

image

La razón es que el Relleno es una instancia compartida entre todos los elementos de la jerarquía. El del padre es la misma instancia que el del hijo, por lo que si modificamos el Color al Relleno, efectivamente, ¡estamos cambiando la misma cosa!

¿Qué hemos de hacer? Pues la cosa quizá no es muy sencilla, pero lo primero de todo es que tenemos que tener claro qué es un Value-Object, es decir, cuándo el valor de una cosa determina qué cosa es.

Esto depende de la semántica que queramos darle. Para mí, si tengo una instancia de Relleno y le cambio el Color, serán dos patrones distintos, por lo que no sería admisible cambiar el Color, sino que debería otra crearme otra instancia de Relleno distinta (Relleno debería ser inmutable). De estas manera, 2 instancias distintas nos darían el resultado deseado.

Espero que la monserga te haya servido, pequeño saltamontes.

Hala, a echarte un Call Of Duty, que ya es escrito bastante, mamoncete.

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