当前位置: 首页 > 编程语言 > C#

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

时间:2023-04-10 18:53:17 C#

如何创建返回集合的XAML标记扩展尝试创建一个自定义标记扩展,允许使用对XAML中别处定义的集合的选定成员的引用来填充集合属性。下面是一个简化的XAML片段,展示了我的目标:每个Country对象的Languages属性将填充一个IEnumerable,其中包含对LanguageSelector中指定的Language对象的引用,这是自定义标记扩展。这是我尝试创建将在此角色中使用的自定义标记扩展:[ContentProperty("Items")][MarkupExtensionReturnType(typeof(IEnumerable))]publicclassLanguageSelector:MarkupExtension{publicLanguageSelector(stringitems){Items=项目;}[ConstructorArgument("items")]publicstringItems{get;放;}publicoverrideobjectProvideValue(IServiceProviderserviceProvider){varservice=serviceProvider.GetService(typeof(IXamlNameResolver))asIXamlNameResolver;varresult=newCollection();foreach(variteminItems.Split(new[]{','},StringSplitOptions.RemoveEmptyEntries).Select(item=>item.Trim())){vartoken=service.Resolve(item);if(token==null){varnames=new[]{item};token=service.GetFixupToken(names,true);}if(tokenisLanguage){result.Add(tokenasLanguage);}}返回结果;实际上,这段代码几乎可以工作。ProvideValue方法会正确返回填充有引用项的IEnumerable,只要引用的对象在XAML中声明在引用它们的对象之前。这是有效的,因为语言实例的反向引用由以下行解析:vartoken=service.Resolve(item);但是,如果XAML包含前向引用(因为语言对象是在Country对象之后声明的),它就会中断,因为这需要修复标记(显然)不能被强制转换为语言的问题。if(token==null){varnames=new[]{item};token=service.GetFixupToken(names,true);作为实验,我尝试将返回的集合转换为Collection,希望XAML以某种方式稍后解析令牌,但在反序列化过程中抛出了无效的强制转换异常。谁能建议如何最好地完成这项工作?非常感谢,Tim您不能使用GetFixupToken方法,因为它们返回的内部类型只能由在默认XAML架构上下文中工作的现有XAML编写器处理。但您可以使用以下方法:[ContentProperty("Items")][MarkupExtensionReturnType(typeof(IEnumerable))]publicclassLanguageSelector:MarkupExtension{publicLanguageSelector(stringitems){Items=items;}[ConstructorArgument("items")]publicstringItems{get;放;}publicoverrideobjectProvideValue(IServiceProviderserviceProvider){string[]items=Items.Split(new[]{','},StringSplitOptions.RemoveEmptyEntries);返回新的IEnumerableWrapper(items,serviceProvider);}类IEnumerableWrapper:IEnumerable,IEnumerator{string[]items;IServiceProvider服务提供者;publicIEnumerableWrapper(string[]items,IServiceProviderserviceProvider){this.items=items;this.serviceProvider=serviceProvider;}publicIEnumeratorGetEnumerator(){返回这个;}int位置=-1;publicLanguageCurrent{get{stringname=items[position];//TODO使用任何可能的方法按名称解析对象varrootProvider=serviceProvider.GetService(typeof(IRootObjectProvider))作为IRootObjectProvidervarnameScope=NameScope.GetNameScope(rootProvider.RootObjectasDependencyObject);将nameScope.FindName(name)作为语言返回;}}publicvoidDispose(){重置();}publicboolMoveNext(){return++position这是一个完整的工作项目,可以解决你的问题起初我建议在Country类上使用[XamlSetMarkupExtension]属性,但实际上你需要的只是XamlSchemaContext的前向名称解析.虽然此功能的文档在地面上非常薄,但您实际上可以告诉Xaml服务延迟目标元素,下面的代码显示了如何操作。请注意,即使在相反的示例中,所有语言名称都已正确解析。基本上,如果您需要一个无法解析的名称,请通过返回修复令牌来请求延迟。是的,正如Dmitry提到的,它对我们来说是不透明的,但这并不重要。当您调用GetFixupToken(...)时,您指定所需的名称列表。当这些名称可用时,您的标记扩展-ProvideValue将被再次调用。那时,它基本上是重建。此处未显示您还应该检查IXamlNameResolver上的布尔属性IsFixupTokenAvailable。如果名称实际上要在以后找到,这应该返回true。如果该值为false并且您仍然有未解析的名称,那么您应该使操作难以理解,可能是因为Xaml中给出的名称最终未解析。有些人可能会好奇地注意到这个项目不是WPF应用程序,即它不引用WPF库;您必须添加到此独立ConsoleApplication的唯一引用是System.Xaml。即使System.Windows.Markup(历史工件)有一个using语句也是如此。在.NET4.0中,XAML服务支持已从WPF(和其他地方)转移到核心BCL库中。恕我直言,这一变化使XAML服务成为前所未有的最佳BCL功能。没有更好的基础来开发以彻底的重新配置功能作为主要要求的大型系统级应用程序。这种“应用程序”的一个例子是WPF。使用系统;使用System.Collections.Generic;使用System.Collections.ObjectModel;使用System.IO;使用System.Linq;使用System.Windows.Markup;使用System.Xaml;命名空间测试{公共类语言{}公共类国家{公共IEnumerable语言{get;放;}}publicclassLanguageSelector:MarkupExtension{publicLanguageSelector(Stringitems){this.items=items;}字符串项;publicoverrideObjectProvideValue(IServiceProviderctx){varxnr=ctx.GetService(typeof(IXamlNameResolver))asIXamlNameResolver;vartmp=items.Split(new[]{',',''},StringSplitOptions.RemoveEmptyEntries).Select(s_lang=>new{s_lang,lang=xnr.Resolve(s_lang)asLanguage});varerr=tmp.Where(a=>a.lang==null).Select(a=>a.s_lang);返回err.Any()?xnr.GetFixupToken(err):tmp.Select(a=>a.lang).ToList();}};publicclassmyClass{Collection_l=newCollection();公共集合语言{get{return_l;}}收集tion_c=newCollection();公共收藏国家{get{return_c;}}//你必须在这里设置你的程序集的名字---vconststrings_xaml=@"";staticvoidMain(string[]args){varxxr=newXamlXmlReader(newStringReader(s_xaml));varxow=newXamlObjectWriter(newXamlSchemaContext());XamlServices.Transform(xxr,xow);myClassmc=(myClass)xow.Result;///在Xaml中使用前向引用}};[编辑...]因为我是XAML服务的新手,所以我可能一直在考虑下面是一个简单的解决方案,它允许您构建您想要的任何引用-完全在XAML中-仅扩展x:Array和x:带有内置标记的参考。不知何故,我没有意识到x:Reference不仅可以填充属性(因为它很常见:{x:Referencesome_name}),而且它本身也可以是XAML标记()。在任何一种情况下,它都充当对文档中其他对象的代理引用。这允许您使用对其他XAML对象的引用来填充x:Array,然后只需将该数组设置为您的属性的值。XAML解析器会根据需要自动解析转发的引用。为了试用它,这里有一个完整的控制台应用程序,它从前面的XAML文件实例化myClass对象。和以前一样,添加对System.Xaml.dll的引用并更改上面XAML的第一行以匹配您的程序集名称。以上就是C#学习教程的全部内容:如何创建返回集合的XAML标记扩展。如果对你有用,需要进一步了解C#学习教程,希望大家多多关注——usingSystem;使用System.Collections.Generic;使用System.Collections.ObjectModel;使用System.IO;使用System.Xaml;namespacetest{publicclassLanguage{}publicclassCountry{publicIEnumerableLanguages{get;放;}}publicclassmyClass{Collection_l=newCollection();公共集合语言{get{return_l;}}集合_c=新集合();公共收藏国家{get{return_c;}}staticvoidMain(){varxxr=newXamlXmlReader(newStreamReader("XMLFile1.xml"));varxow=newXamlObjectWriter(newXamlSchemaContext());XamlServices.Transform(xxr,xow);myClassmc=(myClass)xow.Result;}};}侵权,请点击右侧联系管理员删除。如需转载请注明出处: