2009-03-18 14 views
32

Ho una classe con una proprietà indicizzatore, con una stringa chiave:PropertyChanged per la proprietà indicizzatore

public class IndexerProvider { 
    public object this[string key] { 
     get 
     { 
      return ... 
     } 
     set 
     { 
      ... 
     } 
    } 

    ... 
} 

mi legano a un'istanza di questa classe in WPF, utilizzando indicizzatore la notazione:

<TextBox Text="{Binding [IndexerKeyThingy]}"> 

Funziona bene, ma desidero generare un evento PropertyChanged quando cambia uno dei valori dell'indicizzatore. Ho provato ad innalzarlo con un nome di proprietà di "[keyname]" (cioè incluso [] attorno al nome della chiave), ma non sembra funzionare. Non ho errori di binding nella mia finestra di output.

Non riesco a utilizzare CollectionChangedEvent, perché l'indice non è basato su integer. E tecnicamente, l'oggetto non è comunque una collezione.

Posso fare questo, e quindi, come?

risposta

45

Secondo this blog entry, è necessario utilizzare "Item[]". L'elemento è il nome della proprietà generata dal compilatore quando si utilizza un indicizzatore.

Se si desidera essere espliciti, è possibile decorare la proprietà dell'indicizzatore con un attributo IndexerName.

che renderebbe il look codice come:

public class IndexerProvider : INotifyPropertyChanged { 

    [IndexerName ("Item")] 
    public object this [string key] { 
     get { 
      return ...; 
     } 
     set { 
      ... = value; 
      FirePropertyChanged ("Item[]"); 
     } 
    } 
} 

Almeno rende l'intento più chiaro. Non ti suggerisco di cambiare il nome dell'indicizzatore, se il tuo amico ha trovato la stringa "Item[]" codificata, probabilmente significa che WPF non sarebbe in grado di gestire un nome di indicizzatore diverso.

+0

Questo funziona benissimo. Strano che mi mancava quel blogpost nelle mie ricerche su Google. – Inferis

+1

Questa soluzione funziona benissimo, ma ha una seccante limitazione: non è possibile specificare che il valore venga modificato solo per una chiave ... Quindi se si hanno associazioni su più chiavi, verranno tutte aggiornate –

+0

Alcuni anni dopo, ora c'è la parola chiave 'nameof'. Lo uso per tutte le mie chiamate di 'FirePropertyChange', ma puoi" nominare "l'indicizzatore in qualche modo? – Flynn1179

5

In realtà, credo che impostare l'attributo IndexerName su "Elemento" sia ridondante. L'attributo IndexerName è progettato specificatamente per rinominare un indice, se si desidera assegnare un nome diverso all'elemento della raccolta. Così il vostro codice potrebbe essere simile a questa:

public class IndexerProvider : INotifyPropertyChanged { 

    [IndexerName("myIndexItem")] 
    public object this [string key] { 
     get { 
      return ...; 
     } 
     set { 
      ... = value; 
      FirePropertyChanged ("myIndexItem[]"); 
     } 
    } 
} 

Una volta impostato il nome indicizzatore per quello che vuoi, si può quindi utilizzarlo in caso FirePropertyChanged.

15

Additionaly, è possibile utilizzare

FirePropertyChanged ("Item[IndexerKeyThingy]"); 

Per notificare solo controlli associati a IndexerKeyThingy sul indicizzatore.

+0

+1 Utile sapere che puoi aumentarlo per voce del dizionario – abbottdev

+5

Qualcuno ha ottenuto questo risultato correttamente? Non sta funzionando per me. Solo "Articolo []" sembra attivare gli aggiornamenti. –

+2

Funziona perfettamente con Silverlight 4/5, ma su WPF 4.6.2 non funziona affatto. Devo ancora usare Item []. – AqD

5

Ci sono almeno un paio di avvertimenti aggiuntivi quando si tratta di INotifyPropertyChang (ed/ing) e di indicizzatori.

Il primo è che la maggior parte del popular methods di evitare stringhe di nome di proprietà magiche sono inefficaci. Alla stringa creata dall'attributo [CallerMemberName] manca "[]" alla fine e le espressioni dei membri lambda presentano problemi nell'esprimere il concetto.

() => this[] //Is invalid 
() => this[i] //Is a method call expression on get_Item(TIndex i) 
() => this //Is a constant expression on the base object 

Diversi otherposts hanno usato Binding.IndexerName per evitare la stringa letterale "Item[]", che è ragionevole, ma solleva il secondo potenziale problema. Un'indagine sul disassemblaggio delle parti correlate di WPF ha mostrato il seguente segmento in PropertyPath.ResolvePathParts.

if (this._arySVI[i].type == SourceValueType.Indexer) 
    { 
    IndexerParameterInfo[] array = this.ResolveIndexerParams(this._arySVI[i].paramList, obj, throwOnError); 
    this._earlyBoundPathParts[i] = array; 
    this._arySVI[i].propertyName = "Item[]"; 
    } 

L'uso ripetuto di "Item[]" come valore costante suggerisce che WPF si aspetta che per essere il nome passato in caso PropertyChanged, e, anche se non si cura di ciò l'effettiva proprietà viene chiamata (che ho non ha determinato la mia soddisfazione in un modo o nell'altro), evitando l'uso di [IndexerName] manterrebbe la coerenza.

Problemi correlati