5

Ho i miei ViewModels all'interno di un PCL, perché sto sviluppando un'applicazione Windows 8.1 e Windows Phone in parallelo. Ho una lista di cose all'interno del mio ViewModel come ObservableCollection.ObservationCollection che implementa ISupportIncrementalLoading all'interno di ViewModel all'interno di PCL utilizzando l'architettura MVVM per il supporto WinRT e WP8/WinPRT

Ho un GridView all'interno di una pagina all'interno di un progetto di Windows 8.1. Desidero caricare in modo incrementale elementi dal mio elenco di cose nel mio ViewModel. Normalmente avrei implementare ISupportIncrementalLoading all'interno di una sottoclasse personalizzata di ObservableCollection, però, dato che il mio ViewModel è all'interno di un PCL, ISupportIncrementalLoading non è disponibile (non è supportato da WP8).

Quindi la mia domanda è, qualcuno ha qualche suggerimento su come posso creare un qualche tipo di convertitore, adattatore o livello di astrazione tra il binding ItemsSource di GridView e la proprietà Observable Things del mio ViewModel che implementerebbe ISupportIncrementalLoading e quindi chiama il metodo LoadMoreThings di ViewModel e passa gli elementi a GridView.

Mi sento come se ci sia qualche soluzione, come ad esempio la creazione di un ISupportIncrementalLoading personalizzato all'interno del mio visualizzare i modelli PCL e quindi avere il delegato Visualizza strato ad esso.

grazie

risposta

16

Alla fine, ho utilizzato il modello di fabbrica astratto. I fatti sono:

  • Non è possibile fare riferimento al livello Vista dallo strato PCL ViewModel, come lo strato di VM non dovrebbe occuparsi di livello della Vista. Uno dei vantaggi di questo è che è possibile creare un altro utente del livello ViewModel senza alcuna dipendenza dalla piattaforma di destinazione. per esempio. creare un'applicazione per Windows 8 e Windows Phone 8 dal retro di un progetto PCL della libreria ViewModel.

  • Il GridView è un componente WinRT che può essere associato a un ObservableCollection<T>. è disponibile all'interno di sia il livello Visualizza e il livello ViewModel. Se si desidera supportare il caricamento incrementale all'interno della vostra app (che è un must per i grandi insiemi di dati), allora avete bisogno di creare una sottoclasse speciale di ObservableCollection<T> che implementa ISupportIncrementalLoading. Quello che noi vogliamo fare è semplicemente creare quella sottoclasse all'interno del progetto ViewModel e il gioco è fatto. Ma non possiamo fare questo perché ISupportIncrementalLoading è disponibile solo all'interno di un progetto di WinRT.

Questo problema può essere risolto utilizzando il modello di fabbrica astratto. Tutto ciò che il ViewModel desidera veramente è uno , ma il livello Visualizza richiede una ObservableCollection che implementa ISupportIncrementalLoading. Quindi la risposta è definire un'interfaccia nel livello ViewModel che dia al ViewModel quello che vuole; chiamiamolo IPortabilityFactory. Quindi nel livello Visualizza definire un'implementazione concreta di IPortabilityFactory denominata PortabilityFactory. Utilizzare un IoC nel livello View per mappare IPortabilityFactory (interfaccia ViewModel) su PortabilityFactory (Visualizza livello concreto impl.).

Sulla costruttore della classe ViewModel, hanno un'istanza IPortabilityFactory iniettato. Ora ViewModel ha una fabbrica che gli darà un'istanza ObservableCollection<T>.

Ora invece di chiamare new ObservableCollection<Thing>() nel ViewModel chiamate factory.GetIncrementalCollection<Thing>(...).

OK, quindi abbiamo finito con il livello ViewModel; ora abbiamo bisogno di quella implementazione personalizzata di ObservableCollection<T>. Si chiama IncrementalLoadingCollection ed è definito nel livello View. Implementa ISupportIncrementalLoading.

Ecco il codice e le spiegazioni, insieme a un'implementazione di ISupportIncrementalLoading.

Nel livello ViewModel (PCL) ho un'interfaccia di fabbrica astratta.

public interface IPortabilityFactory 
{ 
    ObservableCollection<T> GetIncrementalCollection<T>(int take, Func<int, Task<List<T>>> loadMoreItems, Action onBatchStart, Action<List<T>> onBatchComplete); 
} 

Nello strato Vista (Windows 8 app, in questo caso) a implementare una fabbrica di cemento come questo:

public class PortabilityFactory : IPortabilityFactory 
{ 
    public ObservableCollection<T> GetIncrementalCollection<T>(int take, Func<int, Task<List<T>>> loadMoreItems, Action onBatchStart, Action<List<T>> onBatchComplete) 
    { 
     return new IncrementalLoadingCollection<T>(take, loadMoreItems, onBatchStart, onBatchComplete); 
    } 
} 

Anche in questo caso, all'interno dello strato vista, mi capita di usare l'unità per il mio CIO . Quando viene creato il IoC, mappo IPortabilityFactory (in PCL) a PortabilityFactory (nel livello View, il progetto dell'app).

Container.RegisterType<IPortabilityFactory, PortabilityFactory>(new ContainerControlledLifetimeManager()); 

Ora abbiamo bisogno di creare una sottoclasse di ObservableCollection ed ecco il codice:

public class IncrementalLoadingCollection<T> 
     : ObservableCollection<T>, ISupportIncrementalLoading 
    { 
     private Func<int, Task<List<T>>> _loadMoreItems = null; 
     private Action<List<T>> _onBatchComplete = null; 
     private Action _onBatchStart = null; 


     /// <summary> 
     /// How many records to currently skip 
     /// </summary> 
     private int Skip { get; set; } 

     /// <summary> 
     /// The max number of items to get per batch 
     /// </summary> 
     private int Take { get; set; } 

     /// <summary> 
     /// The number of items in the last batch retrieved 
     /// </summary> 
     private int VirtualCount { get; set; } 

     /// <summary> 
     /// .ctor 
     /// </summary> 
     /// <param name="take">How many items to take per batch</param> 
     /// <param name="loadMoreItems">The load more items function</param> 
     public IncrementalLoadingCollection(int take, Func<int, Task<List<T>>> loadMoreItems, Action onBatchStart, Action<List<T>> onBatchComplete) 
     { 
      Take = take; 
      _loadMoreItems = loadMoreItems; 
      _onBatchStart = onBatchStart; 
      _onBatchComplete = onBatchComplete; 
      VirtualCount = take; 
     } 

     /// <summary> 
     /// Returns whether there are more items (if the current batch size is equal to the amount retrieved then YES) 
     /// </summary> 
     public bool HasMoreItems 
     { 
      get { return this.VirtualCount >= Take; } 
     } 

     public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count) 
     { 
      CoreDispatcher dispatcher = Window.Current.Dispatcher; 
      _onBatchStart(); // This is the UI thread 

      return Task.Run<LoadMoreItemsResult>(
       async() => 
       { 
        var result = await _loadMoreItems(Skip); 
        this.VirtualCount = result.Count; 
        Skip += Take; 

        await dispatcher.RunAsync(
         CoreDispatcherPriority.Normal, 
         () => 
         { 
          foreach (T item in result) this.Add(item); 
          _onBatchComplete(result); // This is the UI thread 
         }); 

        return new LoadMoreItemsResult() { Count = (uint)result.Count }; 

       }).AsAsyncOperation<LoadMoreItemsResult>(); 
     } 
    } 

costruttore di IncrementalLoadingCollection chiede quattro parametri che saranno forniti dal ViewModel tramite la fabbrica:

  • prendere - questa è la dimensione della pagina

  • loadMoreItems - questo è un riferimento delegato a una funzione all'interno del ViewModel che recupererà il prossimo gruppo di oggetti (soprattutto, questa funzione non verrà eseguito all'interno del thread UI)

  • onBatchStart - questo verrà richiamato appena prima che i loadMoreItems il metodo è chiamato. Questo mi consente di apportare modifiche alle proprietà sul ViewModel che potrebbero influire sulla vista. ad esempio, disporre di una proprietà IsProcessing osservabile associata alla proprietà Visibility di una barra di avanzamento.

  • onBatchComplete - questo verrà richiamato subito dopo il recupero dell'ultimo batch e inoltrato gli articoli. In modo cruciale, questa funzione verrà richiamata sul thread dell'interfaccia utente.

Nello strato ViewModel, il mio ViewModel ha un costruttore su di esso che accetta un oggetto IPortabilityFactory:

public const string IsProcessingPropertyName = "IsProcessing"; 

private bool _isProcessing = false; 
public bool IsProcessing 
{ 
    get 
    { 
     return _isProcessing; 
    } 
    set 
    { 
     if (_isProcessing == value) 
     { 
      return; 
     } 
     RaisePropertyChanging(IsProcessingPropertyName); 
     _isProcessing = value; 
     RaisePropertyChanged(IsProcessingPropertyName); 
     } 
} 

    private IPortabilityFactory _factory = null; 
    public ViewModel(IPortabilityFactory factory) 
    { 
     _factory = factory; 
     Initialize(); 
    } 


    private async void Initialize() 
    { 
     Things = _factory.GetIncrementalCollection<Thing>(10, LoadThings, 
      () => IsProcessing = true, BatchLoaded); 
    } 

    private void BatchLoaded(List<Thing> batch) 
    { 
     IsProcessing = false; 
    } 

    private async Task<List<Thing>> LoadThings(int skip) 
    { 
     var items = await _service.GetThings(skip, 10 /*page size*/); 
     return items; 
    } 

Spero che questo aiuta qualcuno.

Problemi correlati