Подтвердить что ты не робот

Свойство PropertyChanged для индексатора

У меня есть класс с свойством indexer со строковым ключом:

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

    ...
}

Я связываюсь с экземпляром этого класса в WPF, используя нотацию индексатора:

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

Это работает отлично, но я хочу поднять событие PropertyChanged, когда изменяется одно из значений индексатора. Я попытался поднять его с именем свойства "[keyname]" (т.е. Включая [] вокруг имени ключа), но это, похоже, не работает. Я не получаю ошибки привязки в моем окне вывода.

Я не могу использовать CollectionChangedEvent, потому что индекс не основан на целых числах. И технически, объект все равно не является коллекцией.

Могу ли я сделать это, и так, как?

4b9b3361

Ответ 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], будет поддерживать согласованность.