2014-02-07 40 views
2

研究員WPF用戶,WPF行爲+故事板動畫= AssociatedObject的是空

我目前正在試圖總結我的周圍與WPF混合行爲問題的頭。我發現了一個我想在WPF應用程序中使用的行爲,但代碼最初是爲Silverlight編寫的(http://www.sharpgis.net/post/2009/08/11/Silverlight-Behaviors-Triggers-and-Actions.aspx)。

我的問題是,當動畫開始和依賴屬性OffsetMediatorProperty變化,我進入方法OnOffsetMediatorPropertyChanged,該AssociatedObjectnull。另外,它也看起來像所有的字段是null

我使用的行爲:

public class MouseScrollViewer : Behavior<ScrollViewer> 
{ 
    double target = 0; 
    int direction = 0; 
    private Storyboard storyboard; 
    private DoubleAnimation animation; 

    protected override void OnAttached() 
    { 
     base.OnAttached(); 

     CreateStoryBoard(); 

     AssociatedObject.PreviewMouseWheel += AssociatedObject_MouseWheel; 
    } 

    protected override void OnDetaching() 
    { 
     AssociatedObject.PreviewMouseWheel -= AssociatedObject_MouseWheel; 
     storyboard = null; 
     base.OnDetaching(); 
    } 

    private void AssociatedObject_MouseWheel(object sender, MouseWheelEventArgs e) 
    { 
     if (Animate(-Math.Sign(e.Delta) * ScrollAmount)) 
     { 
      e.Handled = true; 
     } 
    } 

    private bool Animate(double offset) 
    { 
     storyboard.Pause(); 

     if (Math.Sign(offset) != direction) 
     { 
      target = AssociatedObject.VerticalOffset; 
      direction = Math.Sign(offset); 
     } 

     target += offset; 
     target = Math.Max(Math.Min(target, AssociatedObject.ScrollableHeight), 0); 

     animation.To = target; 
     animation.From = AssociatedObject.VerticalOffset; 

     if (animation.From != animation.To) 
     { 
      storyboard.Begin(); 
      return true; 
     } 

     return false; 
    } 

    private void CreateStoryBoard() 
    { 
     storyboard = new Storyboard(); 
     animation = new DoubleAnimation 
     { 
      Duration = TimeSpan.FromSeconds(.5), 
      EasingFunction = new ExponentialEase { EasingMode = EasingMode.EaseOut } 
     }; 

     Storyboard.SetTarget(animation, this); 
     Storyboard.SetTargetProperty(animation, new PropertyPath(OffsetMediatorProperty)); 

     storyboard.Children.Add(animation); 
     storyboard.Completed += (s, e) => { direction = 0; }; 
    } 

    internal double OffsetMediator 
    { 
     get { return (double)GetValue(OffsetMediatorProperty); } 
     set { SetValue(OffsetMediatorProperty, value); } 
    } 

    internal static readonly DependencyProperty OffsetMediatorProperty = 
     DependencyProperty.Register("OffsetMediator", typeof(double), typeof(MouseScrollViewer), new PropertyMetadata(0.0, OnOffsetMediatorPropertyChanged)); 

    private static void OnOffsetMediatorPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     MouseScrollViewer msv = d as MouseScrollViewer; 
     if (msv != null && msv.AssociatedObject != null) 
     { 
      msv.AssociatedObject.ScrollToVerticalOffset((double)e.NewValue); 
     } 
    } 

    public double ScrollAmount 
    { 
     get { return (double)GetValue(ScrollAmountProperty); } 
     set { SetValue(ScrollAmountProperty, value); } 
    } 

    public static readonly DependencyProperty ScrollAmountProperty = 
     DependencyProperty.Register("ScrollAmount", typeof(double), typeof(MouseScrollViewer), new PropertyMetadata(50.0)); 
} 

用法:

<ScrollViewer> 
     <i:Interaction.Behaviors> 
      <local:MouseScrollViewer x:Name="ScrollViewer" /> 
     </i:Interaction.Behaviors> 

     <ItemsControl ItemsSource="{Binding}"        
         FontSize="32"> 

     </ItemsControl> 
    </ScrollViewer> 

它看起來像行爲Silverlight的演示工作,而不是在一個WPF應用程序。我真的希望你們中的一些能夠向我解釋爲什麼會發生這種情況,希望能幫助我理清這個問題。

+0

戴上斷點法'OnAttached()',看看是否曾經被調用。如果是,AsscoiatedObject在這種情況下的價值是什麼。並從您設置DP OffsetMediator值的位置開始? –

+0

是的,確實調用了'OnAttached()'方法。關聯對象的值是我的滾動查看器的實例。關於DP,這是通過在Storyboard.SetTargetProperty(animation,new PropertyPath(OffsetMediatorProperty))語句中的'CreateStoryBoard()'方法中設置的Storyboard動畫設置的。 –

+0

我應該補充說,如果我直接設置DP(例如在Animate()方法中),則AssociatedObject不是null。所以我懷疑問題與通過動畫設置的方式有關。 –

回答

2

當故事板使用Begin()運行時,它在內部試圖訪問可冷凍動畫對象而你的情況是MouseScrollViewer類的實例對象,因此它結束了克隆的動畫的實例,並最後將MosueScrollViewer

那麼,實際的問題是的AssociatedObject爲null,因爲動畫上MouseScrollViewer的另一個實例做,而不是你的實際情況


有兩種解決方法是:動畫對象上

首先調用animation.Freeze()這樣就不會創建新實例,但這種方法的問題是它只會工作,第一次和第二次它將失敗即凍結對象屬性不能修改


第二種解決方法是,當需要代碼動畫時,根本不需要storyboard。您可以直接在您當前的實例上動畫。請看下面你需要做的改變:

第一個全部從您的代碼中刪除storyboard

修改CreateStoryBoard()CreateAnimation()

private void CreateAnimation() 
{ 
    animation = new DoubleAnimation 
    { 
     Duration = TimeSpan.FromSeconds(.5), 
     EasingFunction = new ExponentialEase { EasingMode = EasingMode.EaseOut } 
    }; 

    Storyboard.SetTarget(animation, this); 
    Storyboard.SetTargetProperty(animation, new 
           PropertyPath(OffsetMediatorProperty)); 

    animation.Completed += (s, e) => { direction = 0; }; 
} 

Animate()方法應該是這樣的:

private bool Animate(double offset) 
{ 
    if (Math.Sign(offset) != direction) 
    { 
     target = AssociatedObject.VerticalOffset; 
     direction = Math.Sign(offset); 
    } 

    target += offset; 
    target = Math.Max(Math.Min(target, AssociatedObject.ScrollableHeight), 0); 

    animation.To = target; 
    animation.From = AssociatedObject.VerticalOffset; 

    if (animation.From != animation.To) 
    { 
     this.BeginAnimation(OffsetMediatorProperty, animation); <-- HERE 
     return true; 
    } 

    return false; 
}