用户控件动画仅适用于上次声明的控制实例

时间:2015-07-09 21:21:36

标签: xaml animation user-controls windows-10 win-universal-app

我正在对一个(Windows 10通用应用程序)用户界面进行原型设置,并构建了一个非常简单/粗略的用户控件来充当“徽章”,即在圆圈中显示数值,并为更改值设置动画。我的问题是如果控件在应用程序页面中只有一个实例,则它可以正常工作。如果有多个实例(即使其他实例不可见),则只有最后声明的实例会动画化。

我已尝试在用户控件的XAML中声明动画,并在后面的代码中尝试确保没有交叉/混合正在共享的动画。我还为动画属性添加了一个更改回调,它使用Debug.WriteLine写出属性值。对于正确设置动画的控件实例,按预期更改值,即如果我们从10变为20,则属性设置为10,11,12,13 .... 20.对于不起作用的实例,值每次都设置为from属性,即10,10,10,10,10。

下面是用户控件的示例,然后是使用它的三个实例的示例页面。放置这两个是一个名为App3的新Windows 10 Universal应用程序应该重现该问题。在示例页面中,前两个徽章在单击按钮时没有正确设置动画,但最后一个徽章没有。

是否有人能够指出我做错了什么,以及为什么这会打破页面上的多个实例?

感谢。

注意:代码已经变得非常粗糙,因为我已经破解了试图找出问题所在的东西,并且它只是原型代码开始,所以我为这个烂摊子道歉。

<UserControl
x:Class="App3.BadgeView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App3"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="20"
d:DesignWidth="20">
<Grid>
    <Ellipse x:Name="Border" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Fill="{Binding BadgeBorderBrush}" />
    <Ellipse x:Name="BadgeInner" Margin="2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Fill="{Binding BadgeFillBrush}" />
    <TextBlock x:Name="BadgeValue" Margin="5" HorizontalAlignment="Center" FontSize="10" VerticalAlignment="Center" TextAlignment="Center" TextTrimming="CharacterEllipsis" Foreground="White" Text="{Binding DisplayValue}" />
</Grid>

public sealed partial class BadgeView : UserControl
{

    public DependencyProperty BadgeBorderBrushProperty = DependencyProperty.Register("BadgeBorderBrush", typeof(Brush), typeof(BadgeView), new PropertyMetadata(new SolidColorBrush(Windows.UI.Colors.Yellow)));
    public DependencyProperty BadgeFillBrushProperty = DependencyProperty.Register("BadgeFillBrush", typeof(Brush), typeof(BadgeView), new PropertyMetadata(new SolidColorBrush(Windows.UI.Colors.Orange)));

    public DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(int), typeof(BadgeView), new PropertyMetadata(0, new PropertyChangedCallback(ValueChanged)));
    public DependencyProperty DisplayValueProperty = DependencyProperty.Register("DisplayValue", typeof(int), typeof(BadgeView), new PropertyMetadata(0, DisplayValueChanged));

    private static void DisplayValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        System.Diagnostics.Debug.WriteLine(((BadgeView)d).DisplayValue);
    }

    private Storyboard AnimateBadgeValueCount;
    private DoubleAnimation BadgeValueAnimation;

    public BadgeView()
    {
        this.InitializeComponent();
        this.BadgeValue.DataContext = this.BadgeInner.DataContext = this.Border.DataContext = this;

        AnimateBadgeValueCount = new Storyboard(); ;
        AnimateBadgeValueCount.Duration = TimeSpan.FromSeconds(0.5);
        Storyboard.AllowDependentAnimations = true;
        BadgeValueAnimation = new DoubleAnimation();
        BadgeValueAnimation.Duration = TimeSpan.FromSeconds(0.5);
        BadgeValueAnimation.EnableDependentAnimation = true;
        BadgeValueAnimation.EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut };

        this.AnimateBadgeValueCount.FillBehavior = FillBehavior.Stop;
        this.BadgeValueAnimation.FillBehavior = FillBehavior.Stop;
        AnimateBadgeValueCount.Children.Add(BadgeValueAnimation);

        Storyboard.SetTarget(AnimateBadgeValueCount, this);
        Storyboard.SetTargetProperty(AnimateBadgeValueCount, "DisplayValue");

        this.AnimateBadgeValueCount.Completed += AnimateBadgeValueCount_Completed;
    }

    private void AnimateBadgeValueCount_Completed(object sender, object e)
    {
        this.DisplayValue = this.Value;
    }

    private static void ValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var badgeView = (BadgeView)d;

        badgeView.AnimateValue();
    }

    private void AnimateValue()
    {
        if (Value != DisplayValue)
        {
            this.BadgeValue.DataContext = this.BadgeInner.DataContext = this.Border.DataContext = this;

            this.AnimateBadgeValueCount.Stop();
            this.BadgeValueAnimation.From = this.DisplayValue;
            this.BadgeValueAnimation.To = this.Value;
            this.BadgeValueAnimation.FillBehavior = FillBehavior.Stop;
            //Storyboard.SetTarget(this.AnimateBadgeValueCount, this);
            //Storyboard.SetTargetProperty(this.AnimateBadgeValueCount, "DisplayValue");
            this.AnimateBadgeValueCount.Begin();
        }
    }

    public Brush BadgeBorderBrush
    {
        get { return (Brush)this.GetValue(this.BadgeBorderBrushProperty); }
        set
        {
            this.SetValue(this.BadgeBorderBrushProperty, value);
        }
    }

    public Brush BadgeFillBrush
    {
        get { return (Brush)this.GetValue(this.BadgeFillBrushProperty); }
        set
        {
            this.SetValue(this.BadgeFillBrushProperty, value);
        }
    }

    public int Value
    {
        get { return (int)this.GetValue(ValueProperty); }
        set
        {
            this.SetValue(ValueProperty, value);
        }
    }

    public int DisplayValue
    {
        get { return (int)this.GetValue(DisplayValueProperty); }
        set
        {
            this.SetValue(DisplayValueProperty, value);
        }
    }

}

<Page
x:Class="App3.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App3"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">

<StackPanel Orientation="Vertical">
    <Button Content="Do it" x:Name="DoIt1" Click="DoIt1_Click" />
    <local:BadgeView x:Name="Badge1" Width="20" Height="20" BadgeFillBrush="Blue" />

    <Button Content="Do it" x:Name="DoIt2" Click="DoIt2_Click" />
    <local:BadgeView x:Name="Badge2" Width="20" Height="20" />

    <Button Content="Do it" x:Name="DoIt3" Click="DoIt3_Click" />
    <local:BadgeView x:Name="Badge3" Width="20" Height="20" />

</StackPanel>

    public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();
    }

    private void DoIt1_Click(object sender, RoutedEventArgs e)
    {
        this.Badge1.Value += 10;
    }

    private void DoIt2_Click(object sender, RoutedEventArgs e)
    {
        this.Badge2.Value += 10;
    }

    private void DoIt3_Click(object sender, RoutedEventArgs e)
    {
        this.Badge3.Value += 10;
    }
}

1 个答案:

答案 0 :(得分:1)

我所做的是简化代码并简化并在构造函数内的加载事件中移动datacontext:

this.Loaded += (s, e) =>
        {
            this.DataContext = this;

            AnimateBadgeValueCount = new Storyboard(); ;
            AnimateBadgeValueCount.Duration = TimeSpan.FromSeconds(0.5);
            BadgeValueAnimation = new DoubleAnimation();
            BadgeValueAnimation.Duration = TimeSpan.FromSeconds(0.5);
            //BadgeValueAnimation.EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut };
            AnimateBadgeValueCount.Children.Add(BadgeValueAnimation);

            Storyboard.SetTarget(AnimateBadgeValueCount, this);
            Storyboard.SetTargetProperty(AnimateBadgeValueCount, "DisplayValue");

            this.AnimateBadgeValueCount.Completed += AnimateBadgeValueCount_Completed;
        };


private async void AnimateValue()
    {
        if (Value != DisplayValue)
        {
            this.AnimateBadgeValueCount.Stop();
            this.BadgeValueAnimation.From = this.DisplayValue;
            this.BadgeValueAnimation.To = this.Value;
            BadgeValueAnimation.EnableDependentAnimation = true;

            await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, () =>
             {
                 this.AnimateBadgeValueCount.Begin();
             });
        }
    }

我评论了EasingFunction,它有效,但在我看来比较合适。 这很奇怪,因为如果我在构造函数中设置datacontext它会很糟糕,但内部很好。 如果你试试,请告诉我。