附加属性引起的奇怪WPF错误

3
我看到了奇怪的行为。我强烈感觉这是 .net 工具链中的一个 bug。
复现步骤:
1. 创建 WPF 应用程序。 2. 在该解决方案中创建类库。 3. 在类库中定义一些公共类(可以为空)。 4. 在 WPF 应用程序中定义一些具有附加属性和 Window 作为目标类的类。 5. 将其附加到 Window 并提供值。
然后你会得到错误:
“The object 'Window' already has a child and cannot add ''. 'Window' can accept only one child.”
如果你更改结构,使具有附加属性和值类在同一程序集中(可以是 Class Library 或 Wpf Application),它就可以正常工作。
如果你将代码放在内容之后也可以正常工作。但如果你把代码放在内容之前,就会产生错误。
<Window x:Class="WpfApplication9.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:my="clr-namespace:ClassLibrary1;assembly=ClassLibrary1"
        xmlns:my2="clr-namespace:ClassLibrary1"
        Title="MainWindow" Height="350" Width="525">

    <my2:Property.MyProperty>
        <my:ValueClass />
    </my2:Property.MyProperty>

    <Grid>

    </Grid>
</Window>

这并不会产生任何效果:
<Grid>

</Grid>    

<my2:Property.MyProperty>
    <my:ValueClass />
</my2:Property.MyProperty>

有什么想法吗?

谢谢!

1个回答

3
这是因为XAML被编译的方式是自我引用的。尽管你在命名空间中引用了包含附加属性的程序集,即应用程序程序集,但正是该程序集正在编译过程中,此时你需要XAML编译器决定元素语法引用的是一个附加属性而不是普通元素。
如您所发现的那样,将附加属性放在内容后面已经足够让它做出正确选择,即使信息不完整也是如此。另一方面,如果知道这是问题所在,还可以将附加属性放入外部程序集中,例如控件库,这也将避免鸡和蛋问题。
在完美的世界里,C#编译器和XAML编译器将完全集成在一起,这个问题就会消失,但在此之前我们必须使用解决方法。
编辑:再试一次解释一下,因为这有点复杂。
当您编译应用程序程序集时,它包含了C#和XAML。在上面的示例代码中,XAML引用了同一程序集的C#代码中定义的附加属性。因此,XAML编译器需要具有已编译版本的应用程序程序集,以便知道Property.MyProperty是一个附加属性。但是C#编译器无法编译您的应用程序程序集,因为XAML尚未编译。这是鸡和蛋问题:C#和XAML相互依赖。
必须有所作为,因此XAML编译器继续尝试使用不完整的信息来编译应用程序的XAML文件,即使应用程序程序集尚未被编译。在这个过程中,它可能会犯错误,比如无法检测到同一程序集中定义的附加属性。为什么有时能工作呢?我们可以注意到他们已经花了一些心思来处理一些常见情况,因此它似乎大部分时间都能正常工作,但正如您发现的那样,并不总是有效。
最简单的解决方案是将附加属性移动到外部程序集中。像控件一样的附加属性通常与库相关联,因此通常不会出现问题。

@Andrey:正如我所提到的,他们选择处理这个“常见情况”,可能是因为它很容易,而且不会涉及到解决与外部程序集和半编译程序集之间的相互依赖关系。我不能确定具体原因;这与工具内部有关。 - Rick Sladkey
@Rick Sladkey 那就是一个 bug :) 至少错误文本不充分。 - Andrey
@Andrey:我之前见过这个问题(确切的错误)和之前的问题(与XAML定义在同一程序集中的内容)。这是一个更普遍的问题。对于你的问题,让我注意到的是即使应用程序程序集的源代码包含语法错误,错误消息也完全相同,也就是说,它甚至无法编译。这表明它甚至没有查看应用程序程序集。 - Rick Sladkey
@Andrey:是的,你可以说这是一个bug,但如果它能产生更好的错误信息,那么它就可以修复这个bug! - Rick Sladkey
1
@Rick Sladkey,就我个人而言,我并没有看到任何问题。 XAML编译器只能将XAML翻译成C#,而无法验证其内容。然后,在编译期间,C#编译器可以解决所有相互依赖的问题,因为它已经具备了这种能力。对我来说,这就像是XAML编译器咬了更多的东西,而不能咀嚼。 - Andrey
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接