I have class Model
public class Model : INotifyPropertyChanged
...
private GridLength detailsPanelHeight { get; set; }
public GridLength DetailsPanelHeight
{
get { return detailsPanelHeight; }
set
{
if (!GridLength.Equals(detailsPanelHeight, value))
{
detailsPanelHeight = value;
OnPropertyChanged("DetailsPanelHeight");
}
}
}
...
part of XAML code:
<RowDefinition Height="{Binding DetailsPanelHeight}" />
code to do animation (changing row height smoothly):
var animate = new Animation(d => currentItem.DetailsPanelHeight = d, 0, 100);
animate.Commit(this, "ExpandAnimation", 50, 1000, Easing.SpringOut);
code to collapse the row:
var animate = new Animation(d => currentItem.DetailsPanelHeight = d, 100, 0);
animate.Commit(this, "CollapseAnimation", 50, 1000, Easing.SpringOut);
It works for the first time, but for the second time i get an error: "value is less than 0 or is not a number\nParameter name: value". I see d
value is less than zero.
What can i do to fix this problem?
I've used something like this that worked perfectly for me. I hope it fits for you too.
This animation collapses the view cell while calling a command after a delete action invocation. Here's the code:
The tap event handler:
private async void RemoveButtonTapped(object sender, EventArgs e)
{
Parallel.Invoke(() =>
{
if (RemoveCommand?.CanExecute(RemoveCommandParameter) ?? false)
RemoveCommand.Execute(RemoveCommandParameter);
},
AnimatedDestruction);
}
Animation method
private async void AnimatedDestruction()
{
uint transitionTime = 300;
decimal delayFactor = 1.2m;
// Note: stackPanel is the viewCell's top-level container
await Task.WhenAll(
stackPanel.FadeTo(0, Convert.ToUInt32(transitionTime * delayFactor), Easing.CubicInOut),
View.InterpolateValue(stackPanel.Height, 0, Transition, transitionTime, Easing.CubicInOut)
);
}
Transition callback function
private void Transition(double value)
{
const double minHeightValue = 0.001;
value = value <= minHeightValue ? minHeightValue : value;
Height = value;
ForceUpdateSize();
}
The InterpolateValue
function as an extension method (very reusable)
public static Task<bool> InterpolateValue(this View view, double initialValue, double endValue, Action<double> transformIteration, uint length, Easing easing)
{
Task<bool> ret = new Task<bool>(() => false);
if (!view.AnimationIsRunning(nameof(InterpolateValue)))
{
try
{
easing = easing ?? Easing.Linear;
var taskCompletionSource = new TaskCompletionSource<bool>();
view.Animate(nameof(InterpolateValue), ((_double) => initialValue - (initialValue * _double)), transformIteration, 16, length, easing, (v, c) => taskCompletionSource.SetResult(c));
ret = taskCompletionSource.Task;
}
catch
{
// supress animation overlapping errors
}
}
return ret;
}
I hope it works for you.