这是确保在视图模型中加载数据的正确方法吗?

时间:2018-05-22 09:25:28

标签: listview mvvm xamarin.forms initialization prism

我正在使用Prism MVVM框架构建一个Xamarin.Forms应用程序。我有一个案例,我的视图模型可以接收将在视图模型中用于更新列表视图或其他UI元素的数据的完整对象,或者它将只接收我可以使用它们并检索完整的那些对象的ID来自Web服务的对象,但是,这是异步操作,并且在加载视图并且列表视图准备好接收数据时,可能尚未设置这些对象。我以这样的方式编写了一个viewmodel,我认为这将确保listview不会尝试刷新,直到设置了这些对象。我想过使用async void(fire和forget)方法来检索对象,但这意味着如果加载listview时,web请求可能仍在后台等待,然后刷新方法将尝试检索同样的对象再次导致重复请求。

//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" );
        }
    }
}

我做错了吗?有更简单的方法吗?

0 个答案:

没有答案