PropertyChanged for indexer property

瘦欲@ 提交于 2019-12-17 15:32:33

问题


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?


回答1:


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.




回答2:


Additionaly, you can use

FirePropertyChanged ("Item[IndexerKeyThingy]");

To notify only controls bound to IndexerKeyThingy on your indexer.




回答3:


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.




回答4:


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.



来源:https://stackoverflow.com/questions/657675/propertychanged-for-indexer-property

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!