Mi sento come se lo stessi facendo nel modo giusto, ma non sono sicuro. Sto caricando oggetti in una ReactiveList nel mio ViewModel in modo asincrono. Attraverso Xamarin Forms, ho associato l'Elenco alla proprietà ItemSource di un ListView in Xamarin Forms. Ho anche una casella di ricerca che cancellerà la ReactiveList e aggiungerà nuovi elementi alla ReactiveList.Binding ReactiveList <T> a ListView in Xamarin Forms
La prima volta che apro la vista, nessuna interazione fa caricare la lista e un pulsante associato a un ReactiveCommand per caricare più elementi in ReactiveList è disabilitato.
La seconda volta che apro la ViewViewView esegue quasi immediatamente gli oggetti nel nuovo ViewModel, ma ancora, le interazioni non sembrano funzionare. Tuttavia, la modifica della casella di ricerca in realtà elimina gli elementi da ListView, ma gli elementi non vengono aggiunti.
Questo comportamento è su iOS. Sono ReactiveUI 6.3.1 e Xamarin Forms 1.3.2.6316.
qui è il problema semplificata:
public class MyViewModel : ReactiveObject, IRoutableViewModel
{
public MyViewModel(IScreen hostScreen = null)
{
HostScreen = hostScreen ?? Locator.Current.GetService<IScreen>();
List = new ReactiveList<ItemViewModel>()
{
ChangeTrackingEnabled = true
};
//This never gets shown
List.Add(new ItemViewModel()
{
Name = "TEST"
});
//Tried doing this on the main thread: same behavior
LoadItemsCommand = ReactiveCommand.CreateAsyncTask(_ => GetItems()), RxApp.TaskpoolScheduler);
LoadItemsCommand
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(results =>
{
//debugger breaks here in EVERY case and successfully changes List but doesn't necessarily affect the view
try
{
List.AddRange(results.Select(e => new ItemViewModel()
{
Name = e.Name
}));
}
catch (Exception ex)
{
//breakpoint does not break here.
Debug.WriteLine(ex.Message);
throw;
}
});
//No exceptions here either
LoadItemsCommand.ThrownExceptions
.Select(ex => new UserError("Error", "Please check your Internet connection"))
.Subscribe(Observer.Create<UserError>(x => UserError.Throw(x)));
this.WhenAnyValue(e => e.SearchText).Subscribe(e => ResetPage());
}
private Task<IEnumerable<Item>> GetItems()
{
//asynchronously get items
return ...;
}
private int ResetPage()
{
List.Clear();
return 0;
}
[DataMember]
public ReactiveList<ItemViewModel> List { get; private set; }
private string _searchText;
[DataMember]
public string SearchText
{
get { return _searchText; }
set { this.RaiseAndSetIfChanged(ref _searchText, value); }
}
public ReactiveCommand<IEnumerable<Item>> LoadItems { get; protected set; }
public class ItemViewModel : ReactiveObject
{
public string Name { get; set; }
}
public string UrlPathSegment
{
get { return "Page"; }
}
public IScreen HostScreen { get; protected set; }
}
mio XAML:
<StackLayout VerticalOptions="FillAndExpand" Orientation="Vertical">
<Entry x:Name="_searchEntry" Placeholder="Search"></Entry>
<ListView x:Name="_myListView" RowHeight="80">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Vertical" >
<Label Text="{Binding Name}"></Label>
<Label Text="{Binding Address}"></Label>
<Label Text="{Binding PhoneNumber}"></Label>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button x:Name="_loadMoreButton" Text="Load More" TextColor="White" BackgroundColor="#77D065"></Button> </StackLayout>
</XamForms:ReactiveContentPage>
mio punto di vista code-behind:
using System;
using System.Reactive.Concurrency;
using System.Threading.Tasks;
using ReactiveUI;
using ReactiveUI.XamForms;
namespace views
{
public partial class MyView : ReactiveContentPage<MyViewModel>
{
private IDisposable _disconnectHandler;
public NearbyPlacesView()
{
InitializeComponent();
this.Bind(this.ViewModel, model => model.SearchText, view => view._searchEntry.Text);
this.OneWayBind(this.ViewModel, model => model.List, view => view._myListView.ItemsSource);
this.BindCommand(this.ViewModel, model => model.LoadItemsCommand, view => view._loadMoreButton);
}
protected override void OnAppearing()
{
base.OnAppearing();
//Is this the proper way to do this? seems
_disconnectHandler = UserError.RegisterHandler(async error =>
{
RxApp.MainThreadScheduler.ScheduleAsync(async (scheduler, token) =>
{
await DisplayAlert("Error", error.ErrorMessage, "OK");
});
return RecoveryOptionResult.CancelOperation;
});
//Load the items when the view appears. This doesn't feel right though.
ViewModel.LoadItemsCommand.Execute(null);
}
protected override void OnDisappearing()
{
base.OnDisappearing();
_disconnectHandler.Dispose();
_disconnectHandler = null;
}
}
}
Ho visualizzato la voce iniziale del test. Il motivo era perché ResetPage() stava cancellando quella voce di test iniziale. – kmc059000
Ho capito molto di questo. Il posizionamento delle chiamate nel costruttore ViewModel è importante! Ho spostato il 'this.WhenAnyValue (...)' per il SearchText prima dell'impostazione di LoadItemsCommand. 'ResetPage()' ora chiama 'LoadItemsCommand.Execute (null);' E ho estratto 'LoadItemsCommand.Execute (null);' da 'OnAppearing()' nel codice di vista retrostante. Tuttavia sto ancora ricevendo il problema dove la prima volta che visualizzo la pagina, non appare nulla. – kmc059000