2013-03-27 50 views
1

我再次有点难倒,并希望有人能请求帮助。 提前向长码道歉。事件触发器有时无法触发

问题 - 我有一个DataTrigger开始如预期的开火,但它最终失败,我不能解决原因。在提供的例子中,它围绕Canvas移动一个矩形。每按一次该按钮就会按照这个顺序移动它; N,NE,E,SE,S,SW,W,NW。然后再次从N开始序列。一旦第一个序列完成,它将不会向北移动。它只会再次移动净重(即最后一次成功的移动)。

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

Codebehind;

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

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

这里的问题最终归结为一个事实,即动画不会真正改变Canvas.LeftCanvas.Top属性的值。它们似乎是这样做的,因为通过数据绑定获得的动画覆盖值可以获得值。

每个动画完成后,动画将其值“Canvas.Left”或“Canvas.Top”依赖项属性保留为其最终值。如果您获得了依赖项属性的值,则该“保留”值将返回,并且它将覆盖通过数据绑定设置的任何值。在开始第二个动画时,依赖属性的值通过使用前一个动画的保留值来获得。随着越来越多的动画发生,WPF必须通过回溯越来越多的动画链来确定矩形的位置。

我不能说为什么只有最后一个(NW)动画运行后,你运行了所有的人。这很可能与dependency property value precedence有关。这个页面没有说明如果依赖项属性有多个动画时会发生什么,但是在这种情况下,我会假定最后一个动画在该属性上启动。我怀疑DataTrigger正在解雇,但由于某种原因,WPF依赖属性系统忽略了来自'重写'动画的值。

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

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

+0

谢谢卢克,这个建议帮助我解决了这个问题。我照你所说的做了。在MyPerson上添加了两个额外的属性(记录当前位置),将Canvas.Top和Left绑定到这些新属性,并将FillBehavior =“Stop”添加到每个DoubleAnimation。现在,当其中一个属性发生变化时,动画首先发生,一旦完成Canvas.Top/Left绑定再次接管。完善! – ausgeorge 2013-03-27 21:36:34

+0

@ausgeorge:很高兴听到它帮助你! – 2013-03-28 08:04:18