I'm trying to create a ProgressBar
that can animate from 0 to some value. Everything works fine most of time ...
I'm using the code from Owen Johnson. Have some changes like:
public static readonly DependencyProperty SmoothValueProperty =
DependencyProperty.RegisterAttached("SmoothValue", typeof(double), typeof(ProgressBarSmoother),
new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, changing, coerceValueCallback, false, System.Windows.Data.UpdateSourceTrigger.PropertyChanged));
private static object coerceValueCallback(DependencyObject d, object baseValue)
{
Console.WriteLine($"{ (double)baseValue } coercevalue");
return baseValue;
}
public static void changing(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ProgressBarSmoother obj = d as ProgressBarSmoother;
Console.WriteLine($"Contador: {++obj.Count}. Valor novo: { e.NewValue }");
if ((double)e.NewValue == 0)
{
obj.ResetAnimation();
Console.WriteLine($"Contador: {++obj.Count}");
obj.Value = 0;
return;
}
obj.Animation = new DoubleAnimation(0d, (double)e.NewValue, TimeSpan.FromMilliseconds((double)e.NewValue));
obj.Animation.Completed += (sender, arg) =>
{
obj.Animation = null;
obj.Value = 0;
obj.BeginAnimation(ValueProperty, null, HandoffBehavior.SnapshotAndReplace);
Console.WriteLine("Animation completed");
};
obj.Foreground = new SolidColorBrush(Colors.Red);
obj.BeginAnimation(ValueProperty, obj.Animation, HandoffBehavior.SnapshotAndReplace);
Console.WriteLine($"Contador: {++obj.Count}");
}
// Basically the code that changes the progressbar value is this
private void Controller_OnPlayerChangedState(object sender, MultiPlayerController.OnChangeStateEventArgs e)
{
if (e.NewState == AudioPlayer_State.Playing)
{
Duration = AudioPanel.Controller.GetDuration();
panel.SetCurrentPositionAsync (Duration);
}
else if(e.NewState != AudioPlayer_State.Playing && e.NewState != AudioPlayer_State.Paused)
{
panel.SetCurrentPositionAsync ((long)0);
//System.Threading.Thread.Sleep(100);
}
}
(have a lot of code just for test and see whats happining)
I've an Audio Player and the progressbar
is to see the actual position of audio (cannot be paused, only stopped). The stop button works fine. The problem is when Repeat status it's activated.
I receive a event from my dll of Stopped (set value to 0 and my animation goes away) and a few moments later (less then 0ms most of time) a Playing, so I have to update the progressbar
animation with the duration.
Sometimes my animation is not called and I put some logs to discover wht. When is not called, CoerceValueCallback
happens just one time. And when works, called two times: one with 0, when I destroy the older animation and other with the new duration (same as before).
Just for test, I've put a Thread.Sleep
inside the code which changes the value to new duration and works like a charm. My thinking is the delay to change the new value... Basically I need to set to 0 and after to new value.
Can I remove the check of older value needs to be different from newer? Or I can update instantly the value?
Solution until this moment
I created a public function on my control ProgressBarSmoother
called ResetAnimation
public void ResetAnimation()
{
this.Dispatcher.Invoke(() =>
{
SmoothValue = 0;
});
}
And created a getter/setter
of SmoothValue
public double SmoothValue
{
get => (double)GetValue(SmoothValueProperty);
set => SetValue(SmoothValueProperty, value);
}
On my SetCurrentPositionAsync
,
public void SetCurrentPositionAsync (long position)
{
if (position == 0)
progressBar.ResetAnimation();
CurrentPositionAsync = position;
}
And my changing:
public static void changing(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ProgressBarSmoother obj = d as ProgressBarSmoother;
if ((double)e.NewValue == 0)
{
obj.BeginAnimation(ValueProperty, null, HandoffBehavior.SnapshotAndReplace);
return;
}
obj.Animation = new DoubleAnimation(0d, (double)e.NewValue, TimeSpan.FromMilliseconds((double)e.NewValue));
obj.BeginAnimation(ValueProperty, obj.Animation, HandoffBehavior.SnapshotAndReplace);
}
So.. When I set 0, I instantly call ResetAnimation
that can set the Value of property and set on field correctly instead of calling OnPropertyChanged
and having some delay.
My Controller_OnPlayerChangedState
:
private void Controller_OnPlayerChangedState(object sender, MultiPlayerController.OnChangeStateEventArgs e)
{
if (e.NewState == AudioPlayer_State.Playing)
{
Duration = AudioPanel.Controller.GetDuration();
panel.SetCurrentPositionAsync((long)Duration);
}
else if(e.NewState != AudioPlayer_State.Playing && e.NewState != AudioPlayer_State.Paused)
panel.SetCurrentPositionAsync(0);
}
Works fine but I don't know if I can have another solution.