6

I have a binding of the form:

Path=SpecialCollection[0]

The SpecialCollection class extends ObservableCollection and has an indexer property.

public T this[string propertyValue]
{
    get
    {
        // do stuff
        return default(T);
    }
}

My problem is that the binding attempts to get the indexer property value, instead of returning the 0th item in the collection. Is there a way to force the binding to treat 0 as an integer so it returns a collection element, instead of invoking the collection's indexer property's getter?

H.B.
  • 166,899
  • 29
  • 327
  • 400
Craig
  • 1,890
  • 1
  • 26
  • 44
  • Obviously if you use an indexer, the actual indexer will be called. What exactly are you doing there anyway if it returns `default(T)`, i mean, wth? Is it really necessary to overload the indexer for that? – H.B. Apr 21 '11 at 02:11
  • @H.B. You'll notice the 'do stuff' comment I added, which I intended to abstract away the details. FWIW, this particular collection class lets us search for the first item that has a property whose value matches propertyValue. If you're going to propose that we don't do this, it's some legacy stuff that isn't going to change, hence my desire to workaround it. – Craig Apr 21 '11 at 16:54
  • I was aware of the `DoStuff`, but i think an indexer, as the name implies, should only be used to access items via some kind of index. (return default(T) after any kind of operation does not match that) – H.B. Apr 21 '11 at 17:14
  • @H.B. I believe Craig is implying that "Do Stuff" would return the correct value, if it was available, and the return default(T) only gets called when the correct value is not available so the "get" is syntactically happy. – Jesse Chisholm Jun 30 '14 at 18:21

2 Answers2

14

According to MSDN you can tell the binding the type of the value entered as index:

Inside indexers you can have multiple indexer parameters separated by commas (,). The type of each parameter can be specified with parentheses. For example, you can have Path="[(sys:Int32)42,(sys:Int32)24]", where sys is mapped to the System namespace.

I noticed that the Binding constructor taking a path string uses another PropertyPath constructor than the default PropertyPath type converter, said PropertyPath constructor does not work in this scenario. To avoid the problem avoid the Binding constructor by setting the Path property manually which invokes the conversion via type converter.

<!-- Does not work -->
<TextBlock Text="{Binding [(sys:Int32)0]}"/>
<!-- Does work -->
<TextBlock Text="{Binding Path=[(sys:Int32)0]}"/>
H.B.
  • 166,899
  • 29
  • 327
  • 400
  • Beautiful, thank you. I remember seeing this awhile back but couldn't remember what it entailed. – Craig May 02 '11 at 21:18
  • @Craig: It's quite unfortunate that it's mixed into the multiple indices reference, maybe that is the reason i did not see it back when you asked this question. – H.B. May 02 '11 at 21:20
1

Actually you have two indexer properties, one that takes an int argument and one that takes a string argument. Frankly, I don't know how the binding expression chooses which indexer to use when it is ambiguous. If there is only one then it can coerce the index to the type of the indexer argument. If there are two, it can either throw an exception or choose one according to a heuristic. In this case, it apparently chose the wrong one.

To solve this problem you can either move your string indexer "down a level" so it hangs off of a new property so that it doesn't compete with the list indexer or if all you need is List[0] you can add a First property and bypass either indexer.

Rick Sladkey
  • 33,988
  • 6
  • 71
  • 95
  • I ended up settling on this approach. It turned out that I needed to access indexes 0 through 2, so wrapper properties turned out to be the best approach. I could have sworn that I once read about a way to cast the indexer to int though. – Craig Apr 21 '11 at 16:57
  • Or have your string indexer smart enough to recognize a purely numeric string and convert it for the other indexer: **is (key is purely numeric) return this[Int32.Parse(key)];** – Jesse Chisholm Jun 30 '14 at 18:25