Context Actions - How can you get the object for the row where the context action was executed?

CarlBartonCarlBarton USInsider, University, Developer Group Leader ✭✭

I have been searching around and found a few examples of using Context Actions however none of the ones I found interact with the underlying ViewCell's bound object. Basically, when I swipe (using iOS) and click Delete, I want to delete the object from the collection that is bound to this particular cell in the list.

I know it must be something obvious that I am just missing. it doesn't appear to be available in the sender (MenuItem)

` <ViewCell.ContextActions>

</ViewCell.ContextActions>

   public void OnCall(object sender, EventArgs e)
    {
        var mi = ((MenuItem)sender);
        DisplayAlert("Call Context Action", mi.CommandParameter + " Call context action", "OK");
    }

`

Thanks in advance.

Carl

Best Answer

Answers

  • CarlBartonCarlBarton USInsider, University, Developer Group Leader ✭✭

    Sorry the code got mangled. You cannot see the MenuItem Clicked="OnCall" Text="Call" in the ContextActions

  • adamkempadamkemp USInsider, Developer Group Leader mod

    Use a Command with a binding instead of an event, just like I described here.

  • CarlBartonCarlBarton USInsider, University, Developer Group Leader ✭✭

    Adam,

    Thanks for the quick response.

    I think I understand that the Command object is provided to the MenuItem via the get so it can be called when the click occurs and the action that I supply will be called. I still do not see how I get access to the bound object in this manner. There are no parameters passed to the Action in your example.

    Sorry, I am still not understanding.

    Carl

  • adamkempadamkemp USInsider, Developer Group Leader mod

    The command is an object implementing the ICommand interface, which has a method called Execute. If you use a binding to assign a command created by the view model to your MenuItem then when the menu item is activated it will call the Execute method of that command object. The XAML would look like this:

                        <ViewCell.ContextActions>
                            <MenuItem Text="Click" Command="{Binding ClickCommand}" />
                        </ViewCell.ContextActions>
    

    The rest of the code would be the same as my example. You probably don't need a parameter because the code that runs when you click that button is already in the view model for the item that the menu item was in. The bound object would just be this in the command handler. Take a closer look at the code and see how it works.

  • CarlBartonCarlBarton USInsider, University, Developer Group Leader ✭✭

    Ah, I am not using MVVM in this case. I am using straight-up XAML and code-behind. After I sent my reply I did more poking around and it would seem that CommandParameter might be the simplest way to do it (although I have the command there as well.

    Either way I would assume that the CommandParameter would be passed in (MenuItem)sender for the event or as the parameter for the anonymous function for the Action. Your example linked to an MVVM example that showed passing a hard-coded string as the CommandParameter for a bunch of buttons which makes sense so I guess in order to get this to work I need to somehow reference the ViewCell that the ContextActions are tied to and plug it into the CommandParameter in the XAML?

    I have not figured out how to do this yet.

  • adamkempadamkemp USInsider, Developer Group Leader mod

    Ah, I am not using MVVM in this case. I am using straight-up XAML and code-behind.

    Right. That's the problem. ;)

    Either way I would assume that the CommandParameter would be passed in (MenuItem)sender for the event

    No. CommandParameter is used only for the Command. If you don't use the Command then that parameter is ignored. The sender in the event is usually some view (maybe the MenuItem).

    Your example linked to an MVVM example that showed passing a hard-coded string as the CommandParameter for a bunch of buttons

    My example didn't use a parameter so I'm not sure which code you're looking at. Typically a parameter would be used for a case where you might have one command handle multiple kinds of things, and the parameter might give additional context to the command handler. You don't need it to get the item because the item is the thing that owns the command handler, so again the item in the list that the button was associated with is just this in that context.

    It sounds like you're trying to avoid MVVM, and I strongly suggest you don't do that. The API, especially in cases like this, is designed to work with MVVM, and it is going to be very difficult to do certain things (like this) without using MVVM. It's not that hard if you take the time to learn it. You'll make your life harder by avoiding it than by learning it.

  • CarlBartonCarlBarton USInsider, University, Developer Group Leader ✭✭

    Adam,

    Thank you for your assistance and input. I did just want to clarify a couple of things and to assist anyone else who may have a similar question when trying to "enhance" the existing sample provided by Xamarin.

    No. CommandParameter is used only for the Command. If you don't use the Command then that parameter is ignored. The sender in the event is usually some view (maybe the MenuItem).

    It works fine without MVVM. CommandParameter is a property of MenuItem which is the sender of the event. Whatever is passed from the XAML is received in this property.

    My example didn't use a parameter so I'm not sure which code you're looking at.

    Your original response to the other question here forums.xamarin.com/discussion/comment/117517/#Comment_117517

    linked to the Xamarin docs here developer.xamarin.com/guides/cross-platform/xamarin-forms/xaml-for-xamarin-forms/data_bindings_to_MVVM/#Commanding_with_ViewModels

    which contained the example showing the passing of a string in the CommandParameter.

    For those who are interested, here is how you can get it to work using Events as in the Xamarin sample.

    XAML within the MenuItem tag

    MenuItem Clicked="OnCall" Text="Call" CommandParameter="{Binding .}"

    Code-Behind Event Handler

    public async void OnCall(object sender, EventArgs e)
    {
    var mi = ((MenuItem)sender);
    var contact = mi.CommandParameter as ContactHistoryItem;

            if (contact != null)
            {
                // Do whatever you want with the object
            }
        }
    
  • CarlBartonCarlBarton USInsider, University, Developer Group Leader ✭✭

    You basically did provide me with the answer, just not directly. :)

    I basically took something you said and combined it with the only usage of CommandParameter I found (also supplied by you )

    You stated that the Command would actually be owned by the underlying object making it accessible with this. I knew the CommandParameter was part of the MenuItem object and the MVVM example showed hard-coded data being passed. It was only a small leap to passing {Binding .} providing the reference back to the object.

  • VincentwxVincentwx CAMember ✭✭

    I googled the same question before. It would be better if "Working with ListView" section had made it clear how to do it.

  • batmacibatmaci DEMember ✭✭✭✭✭

    @adamkemp you are saying that > You don't need it to get the item because the item is the thing that owns the command handler, so again the item in the list that the button was associated with is just this in that context.

    but when I try with this i get my viewmodel itself. here is how my binding looks like

         public Command OnDeleteClick
            {
                get
                {
                    return new Command(async () =>
                    {
                      await DeleteData();
                    });
                }
            }
    
      <ViewCell.ContextActions>
                            <MenuItem Command="{Binding Path=BindingContext.OnDeleteClick, Source={x:Reference Name=myProgramListPage}}"     
           Text="Delete"
          IsDestructive="True" />
    

    Am I doing something wrong here?

  • adamkempadamkemp USInsider, Developer Group Leader mod

    This is nonsense:

    {Binding Path=BindingContext.OnDeleteClick, Source={x:Reference Name=myProgramListPage}
    

    You should learn the basics of bindings: https://developer.xamarin.com/guides/xamarin-forms/user-interface/xaml-basics/data_binding_basics/

  • batmacibatmaci DEMember ✭✭✭✭✭

    @adamkemp i had exactly same problem described here. and It resolved my problem. It works fine indeed. why do you think it is wrong? as described in the other thread, it makes perfectly sense

  • adamkempadamkemp USInsider, Developer Group Leader mod

    That other thread was for binding to a command in the page view model rather than a command in the item view model. If that's what you want then you need to supply the item to the command somehow. In your attempt you left off the key from the other thread to making to this work. What you left out is CommandParameter="{Binding .}".

    Personally I prefer leaving the command in the item view model and giving that view model a reference to whatever it needs to perform the action.

  • RobertEchten.2700RobertEchten.2700 MXMember ✭✭
    edited February 2016

    If this is still current, I managed to do something similar to what the original poster tried to do. I found that (at least in my case), the BindingContext of the selected item in the list related directly to the item I was looking for. My XAML looks as follows:

    <ViewCell.ContextActions>
        <MenuItem Clicked="HandleEditClick" Text="Edit" IsDestructive="False" />
    </ViewCell.ContextActions>
    

    In the code-behind, I have the following method:

            private void HandleEditClick(object sender, EventArgs e)
            {
                var visit = (sender as Xamarin.Forms.MenuItem).BindingContext as Visit;
    
                if (visit == null)
                    return;
    
                // Do stuff with the item here.
            }
    

    In my case, Visit is my model, the item that is listed in the ListView.

    Hope this helps.

  • EnricoRossiniEnricoRossini USMember ✭✭✭✭
    edited August 2016

    Hi guys,
    for this conversation I found an interesting article puresourcecode.com

    The implementation is working fine

  • nattarSanthanarajnattarSanthanaraj USMember ✭✭

    @RobertEchten.2700 thanks for your comment. I got the items from the listview.

  • RayGoudieRayGoudie CAMember ✭✭

    It would be great if, when doing a long press on an item that it would become the selected item in the list just as the context menu appeared.

    Has anyone resolved this anomaly?

  • Hi @CarlBarton , This is done simply by passing the binding context of the underlying item template as command parameter when the view cell's command is executed, you can easily do that using XAML.
    Here is a detailed blog post I made about implementing this. And also, responding to commands of the list's item templates from its base view model.

Sign In or Register to comment.