Свойство PropertyChanged для индексатора
У меня есть класс с свойством indexer со строковым ключом:
public class IndexerProvider {
public object this[string key] {
get
{
return ...
}
set
{
...
}
}
...
}
Я связываюсь с экземпляром этого класса в WPF, используя нотацию индексатора:
<TextBox Text="{Binding [IndexerKeyThingy]}">
Это работает отлично, но я хочу поднять событие PropertyChanged
, когда изменяется одно из значений индексатора. Я попытался поднять его с именем свойства "[keyname]" (т.е. Включая [] вокруг имени ключа), но это, похоже, не работает. Я не получаю ошибки привязки в моем окне вывода.
Я не могу использовать CollectionChangedEvent, потому что индекс не основан на целых числах. И технически, объект все равно не является коллекцией.
Могу ли я сделать это, и так, как?
Ответы
Ответ 1
Согласно этой записи в блоге, вы должны использовать "Item[]"
. Элемент - это имя свойства, сгенерированного компилятором при использовании индексатора.
Если вы хотите быть явным, вы можете украсить свойство indexer атрибутом IndexerName.
Это сделает код похожим:
public class IndexerProvider : INotifyPropertyChanged {
[IndexerName ("Item")]
public object this [string key] {
get {
return ...;
}
set {
... = value;
FirePropertyChanged ("Item[]");
}
}
}
По крайней мере, это делает цель более ясной. Я не предлагаю вам менять имя индексатора, хотя, если ваш собеседник нашел строку "Item[]"
жестко закодированной, это, вероятно, означает, что WPF не сможет иметь дело с другим именем индексатора.
Ответ 2
Дополнительно, вы можете использовать
FirePropertyChanged ("Item[IndexerKeyThingy]");
Чтобы уведомить только средства управления, связанные с IndexerKeyThingy, на вашем индексе.
Ответ 3
На самом деле, я считаю, что установка атрибута IndexerName в "Item" является избыточной. Атрибут IndexerName специально предназначен для переименования индекса, если вы хотите присвоить ему элемент коллекции другому имени. Таким образом, ваш код может выглядеть примерно так:
public class IndexerProvider : INotifyPropertyChanged {
[IndexerName("myIndexItem")]
public object this [string key] {
get {
return ...;
}
set {
... = value;
FirePropertyChanged ("myIndexItem[]");
}
}
}
После того, как вы установите имя индексатора так, как вы хотите, вы можете использовать его в событии FirePropertyChanged.
Ответ 4
Есть несколько дополнительных предостережений при работе с INotifyPropertyChang (ed/ing) и индексаторами.
Во-первых, большая часть популярные методы, избегающие строк имен магических свойств, неэффективны. Строка, созданная атрибутом [CallerMemberName]
, в конце отсутствует '[]', а выражения лямбда-члена имеют проблемы, выражающие концепцию вообще.
() => this[] //Is invalid
() => this[i] //Is a method call expression on get_Item(TIndex i)
() => this //Is a constant expression on the base object
Несколько другие сообщения использовали Binding.IndexerName
, чтобы избежать строкового литерала "Item[]"
, что является разумным, но вызывает вторую потенциальную проблему. Исследование распараллеливания связанных частей WPF показало следующий сегмент в 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[]";
}
Повторное использование "Item[]"
в качестве постоянного значения предполагает, что WPF ожидает, что это будет имя, переданное в событии PropertyChanged, и даже если ему все равно, что вызвано фактическим свойством (что я и сделал) t для моего удовлетворения так или иначе), избегая использования [IndexerName]
, будет поддерживать согласованность.