简体   繁体   中英

How to change the Action View for an ActionBar's menu item with Xamarin

I have a menu item with an icon in an ActionBar which I would like to change as a ProgressBar when the user click on it, run a task, and then change it back to an icon. Below is the code I have, but nothing happens when the menu item is clicked.

The activity: ImportReferenceView.cs

public class ImportReferenceView : MvxActivity
{
    protected override void OnCreate(Bundle bundle)
    {
        base.OnCreate(bundle);
        SetContentView(Resource.Layout.ImportReferenceView);
        ActionBar.SetDisplayShowCustomEnabled(true);
    }

    public override bool OnCreateOptionsMenu(Android.Views.IMenu menu)
    {
        MenuInflater.Inflate(Resource.Menu.import_actions, menu);
        return true;
    }

    public override bool OnOptionsItemSelected(Android.Views.IMenuItem item)
    {
        switch (item.ItemId)
        {
            case Resource.Id.action_import:
                item.SetActionView(Resource.Layout.progressbar);
                item.ExpandActionView();
                var vm = ((ImportReferenceViewModel)ViewModel);
                Task task = Task.Run(() =>    vm.ImportCommand.Execute(vm.SelectedTableReferences));
                Task.WaitAll(new Task[] { task });
                item.CollapseActionView();
                item.SetActionView(null);
                break;
            default:
                break;
        }
        return true;
    }
}

The menu actions: import_actions.xml

<?xml version="1.0" encoding="utf-8" ?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >

  <item
      android:id="@+id/action_import"
      android:showAsAction="always"
      android:icon="@drawable/action_down"
      android:title="Refresh"/>
</menu>

The progressbar view: progressbar.axml

<?xml version="1.0" encoding="utf-8"?>
<ProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/progressBar2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

I am trying to achieve with Xamarin something like the one described in the Action Bar/Action View section of this article

Any help or suggestion will be really appreciated

Here's how I do it:

public override bool OnCreateOptionsMenu(IMenu menu)
{
    MenuInflater.Inflate(Resource.Menu.home_menu, menu);

    _refreshWrapper = new RefreshActionButtonWrapper(menu);
    var set = this.CreateBindingSet<HomeView, HomeViewModel>();
    set.Bind(_refreshWrapper).For("IsBusy").To(vm => vm.MyService.IsBusy);
    set.Apply();

    return true;
}

I use MvvmCross Data Binding to bind an IsBusy property to a wrapper class. You don't have to do that. You can set the IsBusy property directly (see example below).

public class RefreshActionButtonWrapper
{
    private readonly IMenu _optionsMenu;

    public RefreshActionButtonWrapper(IMenu optionsMenu)
    {
        _optionsMenu = optionsMenu;
    }

    private bool _isBusy;
    public bool IsBusy
    {
        get { return _isBusy; }
        set
        {
            _isBusy = value;

            var dispatcher = MvxMainThreadDispatcher.Instance;
            dispatcher.RequestMainThreadAction(() => SetRefreshActionButtonState(_isBusy));
        }
    }

    public void SetRefreshActionButtonState(bool refreshing)
    {
        if (_optionsMenu == null) return;
        var refreshItem = _optionsMenu.FindItem(Resource.Id.refresh_action);
        if (refreshing)
        {
            refreshItem.SetActionView(Resource.Layout.actionbar_indeterminate_progress);
        }
        else
        {
            refreshItem.SetActionView(null);
        }
    }
}

The trick is to change the action view on the Main thread. MvvmCross provides a method to invoke a request on the main thread:

var dispatcher = MvxMainThreadDispatcher.Instance;
dispatcher.RequestMainThreadAction(() => SetRefreshActionButtonState(_isBusy));

Then you can do something like this. Use await to wait for your import function to complete. I like to wrap IsBusy status in try/finally, to make sure that the IsBusy is always set to false, even on exceptions.

public override bool OnOptionsItemSelected(Android.Views.IMenuItem item)
{
    switch (item.ItemId)
    {
        case Resource.Id.action_import:
            try
            {
                _refreshWrapper.IsBusy = true;
                var vm = ((ImportReferenceViewModel)ViewModel);
                await Task.Run(() => vm.ImportCommand.Execute(vm.SelectedTableReferences));             
            }
            finally 
            {
                _refreshWrapper.IsBusy = false;
            }
            break;
        default:
            break;
    }
    return true;
}

BTW: If you derive your view from MvxActivity<ImportReferenceViewModel> you will have a strongly typed ViewModel property and won't have to cast it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM