Domanda

Sto giocando con PropertyDescriptor e IcustomTedeScriptor (ancora) tentativo di associare un datagrid WPF a un oggetto, per il quale i dati vengono archiviati in un dizionario.

Poiché se si passa WPF DataGrid un elenco di oggetti del dizionario, genererà automaticamente colonne in base alle proprietà pubbliche di un dizionario (confronto, conteggio, chiavi e valori) la mia persona sottoclasita e implementa Icustomtypedescriptor.

ICustomTedeScriptor definisce un metodo GetProperties che restituisce una proprietà di proprietà di proprietà.

PropertyDescriptor è astratto, quindi devi sottoclassare, ho pensato che avrei avuto un costruttore che prendesse il func e un parametri di azione che delegano l'ottenimento e l'impostazione dei valori nel dizionario.

Quindi creo una persona propcertydescriptor per ogni chiave del dizionario in questo modo:

            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);
            }

Il problema è che ogni proprietà ottenga il proprio func e azione, ma condividono tutti la variabile esterna S Quindi, sebbene DataGrid autogenera le colonne per "Id", "FirstName", "LastName", "Age", "Gender", ottengono tutti contro "Gender" che è il valore di riposo finale di S Nel ciclo foreach.

Come posso assicurarmi che ogni delegato utilizzi la chiave del dizionario desiderata, ovvero il valore di S nel momento in cui il func/azione è istanziato?

Molto obbligato.


Ecco il resto della mia idea, sto solo sperimentando qui queste non sono lezioni "reali" ...

// 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);
    }
}
È stato utile?

Soluzione

La soluzione di Marc è ovviamente corretta, ma ho pensato di espandermi il perché di seguito. Come molti di noi sanno, se dichiari una variabile in a for o foreach affermazione, vive solo per quanto c'è dentro, il che lo rende sembrare Come la variabile è la stessa di una variabile dichiarata nel blocco di tale affermazione di tale affermazione, ma non è giusto.

Per capirlo meglio, prendi i seguenti per loop. Poi ri-starerò il ciclo "equivalente" tra un po 'di tempo.

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

Questo funziona in una forma di while come di seguito: (Non è esattamente lo stesso, perché continue agirà in modo diverso, ma per le regole di scoping, è lo stesso)

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

Quando "è esploso" in questo modo, l'ambito delle variabili diventa più chiaro e puoi capire perché cattura sempre lo stesso valore "s" nel tuo programma e perché la soluzione di Marc mostra dove posizionare la tua variabile in modo che una sia unica catturato ogni volta.

Altri suggerimenti

Semplicemente:

        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 variabili catturate, è dove si trova dichiarato questo è importante. Quindi, dichiarando la variabile catturata all'interno del loop, si ottiene un'istanza diversa della classe di acquisizione per iterazione (la variabile loop, s, è tecnicamente dichiarato fuori il cappio).

Crea una copia locale di s dentro il tuo for Loop e usalo.

for(string s in this.Keys) {
string key = s;
//...
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top