WPF中定义多个DataTrigger时未触发问题

3
我有一个WPF问题,一直无法解决。我在Stack Overflow上尝试了很多帖子,但都没有成功,所以想看看是否有人能帮忙。我只想让我的DataGrid单元格背景,根据值的上升或下降而脉动两种颜色之一。我尝试使用EventTrigger NotifyOnTargetUpdated,并绑定背景颜色,但是你不允许在storyboard中绑定属性背景。然后我尝试使用DataTriggers与状态字段“U”(表示上升)和“D”(表示下降)。我在每个更新周期(更新限制)的开始重置状态为“N”,最初使用两个DataTriggers来处理每种情况,看起来好像一切正常。我看到了绿色和红色的动画,但后来发现有一些更新没有被执行。一旦更新过来,似乎只有第一个声明的DataTrigger才会起作用,在极少数情况下,如果第一个条件没有出现,则另一个触发器会触发。 为了测试这个理论是否正确,我分别运行每个条件,如预期的那样,在每种情况下,第一个声明的触发器只会有90%的时间触发。我已经到处寻找原因,但没有找到,如果有人能够解决这个问题,或者甚至以更可靠的方式实现相同的功能,我将非常感激。
 <DataGridTextColumn Header="Last Trade" Width="60" Binding="{Binding last_trade}" IsReadOnly="true">
      <DataGridTextColumn.CellStyle>
                    <Style TargetType="DataGridCell"> 
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding Path=last_trade_state}" Value="D">
                                <DataTrigger.EnterActions>
                                    <BeginStoryboard>
                                        <Storyboard>
                                            <ColorAnimation Storyboard.TargetProperty="Background.Color" Duration="00:00:01" From="LightSalmon" To="Transparent"></ColorAnimation>
                                        </Storyboard>
                                    </BeginStoryboard>
                                </DataTrigger.EnterActions>
                            </DataTrigger>

                            <DataTrigger Binding="{Binding Path=last_trade_state}" Value="U">
                                <DataTrigger.EnterActions>
                                    <BeginStoryboard>
                                        <Storyboard>
                                            <ColorAnimation Storyboard.TargetProperty="Background.Color" Duration="00:00:01" From="LightGreen" To="Transparent"></ColorAnimation>
                                        </Storyboard>
                                    </BeginStoryboard>
                                </DataTrigger.EnterActions>
                            </DataTrigger>

                        </Style.Triggers>
                    </Style>
                </DataGridTextColumn.CellStyle>
            </DataGridTextColumn>

尝试增加故事板动画的持续时间属性,以便您检查它是否正常工作。 00:00:01 太短了,您可能无法注意到它。 - Vignesh.N
我无法重现你的问题。根据你的代码,我创建了一个简单的例子,完美地运行了。你忘记实现 INotifyPropertyChanged 了吗? - lokusking
2个回答

4

诊断

首先让我介绍我认为是问题的MCVE

<Grid Background="CornflowerBlue">
    <CheckBox x:Name="_CheckBox" Content="Toggle me" />
    <Grid.Style>
        <Style TargetType="Grid">
            <Style.Triggers>
                <DataTrigger Binding="{Binding IsChecked, ElementName=_CheckBox}" Value="False">
                    <DataTrigger.EnterActions>
                        <BeginStoryboard>
                            <Storyboard>
                                <ColorAnimation Storyboard.TargetProperty="Background.Color" Duration="00:00:01" From="LightSalmon" To="Transparent" />
                            </Storyboard>
                        </BeginStoryboard>
                    </DataTrigger.EnterActions>
                </DataTrigger>
                <DataTrigger Binding="{Binding IsChecked, ElementName=_CheckBox}" Value="True">
                    <DataTrigger.EnterActions>
                        <BeginStoryboard>
                            <Storyboard>
                                <ColorAnimation Storyboard.TargetProperty="Background.Color" Duration="00:00:01" From="LightGreen" To="Transparent" />
                            </Storyboard>
                        </BeginStoryboard>
                    </DataTrigger.EnterActions>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Grid.Style>
</Grid>

Grid被加载时,我们看到一个红色闪光,表明第一个触发器已经被触发 - 这是预期行为。如果我们选中CheckBox,我们会看到一个绿色闪光,这意味着第二个触发器已经被触发 - 同样是预期的。但问题就在这里 - 如果我们取消选中CheckBox,我们将不再看到红色闪光。实际上,从现在开始,只有在选中CheckBox时才会看到绿色闪光,而在取消选中CheckBox时将不会看到红色闪光。如果我们重新排列触发器,情况是对称的。
这种行为的原因归结为两个事实:
1. 动画一旦启动,就不会自动停止(默认情况下 - 请参见进一步的解释)。 2. 如果我们在已经进行动画的属性上应用动画,它不会停止/删除先前的动画,而是将其添加到该属性的动画列表中 - 在我们的情况下,最后添加的动画覆盖了之前产生的值,但并非总是如此。 动画何时到达其持续时间的末尾由FillBehavior属性控制,该属性取两个值 - HoldEndStop,默认值为HoldEnd。因此,在我们的情况下,当任一动画完成时,它都会保持背景为Transparent。与实际背景透明一致 - 如果背景未经过动画处理,则会更改回CornflowerBlue
考虑到我们已经解释了观察到的行为 - 第一个红色动画是可见的,因为绿色动画尚未开始。但一旦启动,当我们取消选中CheckBox时,红色动画将重新启动,但我们无法看到其效果,因为它们被绿色动画持续保持背景Transparent所覆盖。至少这是一个可行的逻辑模型 - 也许框架足够智能,不会在它们的效果将被忽略时启动动画,因此红色动画实际上没有重新启动。
半个解决方案是在我们的动画上设置FillBehavior=Stop。这使我们接近目标,但并不完全达到目标。这看起来很好,但有两个例外:
1. 动画结束后,背景会恢复为CornflowerBlue - 这可能不是所需的行为。 2. 如果我们在绿色动画仍在运行时取消选中CheckBox,它不会立即停止,而是会完成,然后我们才能看到剩余部分的红色动画。
真正的解决方法是在选中或取消选中CheckBox时,如果当前动画正在运行,则中止当前动画并替换为另一个动画。为此,我们可以对BeginStoryboard元素进行命名,并使用StopStoryboard元素来中止当前动画。
<Grid Background="CornflowerBlue">
    <CheckBox x:Name="_CheckBox" Content="Toggle me" />
    <Grid.Style>
        <Style TargetType="Grid">
            <Style.Triggers>
                <DataTrigger Binding="{Binding IsChecked, ElementName=_CheckBox}" Value="False">
                    <DataTrigger.EnterActions>
                        <StopStoryboard BeginStoryboardName="Storyboard2" />
                        <BeginStoryboard Name="Storyboard1">
                            <Storyboard>
                                <ColorAnimation Storyboard.TargetProperty="Background.Color" Duration="00:00:01" From="LightSalmon" To="Transparent" />
                            </Storyboard>
                        </BeginStoryboard>
                    </DataTrigger.EnterActions>
                </DataTrigger>
                <DataTrigger Binding="{Binding IsChecked, ElementName=_CheckBox}" Value="True">
                    <DataTrigger.EnterActions>
                        <StopStoryboard BeginStoryboardName="Storyboard1" />
                        <BeginStoryboard Name="Storyboard2">
                            <Storyboard>
                                <ColorAnimation Storyboard.TargetProperty="Background.Color" Duration="00:00:01" From="LightGreen" To="Transparent" />
                            </Storyboard>
                        </BeginStoryboard>
                    </DataTrigger.EnterActions>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Grid.Style>
</Grid>

使用FillBehavior=Stop是可选的,它只控制背景是否会回到CornflowerBlue

1 我刚刚确认实际情况并非总是如此,即使使用ColorAnimation,您也可以简单地不在任何动画上设置To属性(并且放弃StopStoryboard元素),很明显它们会混合在一起。


谢谢。解释得非常详细。好的,问题不是我的,所以你只能得到我的赏金。 - Temitope.A

1
我发现这是一个常见问题,在你的代码中,DataTrigger EnterActions已经根据调试执行了,但我不知道为什么第一次后景色没有改变,我把它放在了tabcontrol中,每次选择标签时,只有第一次有效。
最后,我找到了一种解决方法。你可以试试。
<DataGridTextColumn.ElementStyle>
    <Style TargetType="TextBlock">

        <Setter Property="Background">
            <Setter.Value>
                <SolidColorBrush Color="Transparent"/>
            </Setter.Value>
        </Setter>

        <Style.Triggers>
            <DataTrigger Binding="{Binding Path=last_trade_state}" Value="D">
                <DataTrigger.EnterActions>
                    <BeginStoryboard>
                        <Storyboard Storyboard.TargetProperty="(TextBlock.Background).(SolidColorBrush.Color)" >
                            <ColorAnimation Duration="00:00:1" From="LightSalmon" To="Transparent"
                                            ></ColorAnimation>
                        </Storyboard>
                    </BeginStoryboard>
                </DataTrigger.EnterActions>
            </DataTrigger>

            <DataTrigger Binding="{Binding Path=last_trade_state}" Value="U">
                <DataTrigger.EnterActions>
                    <BeginStoryboard>
                        <Storyboard Storyboard.TargetProperty="(TextBlock.Background).(SolidColorBrush.Color)">
                            <ColorAnimation Duration="00:00:1" From="LightGreen" To="Transparent"
                                            ></ColorAnimation>
                        </Storyboard>
                    </BeginStoryboard>
                </DataTrigger.EnterActions>
            </DataTrigger>

        </Style.Triggers>
    </Style>
</DataGridTextColumn.ElementStyle>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接