如何创建XAML标记扩展返回集合 [英] How to create a XAML markup extension that returns a collection

查看:201
本文介绍了如何创建XAML标记扩展返回集合的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用XAML序列化对象图(WPF / Silverlight的以外),我试图创建一个自定义标记扩展,允许集合属性使用引用在XAML别处定义的集合选择的成员来填充

I am using XAML serialization for an object graph (outside of WPF / Silverlight) and I am trying to create a custom markup extension that will allow a collection property to be populated using references to selected members of a collection defined elsewhere in XAML.

下面是一个简化的XAML代码片段,演示了什么,我的目标是实现:

Here's a simplified XAML snippet that demonstrates what I aim to achieve:

<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>

语言的每个属性的国家对象是用填充的的IEnumerable<语言方式> 包含要引用语言中指定对象的 LanguageSelector 的,这是一个自定义标记扩展

The Languages property of each Country object is to be populated with an IEnumerable<Language> containing references to the Language objects specified in the LanguageSelector, which is a custom markup extension.

下面是我在创建自定义标记扩展,将在这个角色服务的尝试:

Here is my attempt at creating the custom markup extension that will serve in this role:

[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<语言> 填充所提及的项目。这工作,因为到语言的实例由下面的代码行解决落后的引用:

In fact, this code almost works. As long as the referenced objects are declared in XAML before the objects that are referencing them, the ProvideValue method correctly returns an IEnumerable<Language> populated with the referenced items. This works because the backward references to the Language instances are resolved by the following code line:

var token = service.Resolve(item);



但是,如果XAML包含向前引用(因为语言的对象在国家的对象),它打破了,因为这是需要(显然)不能转换为修正标记语言

But, if the XAML contains forward references (because the Language objects are declared after the Country objects), it breaks because this requires fixup tokens which (obviously) cannot be cast to Language.

if (token == null)
{
    var names = new[] { item };
    token = service.GetFixupToken(names, true);
}



作为一个实验我试图返回的集合转换成收藏<对象> ; ,希望XAML会以某种方式解决了令牌之后,但在反序列化过程中抛出无效的转换异常

As an experiment I tried converting the returned collection to Collection<object> in the hope that XAML would somehow resolve the tokens later, but it throws invalid cast exceptions during deserialization.

任何人都可以提出如何最好地得到这个工作?

Can anyone suggest how best to get this working?

非常感谢,

Many thanks, Tim

推荐答案

您不能使用 GetFixupToken 的方法,因为它们返回可仅由被加工的内部型。现有的默认XAML架构环境下工作的XAML作家

You can't use the GetFixupToken methods because they return an internal type that can only be processed by the existing XAML writers that work under the default XAML schema context.

但是你可以用下面的办法来代替:

But you can use the following approach instead:

[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) {
        string[] items = Items.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
        return new IEnumerableWrapper(items, serviceProvider);
    }
    class IEnumerableWrapper : IEnumerable<Language>, IEnumerator<Language> {
        string[] items;
        IServiceProvider serviceProvider;
        public IEnumerableWrapper(string[] items, IServiceProvider serviceProvider) {
            this.items = items;
            this.serviceProvider = serviceProvider;
        }
        public IEnumerator<Language> GetEnumerator() {
            return this;
        }
        int position = -1;
        public Language Current {
            get {
                string name = items[position];
                // TODO use any possible methods to resolve object by name
                var rootProvider = serviceProvider.GetService(typeof(IRootObjectProvider)) as IRootObjectProvider
                var nameScope = NameScope.GetNameScope(rootProvider.RootObject as DependencyObject);
                return nameScope.FindName(name) as Language;
            }
        }
        public void Dispose() {
            Reset();
        }
        public bool MoveNext() { 
            return ++position < items.Length; 
        }
        public void Reset() { 
            position = -1; 
        }
        object IEnumerator.Current { get { return Current; } }
        IEnumerator IEnumerable.GetEnumerator() { return this; }
    }
}

这篇关于如何创建XAML标记扩展返回集合的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆