example entry xaml listview windows-phone-8.1 mvvm-light fetch

xaml - entry - xamarin view model example



Deje que un ViewModel sepa cuando el usuario se desplazó al final de un ListView en XAML con MVVM (2)

¿Hay una manera fácil de hacer esto? Internet tiene algunas muestras pero no para las aplicaciones MVVM Windows Phone 8.1 Runtime.

Mi ListView tiene originalmente 20 elementos y cuando un usuario se desplaza hasta el final, se deben cargar otros 20 elementos. Pero, ¿cómo dejo que mi ViewModel sepa que se ha llegado al final de ListView?


Si su caso está cargando más artículos al llegar al final de la lista para crear "carga diferida", entonces eche un vistazo a la interfaz ISupportIncrementalLoading . Es una interfaz que implementa en su colección y ListView la usa automáticamente para cargar más elementos cuando llega el final. Aquí hay un ejemplo: https://marcominerva.wordpress.com/2013/05/22/implementing-the-isupportincrementalloading-interface-in-awindow-store-app/ .

Personalmente, no me gusta este enfoque, ya que tengo una colección que sabe cómo cargar datos. Hay un ExtendedListViewControl ( http://www.tareqateik.com/extendedlistview-control-for-universal-apps-pull-to-refresh-load-more-data-on-demand ) creado por Tareq Ateik disparando un evento cuando el se alcanza el final de la lista. Por lo tanto, cuando se active el evento, trátelo en su ViewModel (si usa Caliburn.Micro, puede "suscribirse" directamente al evento) o usar mensajes para enviar un mensaje al ViewModel.

O simplemente maneje el evento en el código subyacente y llame al método deseado en su ViewModel desde el código subyacente.


Basado en una clase Helper implementada en WinRT Xaml Toolkit, puede obtener una referencia a la scrollBar vertical de scrollBar dentro del control ListView , luego adjuntarle un controlador de eventos, para ayudarlo a detectar cuándo se llega a la parte inferior de ListView todo lo que se implementará en el código detrás de su página, (que no violará ninguna regla mvvm ya que esta es una lógica relacionada con la vista) y una vez que se llegue al final enviará un NotificationMessage al ViewModel de la Página usando el Clase Mvvm-light Messenger , aquí cómo hacerlo en pasos:

Paso 1

agregue la siguiente clase de ayuda (desde Xaml toolkit) a su proyecto, tiene algunos métodos de extensión y obtendrá acceso a la barra de desplazamiento:

public static class VisualTreeHelperExtensions { public static T GetFirstDescendantOfType<T>(this DependencyObject start) where T : DependencyObject { return start.GetDescendantsOfType<T>().FirstOrDefault(); } public static IEnumerable<T> GetDescendantsOfType<T>(this DependencyObject start) where T : DependencyObject { return start.GetDescendants().OfType<T>(); } public static IEnumerable<DependencyObject> GetDescendants(this DependencyObject start) { var queue = new Queue<DependencyObject>(); var count = VisualTreeHelper.GetChildrenCount(start); for (int i = 0; i < count; i++) { var child = VisualTreeHelper.GetChild(start, i); yield return child; queue.Enqueue(child); } while (queue.Count > 0) { var parent = queue.Dequeue(); var count2 = VisualTreeHelper.GetChildrenCount(parent); for (int i = 0; i < count2; i++) { var child = VisualTreeHelper.GetChild(parent, i); yield return child; queue.Enqueue(child); } } } public static T GetFirstAncestorOfType<T>(this DependencyObject start) where T : DependencyObject { return start.GetAncestorsOfType<T>().FirstOrDefault(); } public static IEnumerable<T> GetAncestorsOfType<T>(this DependencyObject start) where T : DependencyObject { return start.GetAncestors().OfType<T>(); } public static IEnumerable<DependencyObject> GetAncestors(this DependencyObject start) { var parent = VisualTreeHelper.GetParent(start); while (parent != null) { yield return parent; parent = VisualTreeHelper.GetParent(parent); } } public static bool IsInVisualTree(this DependencyObject dob) { return Window.Current.Content != null && dob.GetAncestors().Contains(Window.Current.Content); } public static Rect GetBoundingRect(this FrameworkElement dob, FrameworkElement relativeTo = null) { if (relativeTo == null) { relativeTo = Window.Current.Content as FrameworkElement; } if (relativeTo == null) { throw new InvalidOperationException("Element not in visual tree."); } if (dob == relativeTo) return new Rect(0, 0, relativeTo.ActualWidth, relativeTo.ActualHeight); var ancestors = dob.GetAncestors().ToArray(); if (!ancestors.Contains(relativeTo)) { throw new InvalidOperationException("Element not in visual tree."); } var pos = dob .TransformToVisual(relativeTo) .TransformPoint(new Point()); var pos2 = dob .TransformToVisual(relativeTo) .TransformPoint( new Point( dob.ActualWidth, dob.ActualHeight)); return new Rect(pos, pos2); } }

Paso 2

agregue listView a su xaml y un controlador de eventos cargado,

DataContext="{Binding Main, Source={StaticResource Locator}}" Loaded="MainPage_OnLoaded"> <Grid> <ListView ItemsSource="{Binding ItemsCollection}" x:Name="ListV" VerticalAlignment="Center" Height="200" HorizontalAlignment="Center" > </ListView> </Grid> </common:PageBase>

Paso 3

En el código de su página detrás agregue los controladores necesarios

public MainPage() { InitializeComponent(); } private void MainPage_OnLoaded(object sender, RoutedEventArgs e) { var scrollViewer = ListV.GetFirstDescendantOfType<ScrollViewer>(); var scrollbars = scrollViewer.GetDescendantsOfType<ScrollBar>().ToList(); var verticalBar = scrollbars.FirstOrDefault(x => x.Orientation == Orientation.Vertical); if (verticalBar != null) verticalBar.Scroll += BarScroll; } async void BarScroll(object sender, ScrollEventArgs e) { if (e.ScrollEventType != ScrollEventType.EndScroll) return; var bar = sender as ScrollBar; if (bar == null) return; System.Diagnostics.Debug.WriteLine("Scrolling ended"); if (e.NewValue >= bar.Maximum) { System.Diagnostics.Debug.WriteLine("We are at the bottom"); Messenger.Default.Send<NotificationMessage>(new NotificationMessage("LoadMore")); } else { System.Diagnostics.Debug.WriteLine("We are away from the bottom"); } } }

En el código anterior, tenga en cuenta que una vez que se detecte la parte inferior, enviaré un NotificationMessage utilizando la clase Messenger

Messenger.Default.Send<NotificationMessage>(new NotificationMessage("LoadMore"));

Etapa 4

en el modelo Ver de la página, agregue el código que carga los elementos nuevos a ListView una vez que se recibe el mensaje de notificación

public class MainViewModel : ViewModelBase { private ObservableCollection<String> _itemsCollection = new ObservableCollection<string>() { "Item 1", "Item 2", "Item 3", "Item 4" }; public ObservableCollection<String> ItemsCollection { get { return _itemsCollection; } set { if (_itemsCollection == value) { return; } _itemsCollection = value; RaisePropertyChanged(); } } public async Task LoadMoreItems() { ItemsCollection.Add("Item " + ItemsCollection.Count); ItemsCollection.Add("Item " + ItemsCollection.Count); ItemsCollection.Add("Item " + ItemsCollection.Count); ItemsCollection.Add("Item " + ItemsCollection.Count); ItemsCollection.Add("Item " + ItemsCollection.Count); } public MainViewModel() { Messenger.Default.Register<NotificationMessage>(this, async m => { switch (m.Notification) { case "LoadMore": await LoadMoreItems(); break; } }); } }

El crédito de cómo detectar el fondo va al siguiente artículo .

ACTUALIZAR

De hecho, como se menciona en los comentarios, el código anterior funciona perfectamente en una aplicación WinRt Store, pero por alguna razón no funcionará en un proyecto de Windows Phone (winrt 8.1), y parece que el evento scroll en la ScrollBar vertical no está disparando por alguna razón, tengo una solución para eso, en su lugar, manejo el evento ViewChanged del ScrollViewer dentro de ListView , cambie el código detrás de su página a lo siguiente:

public sealed partial class MainPage { public MainPage() { InitializeComponent(); } private void MainPage_OnLoaded(object sender, RoutedEventArgs e) { var scrollViewer = ListV.GetFirstDescendantOfType<ScrollViewer>(); scrollViewer.ViewChanged+=BarScroll; } private void BarScroll(object sender, ScrollViewerViewChangedEventArgs e) { var scrollbars = (sender as ScrollViewer).GetDescendantsOfType<ScrollBar>().ToList(); var verticalBar = scrollbars.FirstOrDefault(x => x.Orientation == Orientation.Vertical); if (verticalBar.Value >= verticalBar.Maximum) { System.Diagnostics.Debug.WriteLine("We are at the bottom"); Messenger.Default.Send<NotificationMessage>(new NotificationMessage("LoadMore")); } } }

y mantener el resto tal como está.