为什么DataTemplate.LoadContent()不尊重模板定义的触发器?

TL-DR版本:

我们试图找出触发器生效的DataTemplate的自动应用与手动调用触发器无效的DataTemplate.LoadContent()之间的区别。

现在细节……

但首先,我首先要说的是,这个问题是为了帮助我们理解框架及其在内部所做的工作,因此,相关代码严格来说明问题本身,并不代表我们的实际代码。 正如他们所说,它仅用于说明目的。 (只是试图避免不可避免的’我不明白你想要做什么’或’那不是我会怎么做’的回应。再一次,它只是为了支持这个问题。希望这是有道理的。)

也就是说,考虑这个XAML定义一个String的DataTemplate有两个触发器(每个触发器都针对不同的元素)……

xmlns:system="clr-namespace:System;assembly=mscorlib" ...              

然后在XAML中该模板在范围内的另一个位置,我们有…

  

……按预期工作。 在代码中,我们可以像这样访问扩展模板(Border)的根元素…

 var expandedTemplateRootElement = VisualTreeHelper.GetChild(TestPresenter, 0) as FrameworkElement; 

……但触发器的应用方式和位置如何? 它们显然有效,但expandTemplateRootElement.Triggers.Count和TestPresenter.Triggers.Count都返回零。

正如问题标题中所述,如果我们尝试手动扩展DataTemplate中的内容,就像这样……

 var rawContents = "Show me the money!"; var dataTemplateToUse = TestPresenter.FindResource(new DataTemplateKey(rawContents.GetType())); var expandedTemplateRootElement = dataTemplateToUse.LoadContent() as FrameworkElement; expandedTemplateRootElement.DataContext = rawContents; SomeOtherPresenter.Contents = expandedTemplateRootElement; 

…虽然这确实在第二个ContentPresenter中显示了Border和TextBlock(在这里称为SomeOtherPresenter),而dataTemplateToUse.Triggers 确实显示了两个已定义,但它们不起作用!

我想知道

  • a)为什么不,和
  • b)如何启用/应用它们。

当然,“作弊”只是简单地启动新的ContentPresenter,设置其内容,然后将其ContentTemplate设置为相关的DataTemplate。 然后你可以把整个东西塞进另一个ContentPresenter中,让框架担心细节,比如这……

 var rawContents = "Hello World"; var dataTemplateToUse = TestPresenter.FindResource(new DataTemplateKey(rawContents.GetType())) as DataTemplate; var innerPresenter = new ContentPresenter() { Content = rawContents, ContentTemplate = dataTemplateToUse }; YetAnotherPresenter.Content = innerPresenter; 

…但是,当自动扩展与手动扩展时,仍然没有解释触发器如何实际应用于扩展内容本身。

整个post以完全不同的方式询问…是否有可能以编程方式在FrameworkElements上创建触发器,模仿DataTemplate中定义的触发器(假设名称匹配并考虑名称范围等?)

我查看了这个的内部实现,并将尝试解释这里正在做什么框架。 因此我们知道ContentPresenter具有ContentTemplate属性。 因此,每当我们将DataTemplate分配给ContentTemplate属性时,我们都可以看到它包含DataTemplate中定义的所有内容,包括触发器和所有内容。

现在, FrameworkElement有一个名为TemplateInternal虚拟属性。 派生的FrameworkElement类实现此属性。 每当在FrameworkElement上应用默认模板时,此属性将在内部填充。

在应用模板FrameworkElement检查是否填充了ContentTemplate ,然后应用此模板的内容,否则应用内部属性i中的内容,e TemplateInternal

现在,Framework元素具有自我捕获PropertyChanges的保护方法,在validation属性更改后,触发应用于元素的datatemplate触发器。 这意味着触发器不会被复制到control.Triggers但仍然保留在元素的Datatemplate中。 Framework元素使用内部StyleHelper类通过检查源和目标名称以及更改的属性来触发触发器。

因此,如果我们想通过元素访问它,则无法访问在框架元素上的默认模板上应用的触发器。 我们可以从资源中加载该模板,如其他答案中所述。

现在,在第二种情况下,您通过LoadContent()方法将DataTemplate内容应用于ContentPresenter内容,它只是创建datatemplate的rootelement实例并使用它更新可视化树。 它不会使用DataTemplate更新ContentTemplateTemplateInternal属性,因此不知道任何触发器。