如何从XAML中的CLR命名空间映射类型而不是声明它的汇编?

在XAML中,我想使用来自两个不同程序集的类型,每个程序集都有自己的命名空间。 我不想在xmlns:=""属性中显式声明名称空间,而是使用[XmlnsDefinition]程序集属性将URI映射到这些类型的名称空间。

其中一个程序集本身与WPF无关,所以我想避免它引用与WPF相关的程序集,特别是System.Xaml.dll程序集,如果该程序集使用[XmlnsDefinition]属性。

我有一个像这样组织的Visual Studio解决方案:

 Gu.Units.sln
     Gu.Units.csproj //这里没有对System.Xaml的引用
     Gu.Units.Wpf.csproj //引用Gu.Units和System.Xaml

Gu.Units.Wpf.csproj我有这个映射:

 [assembly: XmlnsDefinition("http://Gu.com/Units", clrNamespace: "Gu.Units", AssemblyName = "Gu.Units")] [assembly: XmlnsDefinition("http://Gu.com/Units", clrNamespace: "Gu.Units.Wpf", AssemblyName = "Gu.Units.Wpf")] [assembly: XmlnsPrefix("http://Gu.com/Units", "units")] 

我试图在XAML中使用它,如下所示:

   

但由于某种原因,名称空间Gu.Units似乎被忽略了。 也就是说,它不包含在URI http://Gu.com/Units标识的XML命名空间中。 相反,我得到:

名称“LengthUnit”在命名空间“ http://Gu.com/Units ”中不存在。

XAML中的显式名称空间声明 – 即具有xmlns:units="clr-namespace:Gu.Units;assembly=Gu.Units" – 工作正常,但我也想避免这种情况。

有没有办法,我可以让我的Gu.Units.Wpf.dll程序集提供必要的[XmlnsDefinition]属性来映射Gu.Units.Wpf.dll程序[XmlnsDefinition]的命名空间,以便后者本身不需要对System.Xaml.dll的引用System.Xaml.dllSystem.Xaml.dll没有任何特定于XAML的代码?

如果我理解正确,你的问题归结为:

xmlns:units="clr-namespace:Gu.Units;assembly=Gu.Units"工作正常,但这是我想避免的。

有没有办法做到这一点?

答案:不,没有办法避免声明一些 XML名称空间前缀。

  1. [XmlnsPrefix]属性对手动创作的XAML没有影响。 即它不会在XAML的范围中引入xmlns前缀。 它只是XAML创作工具可以检索的标记,因此,如果它们自动生成XAML声明,则可以选择要使用的xmlns前缀。 例如,如果您使用VS Designer从引用的库中添加新对象,它可以查看该库以了解在将对象添加到XAML时,它需要将适当的xmlns:属性添加到外部容器元素,以及在添加了对象的XAML中使用指定的前缀。
  2. 这些属性都不会对在指定属性的同一程序集中创作的XAML产生任何影响。 这些属性仅在完全编译的程序集中有用,当然,在编译程序集中的XAML之前,您不会这样做。 这样XAML就无法使用这些属性。
  3. 当在包含当前正在编辑的XAML的程序集引用的程序[XmlnsDefinition]指定时,将[XmlnsDefinition]属性。 在这种情况下,该属性很有用,但仍然不允许您放弃xmlns:属性声明。 相反,它的作用是允许您将URI(例如http://Gu.com/Units )映射到一个或多个CLR名称空间。 通过这种方式,单个xmlns:前缀属性声明可以a)引用多个CLR命名空间,并且b)这样做而不需要创作的XAML必须实际命名任何CLR命名空间(即它封装了托管代码方面)程序集引用,隐藏在URI后面)。

    通过这种方式,您可以编写xmlns:units="http://Gu.com/Units"而不是编写xmlns:units="clr-namespace:Gu.Units;assembly=Gu.Units" ,这样就可以了units xmlns前缀用于限定通过[XmlnsDefinition]属性附加到http://Gu.com/Units URI的任何CLR名称空间中的任何类型。

    但是您仍然必须声明XML名称空间前缀。 只是声明采用与其他方式不同的forms。

注意:当多个程序集使用[XmlnsDefinition]属性在相同的URI中声明CLR名称空间时,所有名称空间都由XAML中引用所有这些程序集的URI引用。 您可以利用此function将您自己的库的命名空间加入到您希望已在XAML中引用的URI的名称空间(例如http://schemas.microsoft.com/winfx/2006/xaml/presentation )。

只要该URI实际上是由创作工具在XAML中发出的xmlns:属性中使用的,这就“解决”了您要问的问题。 但是将您自己的程序集命名空间与框架中预先存在的命名空间进行混淆是一种破坏并且不明智。 即使没有类型名称冲突,它仍然是一种不好的做法,当然如果存在类型名称冲突,它可能会导致严重的问题。

编辑:

根据你的评论:

我试图解决的问题是将Gu.Units加入http://Gu.com/Units而不添加对Gu.Unit的System.Xaml的引用。

从文档 :

将一个或多个XmlnsDefinitionAttribute属性应用于程序集,以便识别程序集中用于XAML用法的类型。 [强调我的]

即,您只能使用[XmlnsDefinition]来映射给定命名空间中的类型,这些类型实际上是在指定属性本身的同一程序[XmlnsDefinition]声明的。

该属性包含一个AssemblyName属性,它似乎表明您可以包含其他程序集中的类型。 这是文档的自然读取,可能是使用属性的意图。 不幸的是,框架无法控制其他代码如何使用该属性,而XAML工具确实忽略了它。

[XmlnsDefinition]属性只能用于将URI映射到在找到该属性的程序集中声明的名称空间。 您可以指定其他程序集名称,但Visual Studio中的XAML设计器和编译器不关注AssemblyName属性。

WPF团队已经承认这是一个错误 ,但已声明他们不会解决问题。

可以逐个类型地解决问题。 最明显的方法是在程序[XmlnsDefinition]声明一个新类型,其中指定了[XmlnsDefinition] ,从另一个程序集inheritance了您想要的类型。 例如,在Gu.Units.Wpf程序Gu.Units.Wpf ,您可以声明如下类型:

 public class LengthUnits : Gu.Units.LengthUnits { } 

然后你最终会使用Gu.Units.Wpf.LengthUnits类型而不是Gu.Units.LengthUnits 。 显然,只有当类型是未密封的引用类型时,这才有效。 此外,根据实际使用类型的方式,您可能会遇到代码试图使用Gu.Units.LengthUnits实例的Gu.Units.LengthUnits ,其中需要Gu.Units.LengthUnits的实例。 对于简单的单向绑定方案,这可能不会出现,但我可以很容易想象其他情况。

解决该问题的一种不太明显的方法是使用[TypeForwardedTo]属性。 例如,在Gu.Units.Wpf程序Gu.Units.Wpf ,您可以包含以下内容:

 [assembly: TypeForwardedTo(typeof(Gu.Units.LengthUnits))] 

这具有以下优点:所讨论的类型将是相同类型。 但是,这是一个运行时效果,并且与XAML设计器工具不能很好地兼容。 您的项目将构建并正确运行,但设计人员仍会抱怨“未找到”类型’单位:LengthUnits’

我不知道任何可以在命名空间的基础上更广泛地解决问题的解决方法。