Я создаю приложение Xamarin.Forms с использованием инфраструктуры Prism MVVM.У меня есть случай, когда мои view-модели могут получать полные объекты, которые будут использоваться в модели представления для обновления данных для представления списка или других элементов пользовательского интерфейса, или он получит только идентификаторы тех объектов, где я могу их использовать, и получить полныйобъекты из веб-службы, но это асинхронная операция, и к тому времени, когда представление загружено и представление списка готово для получения данных, эти объекты могут быть еще не установлены.Я написал модель представления таким образом, что, я думаю, это гарантирует, что просмотр списка не будет обновляться до тех пор, пока эти объекты не будут установлены.Я думал об использовании асинхронного метода void (запускать и забывать) для извлечения объектов, но это означало бы, что если к моменту загрузки списка просмотра веб-запрос все еще будет находиться в фоновом режиме, а затем метод обновления попытается получитьте же объекты снова вызывают повторяющиеся запросы.
//ViewModelBase implements INaviagtionAware and INotifyPropertyChanged
public class MainPageViewModel : ViewModelBase
{
private string _object1Id;
private string _object2Id;
private Task _getObjectsFromIdsTask;
private Object1 _object1;
private Object2 _object2;
private DelegateCommand _refreshListViewSourceCommand;
private bool _isIsRefreshing;
public Object1 Object1
{
get => _object1;
set => SetProperty(ref _object1, value);
}
public Object2 Object2
{
get => _object2;
set => SetProperty(ref _object2, value);
}
public DelegateCommand RefreshListViewSourceCommand => _refreshListViewSourceCommand ?? (_refreshListViewSourceCommand = new DelegateCommand(RefreshListViewSource, CanExecuteRefreshListViewSource).ObservesProperty(() => IsRefreshing));
public bool IsRefreshing
{
get => _isIsRefreshing;
set => SetProperty(ref _isIsRefreshing, value);
}
public MainPageViewModel ( INavigationService navigationService )
: base ( navigationService )
{
Title = "Main Page";
}
//Called when the page is first loadded and on each pull-to-refresh.
private async void RefreshListViewSource ()
{
IsRefreshing = true;
try
{
//If this is truen then objects where set from either another viewmodel or the task has completed succesfully before this method was called from the view.
if ( Object1 != null &&
Object2 != null )
{
//Populate the listview.
IsRefreshing = false;
return;
}
else if ( _getObjectsFromIdsTask != null )
{
switch ( _getObjectsFromIdsTask.Status )
{
case TaskStatus.Created :
case TaskStatus.WaitingForActivation :
case TaskStatus.WaitingToRun :
case TaskStatus.Running :
case TaskStatus.WaitingForChildrenToComplete :
//If any of the above then we wait for its completion.
await _getObjectsFromIdsTask;
break;
case TaskStatus.Faulted :
case TaskStatus.Canceled :
//If an error occurs then we restart the task.
_getObjectsFromIdsTask = GetObjectsFromIds ();
await _getObjectsFromIdsTask;
break;
case TaskStatus.RanToCompletion :
//If completed then do nothing.
break;
default :
throw new ArgumentOutOfRangeException ();
}
}
else
{
//If it is null then this method was called from the view before the task is set.
//This branch should never be reached, should I delete it.
_getObjectsFromIdsTask = GetObjectsFromIds ();
await _getObjectsFromIdsTask;
}
//If we get to here then the objects must be set
//Populate the listview.
}
catch ( Exception e )
{
Debug.WriteLine ( e );
}
IsRefreshing = false;
}
private bool CanExecuteRefreshListViewSource () => !IsRefreshing;
//Gets the objects from a web service by thier ids.
private async Task GetObjectsFromIds ()
{
//Uses _objectId1 and _objectId2 to get the objects. This just simulates delay.
Task < Object1 > object1 = Task.Delay ( 100 ).ContinueWith ( task => new Object1 () );
Task < Object2 > object2 = Task.Delay ( 100 ).ContinueWith ( task => new Object2 () );
Object1 = await object1;
Object2 = await object2;
}
//called when navigation to the page. I used OnNavigatingTo and not OnNavigatedTo to reduce the chance that the refresh method would be called befor the objects are set.
public override void OnNavigatingTo ( NavigationParameters parameters )
{
base.OnNavigatingTo ( parameters );
//If Ids are sent from another viewmodel then I have to get their objects from a web service. This would happen in case of deep linking from notification or a similar scenario.
if ( parameters.ContainsKey ( "Object1Id" ) &&
parameters.ContainsKey ( "Object2Id" ) )
{
_object1Id = parameters.GetValue < string > ( "Object1" );
_object2Id = parameters.GetValue < string > ( "Object2" );
_getObjectsFromIdsTask = GetObjectsFromIds ();
}
//If the full objects are sent, then I am setting the objects right here.
else if ( parameters.ContainsKey ( "Object1" ) &&
parameters.ContainsKey ( "Object2" ) )
{
Object1 = parameters.GetValue < Object1 > ( "Object1" );
Object2 = parameters.GetValue < Object2 > ( "Object2" );
}
}
}
Я что-то не так делаю?Есть ли более простой способ сделать это?