<EventTrigger RoutedEvent="Binding.TargetUpdated"> 未使用 ContentTemplateSelector 触发

<EventTrigger RoutedEvent="Binding.TargetUpdated"> not firing with ContentTemplateSelector

我正在尝试在 DataObject 的子 属性 更改时为 DataObject 的 DataTemplate 中的边框背景颜色设置动画。 DataObject 是一个名为 Test 的 Class,具有两个属性,Number 和 Text。 我有一个名为 Numbers 的 DataObjects 的 ObservableCollection。 在任务中,我定期更新数字 属性。

<Window
  x:Class="WpfAnimationTest.MainWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:local="clr-namespace:WpfAnimationTest"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  Title="MainWindow"
  Width="800"
  Height="450"
  DataContext="{Binding Main, Source={StaticResource Locator}}"
  mc:Ignorable="d">
    <Grid>
        <Grid.Resources>
            <DataTemplate x:Key="NumberTemplate">
                <TextBlock Text="{Binding NotifyOnTargetUpdated=True}" />
            </DataTemplate>

            <local:ValueTemplateSelector x:Key="TemplateSelector">
                <local:ValueTemplateSelector.NumberTemplate>
                    <DataTemplate>
                        <ContentControl Content="{Binding NotifyOnTargetUpdated=True}" ContentTemplate="{StaticResource NumberTemplate}" />
                    </DataTemplate>
                </local:ValueTemplateSelector.NumberTemplate>
            </local:ValueTemplateSelector>

            <DataTemplate DataType="{x:Type local:Test}">
                <Border x:Name="UpdateBorder" Background="Aqua">
                    <StackPanel Orientation="Horizontal">
                        <TextBlock
                          Width="50"
                          Margin="10"
                          Text="{Binding Text}" />
                        <ContentControl
                          Width="50"
                          Margin="10"
                          Content="{Binding Number}"
                          ContentTemplateSelector="{StaticResource TemplateSelector}" />

                        <!--
                            ContentTemplate="{StaticResource NumberTemplate}"
                        -->
                    </StackPanel>
                </Border>
                <DataTemplate.Triggers>
                    <EventTrigger RoutedEvent="Binding.TargetUpdated">
                        <EventTrigger.Actions>
                            <BeginStoryboard>
                                <Storyboard AutoReverse="True">
                                    <ColorAnimation
                                      FillBehavior="Stop"
                                      Storyboard.TargetName="UpdateBorder"
                                      Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
                                      To="#C5AFFFAA"
                                      Duration="00:00:0.5" />
                                </Storyboard>
                            </BeginStoryboard>
                        </EventTrigger.Actions>
                    </EventTrigger>
                </DataTemplate.Triggers>
            </DataTemplate>
        </Grid.Resources>
        <ListBox ItemsSource="{Binding Numbers}" />
    </Grid>
</Window>
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;

namespace WpfAnimationTest
{
    public class Locator
    {
        public Locator()
        {
            Main = new Main();
        }

        public Main Main { get; set; }
    }

    public class Main
    {
        public ObservableCollection<Test> Numbers { get; set; } = new ObservableCollection<Test>();

        public Main()
        {
            var rnd = new Random(42);

            for (int i = 0; i < 10; i++)
            {
                Numbers.Add(new Test(){Number = i, Text = $"#: {i}"});
            }

            Task.Run(() =>
            {
                while (true)
                {
                    try
                    {

                        Application.Current?.Dispatcher.Invoke(() =>
                        {
                            Numbers[rnd.Next(9)] = new Test
                            {
                                Number = rnd.Next(30),
                                Text = $"# {rnd.Next(30) + 30}"
                            };
                        });
                        Thread.Sleep(1000);

                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e);
                        throw;
                    }
                }
            });

        }
    }

    public class Test
    {
        public int Number { get; set; }

        public string Text { get; set; }
    }

    
    public class ValueTemplateSelector : DataTemplateSelector
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="item"></param>
        /// <param name="container"></param>
        /// <returns></returns>
        public override DataTemplate SelectTemplate(object item, DependencyObject container)
        {
            return !(item is Test value)
                ? null
                : NumberTemplate;
        }
        /// <summary>
        /// 
        /// </summary>
        public DataTemplate NumberTemplate { get; set; }
        
        /// <summary>
        /// 
        /// </summary>
        public DataTemplate DefaultTemplate { get; set; }
    }
}

当在 ContentControl 中为数字 属性 使用 ContentTemplate 时,动画正在运行。 但是当我使用 ContentTemplateSelector 时,动画不再被触发。

我在这里错过了什么?

您的数据绑定错误,导致未处理或意外的数据类型。

目前,您的 ContentControl.Content 属性(在 Test 类型的 DataTemplate 内)绑定到 Test.Number 属性 类型int。因此,传递给 DataTemplateSelector 的对象属于 int 而不是 Test 类型。 DataTemplateSelector.SelectTemplate 方法中的条件将为 return 假(因为 int is not Testtrue)并使 DataTemplateSelector.SelectTemplate return null -没有模板。 XAML 引擎将在 int 实例上调用 ToString,因此能够正确显示值(尽管缺少 DataTemplate)。

解决方案

您必须修复 ContentControl.Content 属性 上的绑定以绑定到 Test 实例而不是绑定到 Test.Number 属性:

<DataTemplate DataType="{x:Type local:Test}">
    <Border x:Name="UpdateBorder"
            Background="Aqua">
      <StackPanel Orientation="Horizontal">
        <TextBlock Width="50"
                    Margin="10"
                    Text="{Binding Text}" />
        <ContentControl Width="50"
                        Margin="10"
                        Content="{Binding}"
                        ContentTemplateSelector="{StaticResource TemplateSelector}" />
      </StackPanel>
    </Border>

    ...
</DataTemplate>

现在DataTemplateSelector可以正常显示了,你还必须调整NumberTemplate,才能正常显示Test.Number 属性。由于我们修改了 ContentControl.Content 属性 的绑定源,新数据类型现在是 Test(而不是 int):

<DataTemplate x:Key="NumberTemplate"
              DataType="{x:Type Test}">
  <TextBlock Text="{Binding Number, NotifyOnTargetUpdated=True}" />
</DataTemplate>

备注

这里有很多冗余代码。通过为 Test 项正确定义 ItemTemplate 删除所有模板和 DataTemplateSelector,您将获得相同的结果。
以下大大简化的版本也应该有效:

<Grid>
  <Grid.Resources>
    <DataTemplate DataType="{x:Type local:Test}">
      <Border x:Name="UpdateBorder"
              Background="Aqua">
        <StackPanel Orientation="Horizontal">
          <TextBlock Width="50"
                      Margin="10"
                      Text="{Binding Text}" />
          <TextBlock Width="50"
                      Margin="10"
                      Text="{Binding Number, NotifyOnTargetUpdated=True}" />
        </StackPanel>
      </Border>
      <DataTemplate.Triggers>
        <EventTrigger RoutedEvent="Binding.TargetUpdated">
          <EventTrigger.Actions>
            <BeginStoryboard>
              <Storyboard AutoReverse="True">
                <ColorAnimation FillBehavior="Stop"
                                Storyboard.TargetName="UpdateBorder"
                                Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
                                To="#C5AFFFAA"
                                Duration="00:00:0.5" />
              </Storyboard>
            </BeginStoryboard>
          </EventTrigger.Actions>
        </EventTrigger>
      </DataTemplate.Triggers>
    </DataTemplate>
  </Grid.Resources>

  <ListBox ItemsSource="{Binding Numbers}" />
</Grid>