c# - Animar(sin problemas) ScrollViewer mediante programación
windows-runtime winrt-xaml (3)
¿Hay alguna manera de animar sin problemas el desplazamiento vertical de un ScrollViewer
en Windows Phone 8.1 Runtime?
He intentado utilizar el método ScrollViewer.ChangeView()
y el cambio de desplazamiento vertical no está animado, independientemente de si configuro el parámetro disableAnimation
en verdadero o falso.
Por ejemplo: myScrollViewer.ChangeView(null, myScrollViewer.VerticalOffset + p, null, false);
El desplazamiento se cambia sin animación.
También intenté usar un mediador de desplazamiento vertical:
/// <summary>
/// Mediator that forwards Offset property changes on to a ScrollViewer
/// instance to enable the animation of Horizontal/VerticalOffset.
/// </summary>
public sealed class ScrollViewerOffsetMediator : FrameworkElement
{
/// <summary>
/// ScrollViewer instance to forward Offset changes on to.
/// </summary>
public ScrollViewer ScrollViewer
{
get { return (ScrollViewer)GetValue(ScrollViewerProperty); }
set { SetValue(ScrollViewerProperty, value); }
}
public static readonly DependencyProperty ScrollViewerProperty =
DependencyProperty.Register("ScrollViewer",
typeof(ScrollViewer),
typeof(ScrollViewerOffsetMediator),
new PropertyMetadata(null, OnScrollViewerChanged));
private static void OnScrollViewerChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var mediator = (ScrollViewerOffsetMediator)o;
var scrollViewer = (ScrollViewer)(e.NewValue);
if (null != scrollViewer)
{
scrollViewer.ScrollToVerticalOffset(mediator.VerticalOffset);
}
}
/// <summary>
/// VerticalOffset property to forward to the ScrollViewer.
/// </summary>
public double VerticalOffset
{
get { return (double)GetValue(VerticalOffsetProperty); }
set { SetValue(VerticalOffsetProperty, value); }
}
public static readonly DependencyProperty VerticalOffsetProperty =
DependencyProperty.Register("VerticalOffset",
typeof(double),
typeof(ScrollViewerOffsetMediator),
new PropertyMetadata(0.0, OnVerticalOffsetChanged));
public static void OnVerticalOffsetChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var mediator = (ScrollViewerOffsetMediator)o;
if (null != mediator.ScrollViewer)
{
mediator.ScrollViewer.ScrollToVerticalOffset((double)(e.NewValue));
}
}
/// <summary>
/// Multiplier for ScrollableHeight property to forward to the ScrollViewer.
/// </summary>
/// <remarks>
/// 0.0 means "scrolled to top"; 1.0 means "scrolled to bottom".
/// </remarks>
public double ScrollableHeightMultiplier
{
get { return (double)GetValue(ScrollableHeightMultiplierProperty); }
set { SetValue(ScrollableHeightMultiplierProperty, value); }
}
public static readonly DependencyProperty ScrollableHeightMultiplierProperty =
DependencyProperty.Register("ScrollableHeightMultiplier",
typeof(double),
typeof(ScrollViewerOffsetMediator),
new PropertyMetadata(0.0, OnScrollableHeightMultiplierChanged));
public static void OnScrollableHeightMultiplierChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var mediator = (ScrollViewerOffsetMediator)o;
var scrollViewer = mediator.ScrollViewer;
if (null != scrollViewer)
{
scrollViewer.ScrollToVerticalOffset((double)(e.NewValue) * scrollViewer.ScrollableHeight);
}
}
}
y puedo animar la propiedad VerticalOffset
con DoubleAnimation
:
Storyboard sb = new Storyboard();
DoubleAnimation da = new DoubleAnimation();
da.EnableDependentAnimation = true;
da.From = Mediator.ScrollViewer.VerticalOffset;
da.To = da.From + p;
da.Duration = new Duration(TimeSpan.FromMilliseconds(300));
da.EasingFunction = new ExponentialEase() { EasingMode = EasingMode.EaseOut };
Storyboard.SetTarget(da, Mediator);
Storyboard.SetTargetProperty(da, "(Mediator.VerticalOffset)");
sb.Children.Add(da);
sb.Begin();
Mediador se declara en XAML. Pero esta animación no es suave en mi dispositivo (Lumia 930).
Creo que esa pregunta ya ha sido respondida aquí:
Desplazamiento animado (Suave) en ScrollViewer
También está el WinRT XAML Toolki, que proporciona "una forma de desplazar un ScrollViewer a una compensación especificada con animación":
Debe seguir con ChangeView
para desplazar animaciones independientemente de si la virtualización de datos está ChangeView
o no.
Sin ver el código donde ChangeView
no funciona, es un poco difícil adivinar lo que realmente está pasando, pero hay un par de cosas que puedes probar.
El primer enfoque es agregar un Task.Delay(1)
antes de llamar a ChangeView
, solo para darle al sistema operativo algo de tiempo para finalizar otras tareas de IU simultáneas.
await Task.Delay(1);
scrollViewer.ChangeView(null, scrollViewer.ScrollableHeight, null, false);
El segundo enfoque es un poco más complejo. Lo que noté es que, cuando tiene muchos elementos complejos en ListView
, la animación de desplazamiento del primer elemento al último (del método ChangeView
) no es muy ChangeView
.
Esto se debe a que ListView
primero necesita realizar / renderizar muchos elementos a lo largo del camino debido a la virtualización de datos y luego realiza el desplazamiento animado. No es muy eficiente en mi humilde opinión.
Lo que se me ocurrió es esto: primero, use un ListView.ScrollIntoView
no animado para desplazarse al último elemento solo para que se realice. Luego, llame a ChangeView
para mover el desplazamiento hasta un tamaño del ActualHeight * 2
del ListView
con la animación desactivada (puede cambiarlo al tamaño que desee en función de la experiencia de desplazamiento de su aplicación). Finalmente, vuelva a llamar a ChangeView
para volver al final, con animación esta vez. Hacer esto proporcionará una experiencia de desplazamiento mucho mejor porque la distancia de desplazamiento es solo la ActualHeight
del ListView
.
Tenga en cuenta que cuando el elemento al que desea desplazarse ya se haya realizado en la interfaz de usuario, no desea hacer nada más arriba. Simplemente calcula la distancia entre este elemento y la parte superior del ScrollViewer
y llama a ChangeView
para desplazarte a él.
Ya completé la lógica de arriba en la sección Actualización 2 de esta respuesta (gracias a esta pregunta me di cuenta de que mi respuesta inicial no funciona cuando la virtualización está activada: p). Déjame saber cómo vas.
Con ScrollToVerticalOffset en desuso / obsoleto en compilaciones más recientes de Windows 10 (dejando que el control de extensión ScrollViewOffSetMediator ya no funcione), y el nuevo método ChangeView que no proporciona una animación fluida o controlable, se necesita una nueva solución. Por favor, consulte mi respuesta aquí, que le permite a uno animar y hacer zoom suavemente el ScrollViewer y sus contenidos a cualquier posición deseada, independientemente de dónde el usuario final de la aplicación tenga las barras de desplazamiento inicialmente posicionadas: