Pregunta

Estoy jugando con PropertyDescriptor y ICustomTypeDescriptor (todavía tratando de enlazar un WPF DataGrid a un objeto, para que los datos se almacenan en un Diccionario.

Ya que si se pasa de WPF DataGrid una lista de objetos de Diccionario se generará automáticamente columnas basadas en las propiedades públicas de un diccionario (Comparador, Contar, Claves y Valores) de mi Persona subclases Diccionario e implementa ICustomTypeDescriptor.

ICustomTypeDescriptor define un GetProperties método que devuelve un PropertyDescriptorCollection.

PropertyDescriptor se resumen así que usted tiene que crear subclases de él, pensé que había que tener un constructor que tomó Func y parámetros de Acción que el delegado de la obtención y establecimiento de los valores en el diccionario.

Yo, a continuación, crear un PersonPropertyDescriptor para cada Clave en el diccionario como este:

            foreach (string s in this.Keys)
            {
                var descriptor = new PersonPropertyDescriptor(
                        s,
                        new Func<object>(() => { return this[s]; }),
                        new Action<object>(o => { this[s] = o; }));
                propList.Add(descriptor);
            }

El problema es que cada propiedad tiene su propia Func y Acción, pero todos ellos comparten la variable externa s por lo tanto, aunque el DataGrid genera las columnas de "ID","Nombre","Apellidos", "Edad", "Género" que todos los get y set en contra de "Género", que es el descanso final valor de s en el bucle foreach.

Cómo puedo asegurarme de que cada delegado utiliza el diccionario deseado Clave, es decir,el valor de s en el momento de la Func/Acción se crea una instancia?

Muy agradecido.


Aquí está el resto de mi idea, yo solo estoy experimentando aquí no se trata de 'real' de las clases...

// DataGrid binds to a People instance
public class People : List<Person>
{
    public People()
    {
        this.Add(new Person());
    }
}

public class Person : Dictionary<string, object>, ICustomTypeDescriptor
{
    private static PropertyDescriptorCollection descriptors;

    public Person()
    {
        this["ID"] = "201203";
        this["FirstName"] = "Bud";
        this["LastName"] = "Tree";
        this["Age"] = 99;
        this["Gender"] = "M";        
    }        

    //... other ICustomTypeDescriptor members...

    public PropertyDescriptorCollection GetProperties()
    {
        if (descriptors == null)
        {
            var propList = new List<PropertyDescriptor>();

            foreach (string s in this.Keys)
            {
                var descriptor = new PersonPropertyDescriptor(
                        s,
                        new Func<object>(() => { return this[s]; }),
                        new Action<object>(o => { this[s] = o; }));
                propList.Add(descriptor);
            }

            descriptors = new PropertyDescriptorCollection(propList.ToArray());
        }

        return descriptors;
    }

    //... other other ICustomTypeDescriptor members...

}

public class PersonPropertyDescriptor : PropertyDescriptor
{
    private Func<object> getFunc;
    private Action<object> setAction;

    public PersonPropertyDescriptor(string name, Func<object> getFunc, Action<object> setAction)
        : base(name, null)
    {
        this.getFunc = getFunc;
        this.setAction = setAction;
    }

    // other ... PropertyDescriptor members...

    public override object GetValue(object component)
    {
        return getFunc();
    }

    public override void SetValue(object component, object value)
    {
        setAction(value);
    }
}
¿Fue útil?

Solución

Marc solución es correcta, pero he pensado que me gustaría ampliar por QUÉ a continuación.Como la mayoría de nosotros sabemos, si se declara una variable en un for o foreach declaración, sólo se vive tan de largo como lo que está dentro, lo que hace parece como la variable es el mismo como una variable declarada en la declaración de bloque de tal declaración, pero eso no es correcto.

Para entenderlo mejor, tomemos la siguiente for-loop.A continuación, voy a volver a estado el "equivalente" de bucle en un tiempo-forma.

for(int i = 0; i < list.Length; i++)
{
    string val;
    list[i] = list[i]++;
    val = list[i].ToString();
    Console.WriteLine(val);
}

Esto funciona en el tiempo-formulario como el siguiente:(no es exactamente la misma, porque continue actuará de manera diferente, pero por reglas de ámbito, es el mismo)

{
    int i = 0;
    while(i < list.Length)
    {
        {
            string val;
            list[i] = list[i]++;
            val = list[i].ToString();
            Console.WriteLine(val);
        }
        i++;
    }
}

Cuando "explotó" de esta manera, el alcance de las variables se hace más claro, y usted puede ver por qué siempre se captura el mismo valor de "s" en su programa, y por qué Marc solución muestra dónde colocar la variable, de modo que uno se captura cada momento.

Otros consejos

Simplemente:

        foreach (string s in this.Keys)
        {
            string copy = s;
            var descriptor = new PersonPropertyDescriptor(
                    copy,
                    new Func<object>(() => { return this[copy]; }),
                    new Action<object>(o => { this[copy] = o; }));
            propList.Add(descriptor);
        }

Con variables capturadas, es donde está declarado eso es importante. Entonces, al declarar la variable capturada dentro del bucle, obtienes una instancia diferente de la clase de captura por iteración (la variable de bucle, s, se declara técnicamente fuera de el lazo).

crear una copia local de s dentro de tu for bucle y use eso.

for(string s in this.Keys) {
string key = s;
//...
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top