TL-DR 版本:
我们试图弄清楚在触发器有效的情况下自动应用 DataTemplate 与在触发器无效的情况下手动调用 DataTemplate.LoadContent() 之间有什么区别。
现在详细...
但首先,让我先说这个问题是为了帮助我们理解框架以及它在内部做了什么,因此,相关的代码只是为了演示问题本身,并不代表我们的实际代码。正如他们所说,这仅用于说明目的。(只是试图避免不可避免的“我不明白你在做什么”或“我不会这样做”的回答。同样,这只是为了支持这个问题。希望这是有道理的。)
也就是说,考虑这个 XAML 定义一个带有两个触发器的字符串的 DataTemplate(每个触发器都针对不同的元素)......
xmlns:system="clr-namespace:System;assembly=mscorlib"
...
<DataTemplate DataType="{x:Type system:String}">
<Border x:Name="Bd" Background="Yellow">
<TextBlock x:Name="Tb" Text="{Binding StringFormat='Formatted Value: {0}'}" />
</Border>
<DataTemplate.Triggers>
<Trigger SourceName="Bd" Property="IsMouseOver" Value="True">
<Setter TargetName="Bd" Property="Background" Value="Red" />
</Trigger>
<Trigger SourceName="Tb" Property="IsMouseOver" Value="True">
<Setter TargetName="Tb" Property="Foreground" Value="Yellow" />
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
然后在 XAML 中该模板在范围内的另一个位置,我们有这个......
<ContentPresenter x:Name="TestPresenter" Content="This is a Test" />
...按预期工作。在代码中,我们可以像这样访问扩展模板(边框)的根元素...
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 (此处称为 SomeOtherPresenter )中正确显示了 Border 和 TextBlock ,并且 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;
...但这仍然不能解释当自动扩展与手动扩展时触发器如何实际应用于扩展内容本身。
整篇文章问了一种完全不同的方式......是否可以以编程方式在 FrameworkElements 上创建触发器,模仿 DataTemplate 中定义的触发器(提供匹配的名称并考虑名称范围等?)