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

Joh*_*son 5 c# wpf xaml namespaces

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

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

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

Gu.Units.sln
    Gu.Units.csproj // no ref to System.Xaml here
    Gu.Units.Wpf.csproj // references Gu.Units and 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")]
Run Code Online (Sandbox Code Playgroud)

我试图像这样在XAML中使用它:

<UserControl x:Class="Gu.Units.Wpf.Demo.Sample"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:units="http://Gu.com/Units">
    <Label Content="{x:Static units:LengthUnit.Millimetres}" />
</UserControl>
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

Pet*_*iho 8

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

xmlns:units="clr-namespace:Gu.Units;assembly=Gu.Units"作品不错,但它是我想要避免的。

有没有办法做到这一点?

答案是:不,没有任何方法可以避免声明一些XML 名称空间前缀。

  1. [XmlnsPrefix]属性对手动编写的 XAML 没有影响。即它引入了xmlns前缀XAML的范围。它只是一个 XAML 创作工具可以检索的标记,以便在它们自动生成 XAML 声明时,它们有办法选择要使用的 xmlns 前缀。例如,如果您使用 VS 设计器从引用的库中添加一个新对象,它可以在该库中查看以了解在将对象添加到 XAML 时,需要将适当的xmlns:属性添加到外部容器元素,并使用在添加对象的 XAML 中指定的前缀。

  2. 这些属性都对在指定这些属性的同一程序集中创作的 XAML 没有任何影响。这些属性仅在完全编译的程序集中有用,当然,在编译程序集中的 XAML 之前您不会得到它。这样 XAML 就不能使用这些属性。

  3. [XmlnsDefinition]如果在包含当前正在编辑的 XAML 的程序集所引用的程序集中指定该特性,则会留下该特性。在这种情况下,属性很有用,但仍然不允许您放弃xmlns:属性声明。相反,它的作用是允许您将一个 URI(例如http://Gu.com/Units)映射到一个或多个 CLR 名称空间。通过这种方式,单个xmlns:前缀属性声明可以a)引用多个 CLR 命名空间,并且b)这样做而无需编写的 XAML 必须实际具体命名任何 CLR 命名空间(即它封装了程序集的托管代码方面)引用,将其隐藏在 URI 后面)。

    这样xmlns:units="clr-namespace:Gu.Units;assembly=Gu.Units",你可以写,而不是写xmlns:units="http://Gu.com/Units"这将允许unitsxmlns 前缀限定来自http://Gu.com/Units通过[XmlnsDefinition]属性附加到URI 的任何 CLR 命名空间的任何类型。

    但是您仍然必须声明 XML 命名空间前缀。只是声明采用了不同于其他方式所需的形式。


注意:当多个程序集使用该[XmlnsDefinition]特性在彼此相同的 URI 中声明 CLR 命名空间时,所有命名空间都由 XAML 中引用所有这些程序集的 URI 引用。您可以利用这一点将您自己的库的命名空间与您期望已在 XAML 中引用的 URI 的命名空间(例如http://schemas.microsoft.com/winfx/2006/xaml/presentation)连接起来。

只要该 URI 实际上在xmlns:创作工具在 XAML 中发出的属性中使用,就可以“解决”您所询问的问题。但是将您自己的程序集名称空间与框架中预先存在的名称空间混为一谈是一种黑客行为,而且是不明智的。即使没有类型名冲突,这仍然是一个糟糕的做法,当然如果有类型名冲突,它会导致严重的问题。


编辑:

根据您的评论:

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

文档

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

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

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

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

WPF 团队承认这是一个错误,但表示他们不会修复该问题。


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

public class LengthUnits : Gu.Units.LengthUnits { }
Run Code Online (Sandbox Code Playgroud)

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

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

[assembly: TypeForwardedTo(typeof(Gu.Units.LengthUnits))]
Run Code Online (Sandbox Code Playgroud)

这样做的优点是所讨论的类型将是相同的类型。但是,这是一种运行时效果,不能很好地与 XAML 设计器工具配合使用。您的项目将正确构建和运行,但设计人员仍会抱怨“未找到类型 'units:LengthUnits'”

我不知道有什么解决方法可以在命名空间的基础上更广泛地解决这个问题。