这是一个完整且有效的项目,可以解决您的问题。起初我打算建议[XamlSetMarkupExtension]
在你的类上使用该属性Country
,但实际上你所需要的只是XamlSchemaContext
' 的前向名称解析。
尽管该功能的文档非常薄,但实际上您可以告诉Xaml 服务延迟您的目标元素,以下代码显示了如何执行此操作。请注意,即使您的示例中的部分被颠倒了,您的所有语言名称都会得到正确解析。
基本上,如果您需要一个无法解析的名称,您可以通过返回一个修复令牌来请求延期。是的,正如德米特里所说,这对我们来说是不透明的,但这并不重要。当您调用 时GetFixupToken(...)
,您将指定您需要的名称列表。您的标记扩展(即 <code>ProvideValue,即)稍后将在这些名称可用时再次调用。在这一点上,它基本上是一个重做。
此处未显示的是您还应该Boolean
检查IsFixupTokenAvailable
. IXamlNameResolver
如果以后确实要找到名称,则应该返回true
. 如果值是false
并且您仍然有未解析的名称,那么您应该硬失败操作,大概是因为 Xaml 中给出的名称最终无法解析。
有些人可能会好奇地注意到这个项目不是WPF 应用程序,即它不引用 WPF 库;您必须添加到此独立ConsoleApplication的唯一参考是System.Xaml
. 即使有(历史文物)的using
声明也是如此。System.Windows.Markup
正是在 .NET 4.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 Services,我可能想多了。下面是一个简单的解决方案,它允许您建立所需的任何引用——完全在 XAML中——仅使用内置标记扩展x:Array
和x:Reference
.
不知何故,我没有意识到它不仅可以x:Reference
填充属性(如常见的{x:Reference some_name}
那样:),而且它本身也可以作为 XAML 标记(<Reference Name="some_name" />
)。在任何一种情况下,它都充当对文档中其他位置的对象的代理引用。这允许您x:Array
使用对其他 XAML 对象的引用来填充一个,然后只需将该数组设置为您的属性的值。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>
要试用它,这里有一个完整的控制台应用程序,它myClass
从前面的 XAML 文件中实例化对象。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;
}
};
}