不使用 ContentTemplateSelector 触发

发布于 2025-01-17 06:43:04 字数 5977 浏览 3 评论 0原文

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

<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 中的 Number 属性使用 ContentTemplate 时,动画正在运行。 但是当我使用 ContentTemplateSelector 时,动画不再被触发。

我在这里缺少什么?

I'm trying to animate the Background Color of a Border in a DataTemplate for a DataObject when a Child Property of the DataObject changes.
The DataObject is a Class called Test with two Properties, Number and Text.
I have an ObservableCollection of DataObjects called Numbers.
In a Task I update the Number Property at a regular Interval.

<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 = 
quot;#: {i}"});
            }

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

                        Application.Current?.Dispatcher.Invoke(() =>
                        {
                            Numbers[rnd.Next(9)] = new Test
                            {
                                Number = rnd.Next(30),
                                Text = 
quot;# {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; }
    }
}

When using a ContentTemplate for the Number Property in the ContentControl the animation is working.
But when I use a ContentTemplateSelector the Animation is not triggered anymore.

What am I missing here?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

忆伤 2025-01-24 06:43:04

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

目前,您的 ContentControl.Content 属性(在 Test 类型的 DataTemplate 内)绑定到 Test.Number int 类型的属性。因此,传递给 DataTemplateSelector 的对象属于 int 类型,而不是 Test 类型。 DataTemplateSelector.SelectTemplate 方法中的条件将返回 false(因为 int is not Test is true)并使 DataTemplateSelector.SelectTemplate 返回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>

Your data bindings are wrong, resulting in unhandled or unexpected data types.

Currently, your ContentControl.Content property (inside the DataTemplate for the type Test) binds to the Test.Number property of type int. Therefore, the object that is passed to the DataTemplateSelector is of type int and not Test. The condition in the DataTemplateSelector.SelectTemplate method will return false (as int is not Test is true) and make the DataTemplateSelector.SelectTemplate return null - no template. The XAML engine will call ToString on the int instance and is therefore able to display the value correctly (despite the lack of a DataTemplate).

Solution

You must fix the binding on the ContentControl.Content property to bind to the Test instance instead of binding to the Test.Number property:

<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>

Now that that the DataTemplateSelector can work properly, you must also adjust the NumberTemplate, so that it can display the Test.Number property properly. Since we have modified the binding source for the ContentControl.Content property, the new data type is now Test (instead of int):

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

Remarks

There is a lot of redundant code here. You will achieve the same results by dropping all the templates and the DataTemplateSelector by defining the ItemTemplate for the Test items properly.
The following drastically simplified version should also work:

<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>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文