c# - 如何从 XAML 中的 CLR 命名空间映射类型,而不是声明它的程序集?

标签 c# wpf xaml namespaces

在 XAML 中,我想使用来自两个不同程序集的类型,每个程序集都有自己的命名空间。而不是在 xmlns:<xml-namespace>="<clr-namespace>" 中显式声明命名空间属性,我想使用 [XmlnsDefinition]将 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

In Gu.Units.Wpf.csproj I have this mapping:

[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 中像这样使用它:
<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>

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

The name "LengthUnit" does not exist in the namespace "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 的代码?

最佳答案

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

Having xmlns:units="clr-namespace:Gu.Units;assembly=Gu.Units" works fine but it is what I'm trying to avoid.

Is there a way to do this?



答案是:不,没有任何方法可以避免声明一些 XML namespace 前缀。
  • [XmlnsPrefix]属性对手动编写的 XAML 没有影响。 IE。它不会在 XAML 的范围内引入 xmlns 前缀。它只是一个 XAML 创作工具可以检索的标记,以便在它们自动生成 XAML 声明时,它们有办法选择要使用的 xmlns 前缀。例如。如果使用 VS Designer 从引用的库中添加新对象,它可以在该库中查找,知道在将对象添加到 XAML 时,需要添加适当的 xmlns:外部容器元素的属性,并在添加对象的 XAML 中使用指定的前缀。
  • 这些属性都对在指定这些属性的同一程序集中创作的 XAML 没有任何影响。这些属性仅在完全编译的程序集中有用,当然,在编译程序集中的 XAML 之前您不会得到它。这样 XAML 就不能使用这些属性。
  • 这留下了 [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"这将允许 units xmlns 前缀来限定来自附加到 http://Gu.com/Units 的任何 CLR 命名空间的任何类型。 URI 通过 [XmlnsDefinition]属性。
    但是您仍然必须声明 XML 命名空间前缀。只是声明采用了不同于其他方式所需的形式。

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

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

    编辑:

    根据您的评论:

    The problem I'm trying to solve is joining Gu.Units to the http://Gu.com/Units without adding a reference to System.Xaml for Gu.Units



    来自 the documentation :

    Apply one or more XmlnsDefinitionAttribute attributes to assemblies in order to identify the types within the assembly for XAML usage. [emphasis mine]



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

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

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

    WPF 团队 has acknowledged this to be a bug ,但已表示他们不会解决问题。

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

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

    解决此问题的一个不太明显的方法是使用 [TypeForwardedTo]属性。例如,在您的 Gu.Units.Wpf大会,你可以包括这个:
    [assembly: TypeForwardedTo(typeof(Gu.Units.LengthUnits))]
    

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

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

    关于c# - 如何从 XAML 中的 CLR 命名空间映射类型,而不是声明它的程序集?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33862121/

    相关文章:

    c# - 通过代码刷新 WPF-Control

    c# - Service Fabric - 为有状态服务调用命名分区

    c# - 在 C# 中使用补充 Unicode 字符作为 char 文字

    wpf - 我可以在 CompositionTarget.Rendering 中做什么?

    ios - iOS 上的 Xamarin.Forms ListView ViewCell 问题

    c# - 允许通过带有 stringformat 和 _ 的键进行 WPF 按钮导航

    wpf - 添加自定义触发器时如何保留 mahapps 地铁样式?

    .net - 如何获取 WPF 内置本地化字符串?

    xaml - WP8 中的 slider 组件不会移动

    c# - 在 MainActivity 中访问 MainPage.xaml 按钮