TL-DR版本:
我们试图弄清楚触发器生效的DataTemplate的自动应用与触发器无效的手动调用DataTemplate.LoadContent()之间的区别。
现在详情......
但首先,我首先要说的是,这个问题是为了帮助我们理解框架及其在内部所做的工作,因此,相关代码严格来证明问题本身并且< em> not 以任何方式表示我们的实际代码。正如他们所说,它仅用于说明目的。 (只是试图避免不可避免的'我不明白你要做什么'或'那不是我会怎么做'的回应。再一次,它只是为了支持这个问题。希望这是有道理的。)
那就是说,考虑这个XAML定义一个String的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" />
...按预期工作。在代码中,我们可以像这样访问扩展模板(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 显示两个已定义,但它们不起作用!< / p>
我正试图找出
当然,“作弊”只是简单地启动一个新的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中定义的触发器(假设名称匹配并考虑名称范围等等?) >
答案 0 :(得分:4)
我研究了这个的内部实现,并试图解释这里正在做什么框架。因此,我们知道ContentPresenter
具有ContentTemplate
属性。因此,每当我们将DataTemplate
赋给ContentTemplate
属性时,我们都可以看到它包含DataTemplate中定义的所有内容,包括触发器和所有内容。
现在,FrameworkElement
有一个名为TemplateInternal
的虚拟属性。派生的FrameworkElement类实现此属性。只要在FrameworkElement上应用默认模板,就会在内部填充此属性。
应用模板FrameworkElement
检查是否填充了ContentTemplate
,然后应用此模板的内容,否则应用内部属性i中的内容,e TemplateInternal
现在,Framework元素具有自我捕获PropertyChanges的保护方法,在验证属性更改后,触发应用于元素的datatemplate触发器。这意味着触发器不会被复制到control.Triggers但仍然保留在元素的Datatemplate中。 Framework元素使用内部StyleHelper
类通过检查源和目标名称以及更改的属性来触发触发器。
因此,如果我们想通过元素访问它,则无法访问在框架元素上的默认模板上应用的触发器。我们可以从资源中加载该模板,如其他答案中所述。
现在,在第二种情况下,您通过DataTemplate
方法将ContentPresenter
内容应用到LoadContent()
内容中,它只是创建了datatemplate的rootelement实例并更新了可视树它。它不会使用DataTemplate更新ContentTemplate
或TemplateInternal
属性,因此不知道任何触发器。