I am trying to fill a collection (list) property with values provided by a markup extension. There is a problem doing this if the property providing the collection has a public setter. I think it is a bug in the XAML implementation (using .NET 3.5), but I am not sure.
Consider the following test class
[ContentProperty("SettableList")]
public class Test : FrameworkElement
{
List<string> _settableList = new List<string>();
List<string> _justGettableList = new List<string>();
public List<string> SettableList { get { return _settableList; } set { _settableList = value; } }
public List<string> JustGettableList { get { return _settableList; } }
}
and the following markup extension, which just provides a string.
public class ProvideStringExtension : MarkupExtension
{
public override object ProvideValue(IServiceProvider serviceProvider)
{
return "Blah";
}
}
Now the following test cases all work fine: Hurz
<!-- b) fill SettableList with explicit string: ok -->
<local:Test>
<local:Test.SettableList>
<sys:String>Hurz</sys:String>
</local:Test.SettableList>
</local:Test>
<!-- c) fill JustGettableList with string from markup extension: ok -->
<local:Test>
<local:Test.JustGettableList>
<local:ProvideStringExtension/>
</local:Test.JustGettableList>
</local:Test>
<!-- d) fill SettableList with string form markup extension by using "ContentProperty": ok -->
<local:Test>
<local:ProvideStringExtension/>
</local:Test>
But this test case fails:
<!-- e) fill SettableList with string from markup extension: FAILS -->
<!-- Throws (tries to convert the provided string to List<string>) -->
<local:Test>
<local:Test.SettableList>
<local:ProvideStringExtension/>
</local:Test.SettableList>
</local:Test>
The failing case throws an exception saying that 'string' cannot be converted to 'List'. Is there an explanation why this fails where as the similar cases d) and c) work fine?
The first thing the XAML parser will try to do is take the value you get and "set" the property to that value. So it wants to do this:
this.SettableList = ProvideStringExtension.ProvideValue();
If there is no setter and the property implements ICollection (maybe IList, I forget), then it will try to add the item to the collection:
this.JusetGettableList.Add(ProvideStringExtension.ProvideValue())
They could have probably reversed this logic, but I'm sure there was a design choice made to do it this way.
Also, I believe even if the setter is private, then it will still fail. But that may have been fixed.