I have a class with an indexer property, with a string key:
public class IndexerProvider {
public object this[string key] {
get
{
return ...
}
set
{
...
}
}
...
}
I bind to an instance of this class in WPF, using indexer notation:
<TextBox Text="{Binding [IndexerKeyThingy]}">
That works fine, but I want to raise a PropertyChanged
event when one of the indexer values changes. I tried raising it with a property name of "[keyname]" (i.e. including [] around the name of the key), but that doesn't seem to work. I don't get binding errors in my output window whatsoever.
I can't use CollectionChangedEvent, because the index is not integer based. And technically, the object isn't a collection anyway.
Can I do this, and so, how?
According to this blog entry, you have to use "Item[]"
. Item being the name of the property generated by the compiler when using an indexer.
If you want to be explicit, you can decorate the indexer property with an IndexerName attribute.
That would make the code look like:
public class IndexerProvider : INotifyPropertyChanged {
[IndexerName ("Item")]
public object this [string key] {
get {
return ...;
}
set {
... = value;
FirePropertyChanged ("Item[]");
}
}
}
At least it makes the intent more clear. I don't suggest you change the indexer name though, if your buddy found the string "Item[]"
hard coded, it probably means that WPF would not be able to deal with a different indexer name.
Additionaly, you can use
FirePropertyChanged ("Item[IndexerKeyThingy]");
To notify only controls bound to IndexerKeyThingy on your indexer.
There are at least a couple of additional caveats when dealing with INotifyPropertyChang(ed/ing) and indexers.
The first is that most of the popular methods of avoiding magic property name strings are ineffective. The string created by the [CallerMemberName]
attribute is missing the '[]' at the end, and lambda member expressions have problems expressing the concept at all.
() => this[] //Is invalid
() => this[i] //Is a method call expression on get_Item(TIndex i)
() => this //Is a constant expression on the base object
Several other posts have used Binding.IndexerName
to avoid the string literal "Item[]"
, which is reasonable, but raises the second potential issue. An investigation of the dissasembly of related parts of WPF turned up the following segment 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[]";
}
The repeated use of "Item[]"
as a constant value suggests that WPF is expecting that to be the name passed in the PropertyChanged event, and, even if it doesn't care what the actual property is called (which I didn't determine to my satisfaction one way or the other), avoiding use of [IndexerName]
would maintain consistency.
Actually, I believe setting the IndexerName attribute to "Item" is redundant. The IndexerName attribute is specifically designed to rename an index, if you want to give it's collection item a different name. So your code could look something like this:
public class IndexerProvider : INotifyPropertyChanged {
[IndexerName("myIndexItem")]
public object this [string key] {
get {
return ...;
}
set {
... = value;
FirePropertyChanged ("myIndexItem[]");
}
}
}
Once you set the indexer name to whatever you want, you can then use it in the FirePropertyChanged event.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With