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

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

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

    ...
}

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

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

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

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

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


person Inferis    schedule 18.03.2009    source источник


Ответы (4)


Согласно этой записи блога, вы нужно использовать "Item[]". Item - это имя свойства, созданное компилятором при использовании индексатора.

Если вы хотите быть явным, вы можете украсить свойство индексатора символом Атрибут IndexerName.

В результате код будет выглядеть так:

public class IndexerProvider : INotifyPropertyChanged {

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

По крайней мере, это проясняет замысел. Я не предлагаю вам изменять имя индексатора, хотя, если ваш друг нашел строку "Item[]" жестко закодированной, это, вероятно, означает, что WPF не сможет иметь дело с другим именем индексатора.

person Jb Evain    schedule 18.03.2009
comment
Это прекрасно работает. Странно, что я пропустил этот пост в моем поиске в Google. - person Inferis; 18.03.2009
comment
Это решение отлично работает, но у него есть досадное ограничение: вы не можете указать, что значение изменилось только для одного ключа ... Так что, если у вас есть привязки для многих ключей, все они будут обновлены - person Thomas Levesque; 04.08.2011
comment
Несколько лет спустя появилось ключевое слово nameof. Я использую это для всех своих FirePropertyChange вызовов, но можете ли вы каким-либо образом nameof индексатор? - person Flynn1179; 04.04.2017
comment
Не уверен, что реализовано в FirePropertyChanged, но использование OnPropertyChanged("Item") - единственный рабочий код по крайней мере в Xamarin Forms. Не уверен, почему это так сложно с добавлением []. - person King King; 26.07.2017
comment
Вы должны использовать Binding.IndexerName (docs.microsoft .com / fr-fr / dotnet / api /) вместо элемента жесткого кодирования [] - person Maxence; 21.01.2021

Дополнительно вы можете использовать

FirePropertyChanged ("Item[IndexerKeyThingy]");

Уведомлять только элементы управления, привязанные к IndexerKeyThingy в вашем индексаторе.

person ghord    schedule 11.08.2011
comment
+1 Полезно знать, что вы можете поднять его для каждой словарной статьи - person abbottdev; 17.03.2014
comment
Кто-нибудь успешно заставил это работать? У меня это не работает. Похоже, что только элемент [] запускает обновления. - person Kevin Donn; 10.09.2015
comment
Он отлично работает в Silverlight 4/5, но в WPF 4.6.2 он не работает вообще. По-прежнему нужно использовать Item []. - person jiping-s; 15.12.2016
comment
Это дискретное INotifyCollectionChanged поведение! Интересно, будет ли это работать в Xamarin.Forms. - person Shimmy Weitzhandler; 10.05.2017
comment
Как это в многомерных массивах? - person Shimmy Weitzhandler; 28.10.2019

Есть как минимум пара дополнительных предостережений при работе с INotifyPropertyChang (ed / ing) и индексаторами.

Во-первых, большинство популярных методов избежать магии строки имен свойств неэффективны. Строка, созданная заголовком _ 1_ в конце атрибута отсутствует '[]', а лямбда-выражения вообще не могут выразить концепцию.

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

Несколько другое сообщения использовали _ 3_, чтобы избежать строкового литерала "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, и, даже если ему все равно, что вызывается фактическое свойство (что я не определил к своему удовлетворению так или иначе), отказ от использования [IndexerName] обеспечит согласованность.

person Andrew Elford    schedule 27.03.2014

На самом деле, я считаю, что установка для атрибута IndexerName значения «Item» излишне. Атрибут IndexerName специально разработан для переименования индекса, если вы хотите дать этому элементу коллекции другое имя. Итак, ваш код может выглядеть примерно так:

public class IndexerProvider : INotifyPropertyChanged {

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

Как только вы установите любое имя индексатора, вы можете использовать его в событии FirePropertyChanged.

person dimondwoof    schedule 11.07.2010