重构动画以使用Task.Delay而不是Timer

时间:2016-10-27 11:14:57

标签: c# wpf async-await

在C#中学习async / await时,我正在尝试更改WPF自定义进度条以使用Task.Delay而不是Timer来为其设置动画。问题是进度条似乎停留在动画的“第一帧”上。

如果不使用Task.Run()调用Animate(),它会工作但是我收到警告因为没有等待此调用,所以在调用完成之前会继续执行当前方法。考虑将'await'运算符应用于调用的结果。并且我不能在Activate()方法中使用await而不使其成为异步,这反过来意味着使整个方法链异步,这似乎是奇怪的。避免警告的另一种方法是使Animate()方法 async void 而不是异步任务,但这似乎强烈反对我读过的内容到目前为止。

解决这个问题的最佳做法是什么?

public class IndeterminateProgressBar : System.Windows.Controls.Control
{
    private bool _active = false;
    private Ellipse _c0;
    private Ellipse _c1;
    private Ellipse _c2;
    private Ellipse _c3;
    private Ellipse _c4;
    private Ellipse _c5;
    private Ellipse _c6;
    private Ellipse _c7;
    private Ellipse _c8;
    private RotateTransform _spinnerRotate;

    public static readonly DependencyProperty ActiveProperty = DependencyProperty.Register("Active", typeof(bool), typeof(IndeterminateProgressBar), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(IndeterminateProgressBar_OnActivePropertyChanged)));
    private static void IndeterminateProgressBar_OnActivePropertyChanged(DependencyObject dObj, DependencyPropertyChangedEventArgs e)
    {
        IndeterminateProgressBar indeterminateProgressBar = dObj as IndeterminateProgressBar;
        indeterminateProgressBar.Active = (bool)e.NewValue;
    }
    public bool Active
    {
        get
        {
            return (bool)GetValue(ActiveProperty);
        }
        set
        {
            SetValue(ActiveProperty, value);
            ChangeState(value);
        }
    }

    static IndeterminateProgressBar()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(IndeterminateProgressBar), new FrameworkPropertyMetadata(typeof(IndeterminateProgressBar)));
    }

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();

        _c0 = GetTemplateChild("Part0") as Ellipse;
        _c1 = GetTemplateChild("Part1") as Ellipse;
        _c2 = GetTemplateChild("Part2") as Ellipse;
        _c3 = GetTemplateChild("Part3") as Ellipse;
        _c4 = GetTemplateChild("Part4") as Ellipse;
        _c5 = GetTemplateChild("Part5") as Ellipse;
        _c6 = GetTemplateChild("Part6") as Ellipse;
        _c7 = GetTemplateChild("Part7") as Ellipse;
        _c8 = GetTemplateChild("Part8") as Ellipse;
        _spinnerRotate = GetTemplateChild("Spinner") as RotateTransform;

        const double offset = Math.PI;
        const double step = Math.PI * 2 / 10.0;

        SetPosition(_c0, offset, 0.0, step);
        SetPosition(_c1, offset, 1.0, step);
        SetPosition(_c2, offset, 2.0, step);
        SetPosition(_c3, offset, 3.0, step);
        SetPosition(_c4, offset, 4.0, step);
        SetPosition(_c5, offset, 5.0, step);
        SetPosition(_c6, offset, 6.0, step);
        SetPosition(_c7, offset, 7.0, step);
        SetPosition(_c8, offset, 8.0, step);
    }

    private void SetPosition(Ellipse ellipse, double offset, double posOffSet, double step)
    {
        ellipse.SetValue(Canvas.LeftProperty, 50.0 + Math.Sin(offset + posOffSet * step) * 50.0); ellipse.SetValue(Canvas.TopProperty, 50 + Math.Cos(offset + posOffSet * step) * 50.0);
    }

    private async Task Animate()
    {
        while (_active)
        {
            if (_spinnerRotate != null)
                _spinnerRotate.Angle = (_spinnerRotate.Angle + 36) % 360;

            await Task.Delay(75);
        }
    }

    private void ChangeState(bool active)
    {
        if (active)
            Activate();
        else
            Deactivate();
    }

    private void Activate()
    {
        if (!_active)
        {
            _active = true;

            Task.Run(async () => await Animate());

            Visibility = Visibility.Visible;
        }
    }

    private void Deactivate()
    {
        if (_active)
        {
            Visibility = Visibility.Collapsed;

            _active = false;
        }
    }
}

0 个答案:

没有答案