在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;
}
}
}