EventTrigger有时无法触发

时间:2013-03-27 11:34:13

标签: wpf inotifypropertychanged datatrigger

我又有点难过了,希望有人可以请求帮助。 向长代码道歉。

问题 - 我有一个DataTrigger按预期开始触发,但最终失败了,我无法解决原因。在提供的示例中,它围绕Canvas移动矩形。每次单击按钮都会按此顺序移动它; N,NE,E,SE,S,SW,W,NW。然后它再次启动序列,从N开始。一旦第一个序列完成,它就不会向北移动。它只会再次移动NW(即最后一次成功的移动)。

触发DataTrigger的属性正在更新。

感谢

XAML;

<Window.Resources>
    <Style x:Key="TestRectStyle" TargetType="{x:Type Rectangle}">
        <Style.Triggers>
            <DataTrigger Binding="{Binding UI_DirectionOfMovement}" Value="North">
                <DataTrigger.EnterActions>
                    <BeginStoryboard>
                        <Storyboard>
                            <DoubleAnimation Storyboard.TargetProperty="(Canvas.Top)" By="-50" Duration="0:0:0.8" AutoReverse="False" />
                            <DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" By="0" Duration="0:0:0.8" AutoReverse="False" />
                        </Storyboard>
                    </BeginStoryboard>
                </DataTrigger.EnterActions>
            </DataTrigger>
            <DataTrigger Binding="{Binding UI_DirectionOfMovement}" Value="NorthEast">
                <DataTrigger.EnterActions>
                    <BeginStoryboard>
                        <Storyboard>
                            <DoubleAnimation Storyboard.TargetProperty="(Canvas.Top)" By="-50" Duration="0:0:0.8" AutoReverse="False" />
                            <DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" By="50" Duration="0:0:0.8" AutoReverse="False" />
                        </Storyboard>
                    </BeginStoryboard>
                </DataTrigger.EnterActions>
            </DataTrigger>
            <DataTrigger Binding="{Binding UI_DirectionOfMovement}" Value="East">
                <DataTrigger.EnterActions>
                    <BeginStoryboard>
                        <Storyboard>
                            <DoubleAnimation Storyboard.TargetProperty="(Canvas.Top)" By="0" Duration="0:0:0.8" AutoReverse="False" />
                            <DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" By="50" Duration="0:0:0.8" AutoReverse="False" />
                        </Storyboard>
                    </BeginStoryboard>
                </DataTrigger.EnterActions>
            </DataTrigger>
            <DataTrigger Binding="{Binding UI_DirectionOfMovement}" Value="SouthEast">
                <DataTrigger.EnterActions>
                    <BeginStoryboard>
                        <Storyboard>
                            <DoubleAnimation Storyboard.TargetProperty="(Canvas.Top)" By="50" Duration="0:0:0.8" AutoReverse="False" />
                            <DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" By="50" Duration="0:0:0.8" AutoReverse="False" />
                        </Storyboard>
                    </BeginStoryboard>
                </DataTrigger.EnterActions>
            </DataTrigger>
            <DataTrigger Binding="{Binding UI_DirectionOfMovement}" Value="South">
                <DataTrigger.EnterActions>
                    <BeginStoryboard>
                        <Storyboard>
                            <DoubleAnimation Storyboard.TargetProperty="(Canvas.Top)" By="50" Duration="0:0:0.8" AutoReverse="False" />
                            <DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" By="0" Duration="0:0:0.8" AutoReverse="False" />
                        </Storyboard>
                    </BeginStoryboard>
                </DataTrigger.EnterActions>
            </DataTrigger>
            <DataTrigger Binding="{Binding UI_DirectionOfMovement}" Value="SouthWest">
                <DataTrigger.EnterActions>
                    <BeginStoryboard>
                        <Storyboard>
                            <DoubleAnimation Storyboard.TargetProperty="(Canvas.Top)" By="50" Duration="0:0:0.8" AutoReverse="False" />
                            <DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" By="-50" Duration="0:0:0.8" AutoReverse="False" />
                        </Storyboard>
                    </BeginStoryboard>
                </DataTrigger.EnterActions>
            </DataTrigger>
            <DataTrigger Binding="{Binding UI_DirectionOfMovement}" Value="West">
                <DataTrigger.EnterActions>
                    <BeginStoryboard>
                        <Storyboard>
                            <DoubleAnimation Storyboard.TargetProperty="(Canvas.Top)" By="0" Duration="0:0:0.8" AutoReverse="False" />
                            <DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" By="-50" Duration="0:0:0.8" AutoReverse="False" />
                        </Storyboard>
                    </BeginStoryboard>
                </DataTrigger.EnterActions>
            </DataTrigger>
            <DataTrigger Binding="{Binding UI_DirectionOfMovement}" Value="NorthWest">
                <DataTrigger.EnterActions>
                    <BeginStoryboard>
                        <Storyboard>
                            <DoubleAnimation Storyboard.TargetProperty="(Canvas.Top)" By="-50" Duration="0:0:0.8" AutoReverse="False" />
                            <DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" By="-50" Duration="0:0:0.8" AutoReverse="False" />
                        </Storyboard>
                    </BeginStoryboard>
                </DataTrigger.EnterActions>
            </DataTrigger>
        </Style.Triggers>
    </Style>

    <DataTemplate x:Key="TestDataTemplate01" DataType="BO:MyPerson">
        <Canvas Width="1000" Height="1000" Background="Transparent">
            <Rectangle Width="50" Height="50" Fill="Red" Style="{StaticResource TestRectStyle}" Canvas.Top="300" Canvas.Left="300" />
        </Canvas>
    </DataTemplate>
</Window.Resources>

<Canvas Width="1000" Height="1000">
    <ItemsControl Name="ic_People" ItemTemplate="{StaticResource TestDataTemplate01}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas Width="1000" Height="1000" />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>

    <Button Canvas.Right="0" Click="Button_Click_1" Width="120">Next Move</Button>
</Canvas>

代码隐藏;

public partial class Window1 : Window
{
    private ObservableCollection<MyPerson> _personList = new ObservableCollection<MyPerson>();

    public Window1()
    {
        InitializeComponent();

        MyPerson person1 = new MyPerson();
        _personList.Add(person1);
        ic_People.ItemsSource = _personList;
    }

    private void Button_Click_1(object sender, RoutedEventArgs e)
    {
        DoNextMove();
    }

    private int debugDirection = 0;
    private void DoNextMove()
    {
        if (debugDirection > 15)
            debugDirection = 0;

        _personList[0].MoveOneTile(debugDirection);
        debugDirection += 2; // increase by 2 to as I've not implemented the odd numbers yet
    }
}

MyPerson代码;

public class MyPerson : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }

    private string _dirMov = "";
    public string UI_DirectionOfMovement
    {
        get { return _dirMov; }
        set
        {
            _dirMov = value;
            OnPropertyChanged("UI_DirectionOfMovement");
        }
    }

    public void MoveOneTile(int directionToMove)
    {
        this.UI_DirectionOfMovement = "clear"; // clearing it first forces an update

        switch (directionToMove)
        {
            case 0: { this.UI_DirectionOfMovement = "North"; break; }
            case 2: { this.UI_DirectionOfMovement = "NorthEast"; break; }
            case 4: { this.UI_DirectionOfMovement = "East"; break; }
            case 6: { this.UI_DirectionOfMovement = "SouthEast"; break; }
            case 8: { this.UI_DirectionOfMovement = "South"; break; }
            case 10: { this.UI_DirectionOfMovement = "SouthWest"; break; }
            case 12: { this.UI_DirectionOfMovement = "West"; break; }
            case 14: { this.UI_DirectionOfMovement = "NorthWest"; break; }
            default: { throw new Exception(); }
        }
    }

    public MyPerson()
    {
    }
}

1 个答案:

答案 0 :(得分:1)

此处的问题最终归结为动画实际上不会更改Canvas.LeftCanvas.Top属性的值。它们似乎只是这样做,因为从动画中获取的值会覆盖通过数据绑定获得的值。

每个动画结束后,动画“保持”Canvas.LeftCanvas.Top依赖属性的值为其最终值。如果获取依赖项属性的值,则此“保持”值将返回,并且它将覆盖通过数据绑定设置的任何值。当您开始第二个动画时,依赖属性的值是通过处理上一个动画的保持值来获得的。随着越来越多的动画发生,WPF必须通过一系列越来越多的动画来确定矩形的位置。

我不能说为什么只有最后一个(NW)动画在你运行它们之后才会运行。它很可能与dependency property value precedence有关。此页面没有说明如果依赖项属性上有多个动画会发生什么,但在这种情况下,我会假设在该属性上启动的最后一个动画优先。我怀疑DataTrigger正在触发,但WPF依赖属性系统由于某种原因忽略了来自'覆盖'动画的值。

我建议避免使用像这样的动画链。相反,请更改您的Person对象以跟踪它们在画布上的位置,例如,添加LeftTop属性。然后,您可以将Canvas.LeftCanvas.Top绑定到这些属性。您的DoNextMove方法还应将这些属性的值设置为动画移动到的位置。在更改UI_DirectionOfMovement属性的值后执行此操作。最后,通过在每个FillBehavior="Stop"上设置DoubleAnimation来阻止您的动画“保留”其最终值。

由于动画值优先于本地设置值,因此在动画开始时设置LeftTop的属性值不是问题。动画运行时,Canvas.LeftCanvas.Top的动画值优先于通过数据绑定设置的任何值。随着动画的完成,动画将释放其对Canvas.LeftCanvas.Top依赖项属性的保留,并且矩形的位置将恢复为通过数据绑定获取。运气好的话,这个位置与动画结束时的位置相同。