Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/303.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何创建返回集合的XAML标记扩展_C#_Xaml_Collections_Markup Extensions - Fatal编程技术网

C# 如何创建返回集合的XAML标记扩展

C# 如何创建返回集合的XAML标记扩展,c#,xaml,collections,markup-extensions,C#,Xaml,Collections,Markup Extensions,我正在为对象图(WPF/Silverlight之外)使用XAML序列化,并尝试创建一个自定义标记扩展,该扩展将允许使用对XAML中别处定义的集合的选定成员的引用来填充集合属性 下面是一个简化的XAML片段,演示了我的目标: <myClass.Languages> <LanguagesCollection> <Language x:Name="English" /> <Language x:Name="French"

我正在为对象图(WPF/Silverlight之外)使用XAML序列化,并尝试创建一个自定义标记扩展,该扩展将允许使用对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>
但是,如果XAML包含前向引用(因为语言对象是在国家对象之后声明的),它就会中断,因为这需要修复标记,而修复标记(显然)不能转换为语言

作为一个实验,我尝试将返回的集合转换为集合,希望XAML稍后能够以某种方式解析令牌,但它在反序列化过程中抛出无效的强制转换异常

有谁能建议如何最好地让这个工作

非常感谢,, Tim

无法使用这些方法,因为它们返回的内部类型只能由在默认XAML架构上下文下工作的现有XAML编写器处理

但您可以使用以下方法:

[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; }
    }
}
[ContentProperty(“项目”)]
[MarkupExtensionReturnType(typeof(IEnumerable))]
公共类语言选择器:MarkupExtension{
公共语言选择器(字符串项){
项目=项目;
}
[施工争议(“项目”)]
公共字符串项{get;set;}
公共覆盖对象ProviderValue(IServiceProvider服务提供程序){
string[]items=items.Split(新[]{',},StringSplitOptions.RemoveEmptyEntries);
返回新的IEnumerableRapper(项目、服务提供商);
}
类IEnumerableRapper:IEnumerable,IEnumerator{
字符串[]项;
IServiceProvider服务提供商;
公共IEnumerableRapper(字符串[]项,IServiceProvider服务提供程序){
这个项目=项目;
this.serviceProvider=serviceProvider;
}
公共IEnumerator GetEnumerator(){
归还这个;
}
int位置=-1;
公共语言流动{
得到{
字符串名称=项目[位置];
//TODO使用任何可能的方法按名称解析对象
var rootProvider=serviceProvider.GetService(typeof(IRootObjectProvider))作为IRootObjectProvider
var nameScope=nameScope.GetNameScope(rootProvider.RootObject作为DependencyObject);
返回nameScope.FindName(name)作为语言;
}
}
公共空间处置(){
重置();
}
公共bool MoveNext(){
return++位置
这是一个完整的、有效的项目,可以解决您的问题。起初我打算建议在
Country
类上使用
[XamlSetMarkupExtension]
属性,但实际上您只需要
XamlSchemaContext
的前向名称解析

尽管该特性的文档非常少,但实际上您可以告诉Xaml服务延迟目标元素,下面的代码将演示如何执行。请注意,即使示例中的部分颠倒了,所有语言名称都得到了正确解析

基本上,如果您需要一个无法解析的名称,您可以通过返回一个fixup令牌来请求延迟。是的,正如Dmitry提到的,这对我们来说是不透明的,但这并不重要。调用
GetFixupToken(…)
时,将指定所需名称的列表。您的标记扩展名-
提供了DEVALUE
,当这些名称可用时,稍后将再次调用该扩展名。在这一点上,它基本上是一个重复

此处未显示的是,您还应该检查
ixamlnamesolver
上的
Boolean
属性
IsFixupTokenAvailable
。如果以后确实可以找到这些名称,则应返回
true
。如果该值为
false
,并且您仍然有未解析的名称,那么您应该努力使操作失败,这可能是因为Xaml中给出的名称最终无法解析

有些人可能会好奇地注意到,这个项目不是WPF应用程序,也就是说,它没有引用WPF库;您必须添加到此独立控制台应用程序的唯一参考是
System.Xaml
。这是正确的,即使对于
System.Windows.Markup
(历史工件)存在
使用
语句。在.NET4.0中,XAML服务支持从WPF(和其他地方)转移到核心BCL库中

我的意思是,这一变化使得XAML服务成为了前所未有的最强大的BCL功能。没有一个更好的基础来开发一个大系统级应用程序,它具有作为基本需求的根本重构能力。WPF就是这种“应用程序”的一个例子

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Windows.Markup;
using System.Xaml;

namespace test
{
    public class Language { }

    public class Country { public IEnumerable<Language> Languages { get; set; } }

    public class LanguageSelector : MarkupExtension
    {
        public LanguageSelector(String items) { this.items = items; }
        String items;

        public override Object ProvideValue(IServiceProvider ctx)
        {
            var xnr = ctx.GetService(typeof(IXamlNameResolver)) as IXamlNameResolver;

            var tmp = items.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries)
                           .Select(s_lang => new
                            {
                                s_lang,
                                lang = xnr.Resolve(s_lang) as Language
                            });

            var err = tmp.Where(a => a.lang == null).Select(a => a.s_lang);
            return err.Any() ? 
                    xnr.GetFixupToken(err) : 
                    tmp.Select(a => a.lang).ToList();
        }
    };

    public class myClass
    {
        Collection<Language> _l = new Collection<Language>();
        public Collection<Language> Languages { get { return _l; } }

        Collection<Country> _c = new Collection<Country>();
        public Collection<Country> Countries { get { return _c; } }

        // you must set the name of your assembly here ---v
        const string s_xaml = @"
<myClass xmlns=""clr-namespace:test;assembly=ConsoleApplication2""
         xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"">

    <myClass.Countries> 
        <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'}"" /> 
    </myClass.Countries> 

    <myClass.Languages>
        <Language x:Name=""English"" /> 
        <Language x:Name=""French"" /> 
        <Language x:Name=""Italian"" /> 
    </myClass.Languages> 

</myClass>
";
        static void Main(string[] args)
        {
            var xxr = new XamlXmlReader(new StringReader(s_xaml));
            var xow = new XamlObjectWriter(new XamlSchemaContext());
            XamlServices.Transform(xxr, xow);
            myClass mc = (myClass)xow.Result;   /// works with forward references in Xaml
        }
    };
}
为了试用,这里有一个完整的控制台应用程序,它实例化了前面XAML文件中的
myClass
对象。与前面一样,添加对
System.Xaml.dll
的引用,并更改上面Xaml的第一行以匹配程序集名称

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Xaml;

namespace test
{
    public class Language { }

    public class Country { public IEnumerable<Language> Languages { get; set; } }

    public class myClass
    {
        Collection<Language> _l = new Collection<Language>();
        public Collection<Language> Languages { get { return _l; } }

        Collection<Country> _c = new Collection<Country>();
        public Collection<Country> Countries { get { return _c; } }

        static void Main()
        {
            var xxr = new XamlXmlReader(new StreamReader("XMLFile1.xml"));
            var xow = new XamlObjectWriter(new XamlSchemaContext());
            XamlServices.Transform(xxr, xow);
            myClass mc = (myClass)xow.Result;
        }
    };
}
使用系统;
使用System.Collections.Generic;
使用System.Collections.ObjectModel;
使用System.IO;
使用System.Xaml;
名称空间测试
{
公共类语言{}
公共类国家{public IEnumerable Languages{get;set;}}
公共类myClass
{
集合_l=新集合();
公共集合语言{get{return}
集合_c=新集合();
公共收集国家{get{return}
静态void Main()
{
var xxr=新的XamlXmlReader(新的StreamReader
[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; }
    }
}
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Windows.Markup;
using System.Xaml;

namespace test
{
    public class Language { }

    public class Country { public IEnumerable<Language> Languages { get; set; } }

    public class LanguageSelector : MarkupExtension
    {
        public LanguageSelector(String items) { this.items = items; }
        String items;

        public override Object ProvideValue(IServiceProvider ctx)
        {
            var xnr = ctx.GetService(typeof(IXamlNameResolver)) as IXamlNameResolver;

            var tmp = items.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries)
                           .Select(s_lang => new
                            {
                                s_lang,
                                lang = xnr.Resolve(s_lang) as Language
                            });

            var err = tmp.Where(a => a.lang == null).Select(a => a.s_lang);
            return err.Any() ? 
                    xnr.GetFixupToken(err) : 
                    tmp.Select(a => a.lang).ToList();
        }
    };

    public class myClass
    {
        Collection<Language> _l = new Collection<Language>();
        public Collection<Language> Languages { get { return _l; } }

        Collection<Country> _c = new Collection<Country>();
        public Collection<Country> Countries { get { return _c; } }

        // you must set the name of your assembly here ---v
        const string s_xaml = @"
<myClass xmlns=""clr-namespace:test;assembly=ConsoleApplication2""
         xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"">

    <myClass.Countries> 
        <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'}"" /> 
    </myClass.Countries> 

    <myClass.Languages>
        <Language x:Name=""English"" /> 
        <Language x:Name=""French"" /> 
        <Language x:Name=""Italian"" /> 
    </myClass.Languages> 

</myClass>
";
        static void Main(string[] args)
        {
            var xxr = new XamlXmlReader(new StringReader(s_xaml));
            var xow = new XamlObjectWriter(new XamlSchemaContext());
            XamlServices.Transform(xxr, xow);
            myClass mc = (myClass)xow.Result;   /// works with forward references in Xaml
        }
    };
}
<myClass xmlns="clr-namespace:test;assembly=ConsoleApplication2"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <myClass.Countries>
        <Country x:Name="UK">
            <Country.Languages>
                <x:Array Type="Language">
                    <x:Reference Name="English" />
                </x:Array>
            </Country.Languages>
        </Country>
        <Country x:Name="France">
            <Country.Languages>
                <x:Array Type="Language">
                    <x:Reference Name="French" />
                </x:Array>
            </Country.Languages>
        </Country>
        <Country x:Name="Italy">
            <Country.Languages>
                <x:Array Type="Language">
                    <x:Reference Name="Italian" />
                </x:Array>
            </Country.Languages>
        </Country>
        <Country x:Name="Switzerland">
            <Country.Languages>
                <x:Array Type="Language">
                    <x:Reference Name="English" />
                    <x:Reference Name="French" />
                    <x:Reference Name="Italian" />
                </x:Array>
            </Country.Languages>
        </Country>
    </myClass.Countries>
    <myClass.Languages>
        <Language x:Name="English" />
        <Language x:Name="French" />
        <Language x:Name="Italian" />
    </myClass.Languages>
</myClass>
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Xaml;

namespace test
{
    public class Language { }

    public class Country { public IEnumerable<Language> Languages { get; set; } }

    public class myClass
    {
        Collection<Language> _l = new Collection<Language>();
        public Collection<Language> Languages { get { return _l; } }

        Collection<Country> _c = new Collection<Country>();
        public Collection<Country> Countries { get { return _c; } }

        static void Main()
        {
            var xxr = new XamlXmlReader(new StreamReader("XMLFile1.xml"));
            var xow = new XamlObjectWriter(new XamlSchemaContext());
            XamlServices.Transform(xxr, xow);
            myClass mc = (myClass)xow.Result;
        }
    };
}