gpt4 book ai didi

c# - 为什么 DataTemplate.LoadContent() 不遵守模板定义的触发器?

转载 作者:行者123 更新时间:2023-11-30 15:32:36 36 4
gpt4 key购买 nike

TL-DR 版本:

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

现在详细...

但首先,让我先说这个问题是为了帮助我们了解框架及其内部所做的事情,因此,相关代码严格是为了演示问题本身并做< em>不以任何方式代表我们的实际代码。正如他们所说,它仅用于说明目的。 (只是试图避免不可避免的“我不明白你想做什么”或“那不是我会怎么做”的回答。同样,这只是为了支持这个问题。希望这是有道理的。)

就是说,考虑这个 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;

...但是如何以及在何处应用触发器?它们显然有效,但 expandedTemplateRootElement.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 中定义的触发器(假设名称匹配并考虑名称范围等?)

最佳答案

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

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

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

现在 Framework 元素本身具有用于捕获 PropertyChanges 的 protected 方法,该方法在验证属性更改后触发应用于元素的数据模板触发器。因此,这意味着触发器不会复制到 control.Triggers,但仍保留在元素的 Datatemplate 中。框架元素使用内部 StyleHelper 类通过检查源和目标名称以及更改的属性来触发触发器。

因此,如果我们想通过元素访问应用在框架元素上的默认模板的触发器是不可能的。我们也可以按照其他答案中的说明从资源中加载该模板。

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

关于c# - 为什么 DataTemplate.LoadContent() 不遵守模板定义的触发器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18649665/

36 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com