XAML 中的花括号实际上是啥意思?

Posted

技术标签:

【中文标题】XAML 中的花括号实际上是啥意思?【英文标题】:What do curly braces in XAML actually mean?XAML 中的花括号实际上是什么意思? 【发布时间】:2021-12-24 09:31:08 【问题描述】:

我是一名资深的 C# 程序员,但我对 WPF 和 XAML 完全陌生。我可以找到很多教程说“这是如何实现这个特定的事情”,而不是“这是为什么你这样做是为了实现这个特定的事情”。我很难理解 XAML 中各种语法的含义。

在这种情况下,属性中的大括号实际上是什么意思?它们在代码中被翻译成什么?我如何推理它们?它们是如何被解释的?为什么似乎有多种语法(Binding="Binding someProperty" vs Binding="Binding path=someProperty")?

我肯定遗漏了一些明显的东西,但我确实花了几天时间阅读教程,观看教程,甚至在极度枯燥和难以理解的 Microsoft documentation 中奋力拼搏,但我似乎仍然无法理解出去。


让我试着说明一下我卡在哪里了。

例如,假设我得到了这个:

int result = SomeUnknownFunction(42, 79);

我不必知道SomeUnknownFunction 做了什么来推断这里发生的事情。我可以说,好的,第一部分定义了一个int 类型的新变量,第二部分运行带有两个输入的某个函数,等号将该函数的结果分配给该变量。

现在说我得到了这个:

<TextBox Text="Binding Name">

我可以说,好的,当 XAML 解析器到达这一位时,它吐出的 C# 代码会创建一个 TextBox 类型的对象实例。该类具有(或继承)一个名为 Text 的属性,我们将其设置为……呃……花括号内的一些神奇语法……

这就是我卡住的地方。我认为其中的一部分被称为“标记扩展”(这是可能的最令人难以置信的通用和无意义的名称),但我什至无法弄清楚这是否意味着“花括号和里面的所有东西”或只是“绑定部分”。有时花括号内有等号,有时没有。有什么不同? Binding 这个词是……什么?它是一个函数吗?我们传入Name,它返回一个新的Binding 对象,该对象以某种方式分配给Binding 属性?

【问题讨论】:

这里有描述:docs.microsoft.com/en-us/dotnet/desktop/wpf/data/… @Dai 谢谢,但我不想具体了解绑定,我想了解大括号语法。这是“这是如何实现这个特定的事情” 的另一个实例,而没有解释为什么 那是如何实现那个特定的事情。另外为什么要在标题中加上“in XAML”?这就是标签的用途。 我编辑了您的标题,因为我的阅读内容是“C# 中的花括号是干什么用的?”这不是你要问的。 @Dai 好吧...不,我认为我并不真正关心 XAML 编译器的工作原理,我真的只想知道花括号的实际效果是什么。我想了解如何推断效果。我已经编辑了我的问题以澄清我的困惑。或者只是让它成为一个更糟糕的问题,我不确定。我在这里挣扎。 获取一份 Adam Nathan 的 WPF 书籍 (WPF Unleashed)。直到我读了第一章(?)我才开始真正了解 XAML 【参考方案1】:

我想这解释了大括号的语法含义:

“标记扩展是一种 XAML 技术,用于获取以下值 不是原始的或特定的 XAML 类型。对于属性使用,标记 扩展使用左花括号的已知字符序列 进入标记扩展范围,右花括号 进入 退出。” (Overview of markup extensions for XAML)


“我认为其中的某些部分称为“标记扩展”(即 可能是最令人难以置信的通用和无意义的名称),但我 甚至无法弄清楚这是否意味着“花括号和一切 内部”或只是“绑定部分”“

试图解释它,同时保持简单

XAML 是一种基于某些语义规则的标记语言。由于非常先进的处理器,XAML 具有一组动态的标记/对象​​。使用声明的命名空间,XAML 处理器能够将 XAML 元素映射到实际的 C# 对象。 XAML 允许使用像Trigger 这样的特殊对象来实现一些简单的逻辑。但是当涉及到复杂的逻辑和动态值时,该语言或一般的标记语言非常有限。

标记语言是静态的,因为它们需要静态值。例如,在 C# 中,您可以实现基于在运行时动态计算的变量(充当数据的占位符)的算法。在标记中,所有值都是在您编写标记时静态提供的。 这个想法是使用标记扩展来启用动态值计算。

例如,您无法单独使用标记来实现像 WPF 绑定引擎这样复杂的动态逻辑。数据绑定是一个允许将动态数据值“注入”到标记中的概念(简单地说)。数据绑定是通过标记扩展的概念来实现的。

扩展 XAML 标记功能的 WPF 方法是引入标记扩展的概念。从这个角度来看,“标记扩展”这个名字非常好。其他标记语言试图通过向处理器添加更多功能(扩展语言语法和语义)或通过动态修改/覆盖实际标记来实现类似的功能。相比之下,标记扩展更强大,因为它们真正扩展了标记超出了处理器的限制。这些规则现在由 .NET 编译器而非标记处理器决定。

要实现标记扩展,对象必须扩展抽象类MarkupExtension。实现通常有一个构造函数并且可以提供额外的公共属性。 MarkupExtension 公开了一个抽象方法 MarkupExtension.ProvideValue,由 XAML 处理器调用以获得由实现者提供的动态计算值。 一个著名的扩展是Binding 类。 Binding 扩展了 MarkupExtension 并公开了其他属性,例如 SourceConverterPath 等。

XAML 处理器预期的语法是

<markup_extension_class_name> constructor_value, <markup_extension_property>=property_value

点赞&lt;TextBlock Text="Binding Username, ElementName=MyControl" /&gt;

处理器正在处理标记&lt;TextBlock

处理器在TextBlock 元素上找到属性Text 和分配的值(表达式)。

值的大括号告诉处理器,左大括号和右大括号之间的内容是一个对象 扩展MarkupExtension

XAML 引擎将创建此对象的实例,在本例中为 Binding 类,使用 构造函数参数Username 并初始化其属性 ElementName 的值为 "MyControl"

XAML 引擎然后将使用构造的实例调用MarkupExtension.ProvideValue 方法来获取动态计算的值。在这种情况下,XAML 引擎将创建一个TextBlock 实例并将Binding 标记扩展的动态计算结果分配给TextBlock.Text 属性

XAML 处理器的一个特殊规则是,如果扩展的类名以 "Extension" 为后缀。即使实际标记省略了 "Extension" 后缀,处理器仍然能够识别该类。例如,如果类名为 BindingExtension,您可以简化名称并在标记中写入 Binding

通过实现Binding 标记扩展,相当简单的标记代码扩展到高度复杂的 WPF 框架,以将绑定引擎“连接”到 XAML 语言。

遗憾的是,除了 Microsoft 官方文档之外,我无法推荐其他好的来源。除了您在问题中提供的链接或其他答案建议的链接之外,您还可以阅读MarkupExtension.ProvideValue 属性和MarkupExtension 类的有用注释。 我还建议实现您自己的非常简单的标记扩展,或者查看您可以在网上找到的现有实现以了解这个想法。标记扩展非常易于使用。

【讨论】:

这是一个很好的答案,谢谢!实际上我自己写了很长的答案(我认为是正确的)但是由于长时间停电我无法发布它,然后因为是周末而忘记了。我即将发布它,因为我认为它以更好的方式为完整的初学者解释了一些概念,但我认为这两个答案都值得一读。 WRT 名称“标记扩展”,我仍然认为这是一个非常糟糕的名称。当然,它是对标记的扩展,但尚不清楚以何种方式。它应该被称为“动态价值”或更有意义的东西。可以这么说,“牛奶”与“液体”相比。【参考方案2】:

这是 XAML 文件语法的一部分,您可以在此处阅读所有血腥细节

Markup Extensions

XAML 定义了一个标记扩展编程实体,它支持 摆脱对字符串属性的正常 XAML 处理器处理 值或对象元素,并将处理推迟到支持 班级。标识 XAML 的标记扩展的字符 使用属性语法时的处理器是左大括号 (), 后跟除右大括号 () 以外的任何字符。这 左大括号后面的第一个字符串必须引用 提供特定扩展行为的类,其中 如果子字符串是一部分,reference 可能会省略子字符串“Extension” 的真实类名。此后,可能会出现一个空格,并且 然后每个后续字符都用作扩展名的输入 实现,直到遇到右花括号。

更多信息在这里

Overview of markup extensions for XAML

@Charlieface 提供的另一个很好的链接

Basic Markup Extension Syntax

可以实现标记扩展来为属性提供值 在属性用法中,在属性元素用法中的属性,或 两者都有。

当用于提供属性值时,区分的语法 XAML 处理器的标记扩展序列是 打开和关闭花括号( 和 )。标记的类型 然后扩展名由紧随其后的字符串标记标识 左花括号。

在属性元素语法中使用时,标记扩展是可视的 与用于提供属性元素的任何其他元素相同 value:引用标记扩展的 XAML 元素声明 类作为元素,括在尖括号 () 中。

【讨论】:

好的,我已经阅读了该页面。这是....好吧,这是热垃圾。如果他们尝试过,他们不可能让它变得更加模糊。这就像试图阅读规范文档。 “左大括号后面的第一个字符串必须引用提供特定扩展行为的类,如果该子字符串是真实类名的一部分,则该引用可以省略子字符串“Extension”。” Wtf .我不知道那是什么意思。我对编程并不陌生,也无法理解它,那么实际上新手应该如何解决这个问题?它令人难以置信。 @Clonkex 我能感受到你的痛苦,也理解你的沮丧。 这可能是一个更好的链接docs.microsoft.com/en-us/dotnet/desktop/wpf/advanced/… 就像属性一样。您可以说 [KeyAttribute] 或 [Key],它们都以类 KeyAttribute 为目标。但是,如果您定义一个 KeyAttributeAttribute,那么第一个将最好绑定到它。【参考方案3】:

他们说在互联网上获得正确答案的最佳方法是发布一个不正确的答案,所以让我们试一试吧。

我将以此为例:

<TextBox x:Name="MyTextBox" Text="Some default text :)"/>
<Label Content="Binding Text, ElementName=MyTextBox"/>

这会将Label 类的Content 属性绑定到TextBox 类的Text 属性,从而得到以下结果:


这是如何工作的

当 XAML 编译器运行时,&lt;Label ... /&gt; 将被转换为在运行时生成 Label 类实例的代码。我们可以通过 XAML 设置该实例的属性。通常,当您使用Attribute syntax 设置属性时,双引号内的字符串正是该属性将被设置的内容。例如:

<Label Content="Binding Text, ElementName=MyTextBox"/>

由于我们没有使用花括号,因此该字符串将按字面意思作为分配给Content 属性的值:

这是我们到目前为止所学到的:

如果我们添加花括号,现在 XAML 编译器会以不同的方式处理它。花括号中第一个空格之前的单词现在被视为标记扩展名的名称。标记扩展是继承自 MarkupExtension 类的类。

在这种情况下,花括号中文本的第一部分是Binding。 By convention,标记扩展类通常以“扩展”结尾,如BindingExtension1。 XAML 很智能,允许您在输入大括号中的标记扩展名时省去类名的 Extension 部分,从而节省一些输入并减少冗长。

所以现在我们知道花括号和花括号内字符串的第一部分是什么意思了,但剩下的呢?为什么有有时等号有时没有?

文本Binding 之后有一个空格,字符串的其余部分被视为标记扩展的逗号分隔输入。这在实践中如何运作?好吧,如果第一个输入没有等号,它们将作为字符串传递到标记扩展类的构造函数中。2

在我们的示例中,Binding 类将使用参数值Text 创建。如果您查看source for the Binding class,您会看到有两个构造函数。接受字符串的那个将Path 属性设置为该字符串的值:

        /// <summary>
        /// Default constructor.
        /// </summary>
        public Binding() 
 
        /// <summary>
        /// Convenience constructor.  Sets most fields to default values.
        /// </summary>
        /// <param name="path">source path </param>
        public Binding(string path)
        
            if (path != null)
            
                if (System.Windows.Threading.Dispatcher.CurrentDispatcher == null)
                    throw new InvalidOperationException();  // This is actually never called since CurrentDispatcher will throw if null.
 
                Path = new PropertyPath(path, (object[])null);
            
        

在使用您提供的任何构造函数参数实例化类之后,它会将剩余的等号分隔的输入视为键值对。键是标记扩展上公共属性的名称,值是该公共属性设置的值。

如果您再次查看Binding class' source,您会看到有一个名为ElementName 的公共属性。在我们的示例中,ElementName 被设置为 MyTextBox 的文字字符串值。


总结

花括号告诉 XAML 编译器将这些花括号的内容视为继承自 MarkupExtension 的类的名称,后跟一个空格,然后是构造函数参数(如果有),然后是键值表示公共属性名称及其值的对。

然后将通过在扩展MarkupExtension 的类上调用ProvideValue 方法来计算该属性的值。


注意事项

1.Binding 类是此规则的一个例外,实际上只是称为 Binding,没有“扩展”部分。

2. 我不确定如果没有匹配的构造函数会发生什么。


免责声明:我完全不知道我在说什么。如有错误请指正!

【讨论】:

以上是关于XAML 中的花括号实际上是啥意思?的主要内容,如果未能解决你的问题,请参考以下文章

Python0:4是啥意思?

Scala方法调用中的花括号[重复]

python 一条语句后加个中括号是啥意思a=b[b==0]?

使用 Kotlin 的 RxJava 中的花括号和普通括号有啥区别

函数的 JavaScript 参数中的花括号

JSP中的美元花括号$是啥意思?