为什么DataTemplate.LoadContent()不遵守模板定义的触发器? [英] Why does DataTemplate.LoadContent() not respect template-defined triggers?

查看:45
本文介绍了为什么DataTemplate.LoadContent()不遵守模板定义的触发器?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

TL-DR版本:

我们正试图弄清自动执行触发效果的DataTemplate与手动调用DataTemplate的区别

We're trying to figure out what the difference is between the automatic application of a DataTemplate where triggers are in effect vs. manually calling DataTemplate.LoadContent() where triggers are not in effect.

现在详细说明...

但是首先,让我首先说这个问题是为了帮助我们了解框架及其内部功能,因此,相关代码严格地来演示问题本身,而则不以任何方式代表我们的实际代码。正如他们所说,仅用于说明目的。 (只是想避免不可避免的我不明白您要做什么或那不是我要怎么做的回答。同样,这只是为了支持这个问题。希望是有道理的。)

But first, let me start by saying this question is to help us understand the framework and what it's doing internally, and as such, the associated code is strictly to demonstrate the question itself and does not represent our actual code in any way. It is, as they say, for illustrative purposes only. (Just trying to avoid the inevitable 'I don't understand what' you're trying to do' or the 'That's not how I would do it' responses. Again, it's just to support the question. Hope that makes sense.)

也就是说,请考虑使用XAML定义具有两个触发器的String的DataTemplate(每个触发器针对不同的元素)...

That said, consider this XAML defining a String's DataTemplate with two triggers (each targeting a different element)...

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的另一个位置(该模板在范围内),我们有这个...

Then in another location in XAML where that template is in scope, we have this...

<ContentPresenter x:Name="TestPresenter" Content="This is a Test" />

...可以正常工作。在代码中,我们可以像这样访问扩展模板的根元素(边框)...

...which works as expected. In code, we can gain access to the root element of the expanded template (the Border) like this...

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

...但是如何以及在何处应用触发器?显然,它们可以工作,但是expandTemplateRootElement.Triggers.Count和TestPresenter.Triggers.Count都返回零。

...but how and where are the triggers applied? They obviously work, but both expandedTemplateRootElement.Triggers.Count and TestPresenter.Triggers.Count return zero.

如问题标题本身所述,如果我们尝试扩展

As stated in the question's title itself, if we try to expand out the content from the DataTemplate manually, like this...

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 ),并且dataTemplateToUse.Triggers 确实显示了两个已定义,但它们不起作用!

...while this does properly show the Border and the TextBlock in the second ContentPresenter (called SomeOtherPresenter here), and dataTemplateToUse.Triggers does show two are defined, they don't work!

我正试图找出


  • a)为什么不这样做,

  • b)如何启用/应用它们。

当然,欺骗是简单地启动一个新的ContentPresenter,设置其Content,然后将其ContentTemplate设置为有问题的DataTemplate。然后,您可以将整个内容塞进另一个ContentPresenter中,让框架担心细节,例如...

Of course the 'cheat' would be to simply spin up a new ContentPresenter, set its Content, then set its ContentTemplate to the DataTemplate in question. Then you could just stuff the entire thing in another ContentPresenter and let the framework worry about the details, like this...

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;

...但这仍然不能解释触发器如何实际应用于扩展内容本身

...but that still doesn't explain how the triggers are actually applied to the expanded contents themselves when automatically expanded vs. manual expansion.

这整篇文章提出了一种完全不同的方式...是否可以以编程方式在FrameworkElements上创建触发器,以模仿在DataTemplate中定义的触发器(是否提供了匹配的名称,并考虑了名称范围等?)

This entire post asked a completely different way... Is it possible to programmatically create triggers on FrameworkElements, mimicking those defined in a DataTemplate (provided the names matched up and taking into consideration name scopes, etc.?)

推荐答案

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

I looked into the internal implementation of this and will try to explain what framework is doing here. So we know that ContentPresenter has ContentTemplate property with it. So whenever we assign the DataTemplate to ContentTemplate property we can see that it contains everything defined in the DataTemplate including Triggers and everything.

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

Now, FrameworkElement has one virtual property called TemplateInternal. Derived FrameworkElement classes implement this property. Whenever default template is applied on FrameworkElement this property is populated internally.

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

While applying the template FrameworkElement checks if ContentTemplate is populated then apply the content of this template else apply the content from internal property i,e TemplateInternal

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

Now Framework element has protected method for capturing PropertyChanges in itself which after verifying the property change, triggers the datatemplate Triggers applied on the element. So that means the triggers are not copied to control.Triggers but still remain with Datatemplate of the element. Framework element uses internal StyleHelper class to fire the trigger by checking the source and target name and changed property.

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

So, accessing the triggers applied on default template on framework element is not possible if we want to access it through the element. We can load that template from the resources as explained in other answers also.

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

Now, in second case where you are applying the DataTemplate content into the ContentPresenter content by LoadContent() method it just creates the instance of the rootelement of datatemplate and update the visual tree with it. It does not update the ContentTemplate or TemplateInternal property with the DataTemplate hence does not know about any triggers.

这篇关于为什么DataTemplate.LoadContent()不遵守模板定义的触发器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆