WPF用户,
我目前正试图解决与WPF Blend行为相关的问题。我发现了一个我想在WPF应用程序中使用的行为,但代码最初是为Silverlight编写的(http://www.sharpgis.net/post/2009/08/11/Silverlight-Behaviors-Triggers-and-Actions.aspx)。
我遇到的问题是,当动画开始并且依赖属性OffsetMediatorProperty
发生变化时,我进入方法OnOffsetMediatorPropertyChanged
,AssociatedObject
为null
。此外,它看起来似乎所有字段都是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 :(得分:2)
当使用Begin()
运行storyboard时,它会在内部尝试访问freezable动画对象,在您的情况下,它是类MouseScrollViewer
的实例对象,因此它最终会克隆一个动画实例并且最终你的MosueScrollViewer 。
因此,实际问题是 AssociatedObject为null,因为动画是在另一个MouseScrollViewer实例上完成的,而不是在您的实际实例上 。
有两种解决方法:
首先在动画对象上调用animation.Freeze()
,以便不创建新实例,但是这种方法的问题是它只能在第一次工作,第二次会失败, 冻结对象属性可以不得修改 。
第二种解决方法是,当您需要代码中的动画时,根本不需要故事板。您可以 直接在当前实例上执行动画 。请看下面您需要做的更改:
首先将您的代码完全删除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;
}