ListView PullToRefresh doesn't have RefreshCommandParameter - how I got around it.

ChaseFlorellChaseFlorell CAInsider, University mod
edited April 2015 in Xamarin.Forms

So I implemented Pull-To-Refresh in my App today, but in the process, I've discovered that there isn't a RefreshCommandParameter that I could bind to. The reason I was looking for said property is because I wanted to bind ListView.IsRefreshing to my ViewModel.IsBusy property.

Since I couldn't pass the view model in as I wanted to (<ListView .... RefreshCommandParameter="{Binding}" />), I had to come up with another way... by exposing statics on my ViewModel (yuck).

What I did was setup my ViewModel like this

public class MyViewModel {
    private static MyViewModel _context;
    private readonly RefreshProjectListCommand _refreshProjectListCommand;

    public MyViewModel(RefreshProjectListCommand refreshProjectListCommand) {
        _context = this;
        _refreshProjectListCommand = refreshProjectListCommand;
    } 

    public string IsBusyPropertyName= @"IsBusy";
    private bool _isBusy;
    public bool IsBusy {
        get {return _isBusy}
        set { SetField(ref _isBusy, value, IsBusyPropertyName);}
    }

    public string ProjectsPropertyName = @"Projects";
    provate ProjectModel _projects;
    public ObservableCollection<ProjectModel> Projects
    {
        get { return _projects; }
        set { SetField(ref _projects, value, ProjectsPropertyName); }
    }

    public Command RefreshProjectListCommand
    {
        get
        {
            return new Command(
                parameter => _refreshProjectListCommand.Execute(parameter),
                parameter => _refreshProjectListCommand.CanExecute(parameter)
                );
        }
    }

    public static void SetIsBusy(bool isBusy) {
        // this exists because the ListView doesn't expose a RefreshCommandParameter
        _context.IsBusy = isBusy;
    }

    public static void SetProjects(ObservableCollection<ProjectModel> projects) {
        // this exists because the ListView doesn't expose a RefreshCommandParameter
        _context.Projects = projects;
    }
}

Then my command looks like this.

    public override bool CanExecute(object parameter)
    {
        return true;
    }

    public override async void Execute(object parameter)
    {
        MenuPageViewModel.SetIsBusy(true);

        await _syncEngine.ProjectSyncAsync();

        var p = await _projectService.FindByUserIdAsync(App.CurrentUser.Id);
        var projectsToReturn = _menuPageViewModelMapper.BuildListFrom(p).ToObservableCollection();

        MyPage.SetProjects(projectsToReturn);
        MyPage.SetIsBusy(false);
    }

Now I'm able to update the ObservableCollection<ProjectModel> AND update the IsRefreshing property even though I'm missing the RefreshCommandParameter.

@TheRealJasonSmith, any chance we can see that property in the future?

Posts

  • AlisonFernandesAlisonFernandes USMember ✭✭
    edited April 2015

    Sorry, misunderstood your question. Can You just Bind the command and IsRefreshing to a bool IsBusy in the view model, which gets updated inside the method that the command runs?

  • ChaseFlorellChaseFlorell CAInsider, University mod
    edited April 2015

    No, I need the whole view model object... Remember, I'm refreshing the view.

  • AlisonFernandesAlisonFernandes USMember ✭✭
    edited April 2015

    My point was, you would benefit if your command was being created inside your ViewModel, in order for it to have access to the ViewModel properties (or in the VM contructor create a command using a method inside your VM).

    http://developer.xamarin.com/guides/cross-platform/xamarin-forms/xaml-for-xamarin-forms/data_bindings_to_MVVM/

    First example using Forms Command, search for the AddCharCommand initialization inside the KeypadViewModel. This way, you would be able to change the state of your IsBusy property, thus, voiding the need to have a RefreshParameterProperty as you suggested.

    Another example, now using MVVMLight RelayCommand. Check the way IsLoading status is being changed inside the anon function, it's exactly the same principle.

  • adamkempadamkemp USInsider, Developer Group Leader mod

    It definitely does make sense to have any command be in your view model (that's kind of the whole point of using commands, really), but it also makes sense for any command property to have a corresponding command parameter property. It surprises me that there isn't one here.

  • ChaseFlorellChaseFlorell CAInsider, University mod

    You're right, and it works, but I feel as though it violates the single responsibility principle. Commands should be able to be objects unto themselves,

  • AlisonFernandesAlisonFernandes USMember ✭✭

    IMO that would only apply if you could share the command. Since in 99% of the cases you just need to use services that already abstract functionality and update the ViewModel specific properties (as you're doing in your code example for instance), I deem it as an unnecessary abstraction, but I get where you're coming from.

    Furthermore, you can also take into account that the commands are just used for the View to tell the VM it needs to do stuff, thus, they are View->ViewModel specific by inference.

  • ChaseFlorellChaseFlorell CAInsider, University mod

    I guess all I'm saying is that if every other command has a corresponding command parameter, why not this one? I personally like abstracting everything... it just keeps things clean.

    Also (as personal preference), I don't like putting anything logic related in a ViewModel. I treat my ViewModels as POCO's only. I know there's an argument for DDD style, but I'm not there myself.

  • EricMaupinEricMaupin USXamarin Team Xamurai
    edited April 2015

    I guess all I'm saying is that if every other command has a corresponding command parameter, why not this one?

    Essentially the use of it just wasn't seen. Since the refresh is at a high level (ListView rather than a specific item), what could you need to pass to the view model you're binding to doesn't already have? In your example, it looks like you're trying to do a bunch of extra work to what amounts to passing this here:

    parameter => _refreshProjectListCommand.Execute(parameter)
    

    That said, the example is a decent use case and it is easy to add, so we will revisit.

  • ChaseFlorellChaseFlorell CAInsider, University mod
    edited April 2015

    Thanks Eric, yeah there's a few things that need to happen in that command. Namely, we re-sync projects with the server and immediately show them to the user in the ListView.

    Since the refresh is at a high level (ListView rather than a specific item), what could you need to pass to the view model you're binding to doesn't already have?

    Isn't that flawed logic (absolutely no offense intended)? I mean, we're refreshing/updating the listview with something that it doesn't have... that's why we're using pull-to-refresh... no?

  • ChaseFlorellChaseFlorell CAInsider, University mod

    Also, you're right about this, not sure why I didn't see it.

  • abdullahtahan.7433abdullahtahan.7433 SAMember ✭✭

    i'm having the same issue right now , i initialize my list OnAppearing but i want to pass the userid to on refresh IsPullToRefresh so currently i don't know how to pass the useris in my xaml
    protected override void OnAppearing()
    {
    listView.RefreshCommand.Execute(userid);
    }
    in my viewmodel :

    public ICommand RefreshCommand2
    {get { return new Command(async (parameter) => await ExecuteRefreshCommand2(parameter), (parameter) => CanExecuteRefreshCommand2()); }}

  • PRAGYATIWARIPRAGYATIWARI USMember

    How to use pull to refresh option in Grid being made in xamerin?

  • ChaseFlorellChaseFlorell CAInsider, University mod
    The grid isn't a data view, there is no pull to refresh.
Sign In or Register to comment.