Я использую сериализацию XAML для графа объектов (вне WPF / Silverlight) и пытаюсь создать пользовательское расширение разметки, которое позволит заполнять свойство коллекции с помощью ссылок на выбранные члены коллекции, определенные в другом месте XAML.
Вот упрощенный фрагмент XAML, демонстрирующий то, чего я хочу достичь:
<myClass.Languages>
<LanguagesCollection>
<Language x:Name="English" />
<Language x:Name="French" />
<Language x:Name="Italian" />
</LanguagesCollection>
</myClass.Languages>
<myClass.Countries>
<CountryCollection>
<Country x:Name="UK" Languages="{LanguageSelector 'English'}" />
<Country x:Name="France" Languages="{LanguageSelector 'French'}" />
<Country x:Name="Italy" Languages="{LanguageSelector 'Italian'}" />
<Country x:Name="Switzerland" Languages="{LanguageSelector 'English, French, Italian'}" />
</CountryCollection>
</myClass.Countries>
Свойство Languages каждого объекта Country должно быть заполнено IEnumerable
Вот моя попытка создать пользовательское расширение разметки, которое будет выполнять эту роль:
[ContentProperty("Items")]
[MarkupExtensionReturnType(typeof(IEnumerable<Language>))]
public class LanguageSelector : MarkupExtension
{
public LanguageSelector(string items)
{
Items = items;
}
[ConstructorArgument("items")]
public string Items { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
var service = serviceProvider.GetService(typeof(IXamlNameResolver)) as IXamlNameResolver;
var result = new Collection<Language>();
foreach (var item in Items.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(item => item.Trim()))
{
var token = service.Resolve(item);
if (token == null)
{
var names = new[] { item };
token = service.GetFixupToken(names, true);
}
if (token is Language)
{
result.Add(token as Language);
}
}
return result;
}
}
На самом деле, этот код почти работает. Пока ссылающиеся объекты объявляются в XAML до объектов, которые на них ссылаются, метод ProvideValue корректно возвращает IEnumerable
var token = service.Resolve(item);
Но, если XAML содержит прямые ссылки (потому что объекты Language объявлены после объектов Country), это ломается, потому что это требует фиксирующих токенов, которые (очевидно) не могут быть приведены к Language.
if (token == null)
{
var names = new[] { item };
token = service.GetFixupToken(names, true);
}
В качестве эксперимента я попробовал преобразовать возвращаемую коллекцию в Collection в надежде, что XAML сможет как-то разрешить токены позже, но это приводит к исключениям invalid cast при десериализации.
Может ли кто-нибудь подсказать, как лучше всего заставить это работать?
Большое спасибо, Tim