Can't get context menu working

JeremyHerbisonJeremyHerbison CAMember ✭✭
edited March 2015 in Xamarin.Forms

Hi,

I can't seem to get my context MenuItems to fire either the edit or the delete commands indicated below. The menu comes up just fine, but nothing happens. Hopefully someone can help me out.

Bindings to my viewmodel are otherwise working fine.

<ListView ItemsSource="{Binding Encounters}"> <ListView.ItemTemplate> <DataTemplate> <ViewCell> <ViewCell.ContextActions> <MenuItem Text="Edit" Command="{Binding EditEncounterCommand}" CommandParameter="{Binding .}" /> <MenuItem Text="Delete" Command="{Binding DeleteEncounterCommand}" CommandParameter="{Binding .}" IsDestructive="True" /> </ViewCell.ContextActions> <ViewCell.View> <StackLayout Orientation="Horizontal"> <Label Text="{Binding Patient.GivenNames}" /> <Label Text="{Binding Patient.LastName}" /> </StackLayout> </ViewCell.View> </ViewCell> </DataTemplate> </ListView.ItemTemplate> </ListView>

Best Answer

Answers

  • JeremyHerbisonJeremyHerbison CAMember ✭✭
    edited March 2015

    Looks like the problem is that the Command is on the listview's viewmodel, not my Encounter object. And it sounds like there is no RelativeBinding syntax in Xamarin.Forms, so... not sure how to do this elegantly.

  • OSDMobileOSDMobile NLUniversity ✭✭

    I encounter the same issue. Did you find a solution?

  • TomSoderlingTomSoderling USUniversity ✭✭✭
    edited August 2015

    Nuts, this is the exact issue I'm seeing as well

    Using the command in the toolbar works just fine, but not when I use it in the ListView. I guess the binding context for each ListView item is specific
    <ContentPage.ToolbarItems> <ToolbarItem Text="Edit" Command="{Binding EditProject}" CommandParameter="{Binding SelectedProject}" /> </ContentPage.ToolbarItems>

  • AlexBekkerAlexBekker DEMember

    Thanks for sharing! Is this also possible in Code Behind?

  • Fantastic!

  • rarenivarrarenivar USMember ✭✭

    I ran into this issue recently and solved it a bit different, but it's basically the same idea...

    <ImageCell Text="{Binding Title}" TextColor="Black" ImageSource="{Binding IconSource}" Command="{Binding Source={StaticResource mainMenuViewModel}, Path=MenuItemPressed}" CommandParameter="{Binding Title}"> </ImageCell>

    And here's the post

  • batmacibatmaci DEMember ✭✭✭✭✭

    @TomSoderling said:
    Good news!
    Looks like there was a bug when x:Reference was used inside a DataTemplate. It's now been fixed in Xamarin.Forms v1.4.4
    https://bugzilla.xamarin.com/show_bug.cgi?id=30684

    This allows you to set the BindingContext of the ContextAction MenuItem to that of its parent ListView - meaning the command will now work.
    For the CommandParameter, you can set the Binding Source to be that of the TextCell (or ViewCell), so that will supply the particular object represented in the cell to your command

    Hopefully some code will make this more clear. My ListView's ItemSource is an ObservableCollection named "Projects"

    <ListView x:Name="projectListView" ItemsSource="{Binding Projects}" SelectedItem="{Binding SelectedProject, Mode=TwoWay}"> <ListView.ItemTemplate> <DataTemplate> <TextCell x:Name="textCell" Text="{Binding Name}" Detail="{Binding LastTestRun}"> <TextCell.ContextActions> <MenuItem Text="Edit" BindingContext="{Binding Source={x:Reference projectListView}, Path=BindingContext}" Command="{Binding EditProject}" CommandParameter="{Binding Source={x:Reference textCell}, Path=BindingContext}" /> </TextCell.ContextActions> </TextCell> </DataTemplate> </ListView.ItemTemplate> </ListView>

    This part tells it where to find my Command (in my ViewModel) by using the same BindingContext that my ListView is using.
    BindingContext="{Binding Source={x:Reference projectListView}, Path=BindingContext}"

    This part tells it what object to supply back to my Command, by setting the Source to be that of the TextCell
    CommandParameter="{Binding Source={x:Reference textCell}, Path=BindingContext}"

    How do you get the selecteditem on ViewModel in order to edit or delete it? you say

    This part tells it what object to supply back to my Command, by setting the Source to be that of the TextCell
    `
    CommandParameter="{Binding Source={x:Reference textCell}, Path=BindingContext}"

    But how do you access it? eventually you dont bind it to any property on viewModel

  • Pierre-ChristopheDusPierre-ChristopheDus FRUniversity ✭✭✭

    Hello,
    I meet a similar case for 2 Buttons in a ListView, allowing me to do "with" or "without" for the selected item.
    (in fact, I use Label + Label.GestureRecognizers instead of Button, but the result is the same thing).

    My Model is like this:
    public class Family { ... private string _title; public string Title { get { return this._title; } set { if (value != this._title) { this._title = value; RaisePropertyChanged(() => Title); } } ... }

    The ViewModel is like this:
    `public class DebugPageViewModel : ViewModelServices
    {
    private ObservableCollection _families;
    public ObservableCollection Families
    {
    get { return _families; }
    set
    {
    _families = value;
    RaisePropertyChanged(() => Families);
    }
    }

    public ICommand WithTappedCommand { get; set; }
    public ICommand WithoutTappedCommand { get; set; }
    
    public DebugPageViewModel()
    {
        LoadDatas();
        WithTappedCommand = new Command(() =>
        {
    
        });
        WithoutTappedCommand = new Command(() =>
        {
    
        });
    }
    ...`
    

    The XAML of the page is like this:
    <ListView Grid.Row="1" ItemsSource="{Binding Familles}"> <ListView.ItemTemplate> <DataTemplate> <ViewCell x:Name="ViewCell"> <ViewCell.View> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="1*" /> <ColumnDefinition Width="2*" /> <ColumnDefinition Width="1*" /> </Grid.ColumnDefinitions> <Label Grid.Column="0" Text="With" XAlign="Start" YAlign="Center"> <Label.GestureRecognizers> <TapGestureRecognizer Command="{Binding WithTappedCommand}"/> </Label.GestureRecognizers> </Label> <Label Grid.Column="1" Text="{Binding Title}" XAlign="Center" YAlign="Center" /> <Label Grid.Column="2" Text="Without" XAlign="End" YAlign="Center"> <Label.GestureRecognizers> <TapGestureRecognizer Command="{Binding WithOutTappedCommand}"/> </Label.GestureRecognizers> </Label> </Grid> </ViewCell.View> </ViewCell> </DataTemplate> </ListView.ItemTemplate> </ListView>

    => So the Command is not recognized, as the BindingContext is not mapped on the ViewModel, but on the Model...

    I tried other options, but I get an exception: "System.Exception: Can't resolve name on Element"

    <TapGestureRecognizer Command="{Binding WithTappedCommand, BindingContext={x:Reference Name=root}}" />

    <TapGestureRecognizer BindingContext ="{Binding Source={x:Reference DebugPageViewModel}, Path=BindingContext}" Command ="{Binding WithTappedCommand}" />

    <TapGestureRecognizer Command="{Binding Source={x:Reference DebugPageViewModel}, Path=BindingContext.WithTappedCommand}" />

    => Would you have an indea of what I'm doing wrong?

  • RobinSchroeder.8683RobinSchroeder.8683 USMember ✭✭

    @Pierre-ChristopheDus - Xamarin.Forms ListView with two buttons wired to two ViewModel Commands using an Image Gesture Recognizer per @TomSoderling 's pattern above. Works great.

    In the View:

    <ListView x:Name="PropertySearchResults" ItemsSource="{Binding Properties}">
         <ListView.ItemTemplate>
          <DataTemplate>
            <ViewCell x:Name="PropertyCell">
            {...}           
              <StackLayout>
                    <Image Source="map.png">
                      <Image.GestureRecognizers>
                        <TapGestureRecognizer 
                    BindingContext="{Binding Source={x:Reference PropertySearchResults}, Path=BindingContext}"
                                    Command="{Binding PropertyMapCommand}"
                                    CommandParameter="{Binding Source={x:Reference PropertyCell}, Path=BindingContext}" />
                      </Image.GestureRecognizers>
                    </Image>
                    <Image Source="tag.png">
                      <Image.GestureRecognizers>
                        <TapGestureRecognizer 
                    BindingContext="{Binding Source={x:Reference PropertySearchResults}, Path=BindingContext}"
                                    Command="{Binding PropertyTagCommand}"
                                    CommandParameter="{Binding Source={x:Reference PropertyCell}, Path=BindingContext}" />
                      </Image.GestureRecognizers>
                    </Image>
               </StackLayout>
          </ViewCell>
          </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
    

    In the ViewModel:

            private Command<Model.Property> _propertyMapCommand;
            private Command<Model.Property> _propertyTagCommand;
            public ObservableCollection<Model.Property> Properties
            {
                get { return _properties; }
                set
                {
                    _properties = value;
                    OnPropertyChanged();
                }
            }
         public Command PropertyMapCommand
                {
                    get
                    {
                        return _propertyMapCommand ?? (_propertyMapCommand = new Command<Model.Property>(p => ExecutePropertyMapCommand(p), CanExecutePropertyMapCommand));
                    }
                }
    
                public Command PropertyTagCommand
                {
                    get
                    {
                        return _propertyTagCommand ?? (_propertyTagCommand = new Command<Model.Property>(p =>                     ExecutePropertyTagCommand(p), CanExecutePropertyTagCommand));
                    }
                }
    
        private void ExecutePropertyMapCommand(Model.Property p)
            {
                //TODO: something here
                Debug.WriteLine($"PropertyMenuCommand: {p.Name}");
            }
    
            private void ExecutePropertyTagCommand(Model.Property p)
            {
                //TODO: something here
                Debug.WriteLine($"PropertyTagCommand: {p.Name}");
            }
    

    Happy Coding.

  • Abhijeet_SuryaAbhijeet_Surya USMember ✭✭✭

    @TomSoderling
    @adamkemp

    My business requirement is to bind text of the context menu from data model depend on some condition.
    But I want it use command from view model.

  • Abhijeet_SuryaAbhijeet_Surya USMember ✭✭✭
    edited September 2016

    @TomSoderling @adamkemp

    Done Thanks -- Command is in ViewModel & MenuItem Text is in Model


  • AlfeuLucasAlfeuLucas USMember

    Nice tips!

  • TripVoltageTripVoltage GBMember

    @TomSoderling said:
    Good news!
    Looks like there was a bug when x:Reference was used inside a DataTemplate. It's now been fixed in Xamarin.Forms v1.4.4
    https://bugzilla.xamarin.com/show_bug.cgi?id=30684

    This allows you to set the BindingContext of the ContextAction MenuItem to that of its parent ListView - meaning the command will now work.
    For the CommandParameter, you can set the Binding Source to be that of the TextCell (or ViewCell), so that will supply the particular object represented in the cell to your command

    Hopefully some code will make this more clear. My ListView's ItemSource is an ObservableCollection named "Projects"

    <ListView x:Name="projectListView" ItemsSource="{Binding Projects}" SelectedItem="{Binding SelectedProject, Mode=TwoWay}"> <ListView.ItemTemplate> <DataTemplate> <TextCell x:Name="textCell" Text="{Binding Name}" Detail="{Binding LastTestRun}"> <TextCell.ContextActions> <MenuItem Text="Edit" BindingContext="{Binding Source={x:Reference projectListView}, Path=BindingContext}" Command="{Binding EditProject}" CommandParameter="{Binding Source={x:Reference textCell}, Path=BindingContext}" /> </TextCell.ContextActions> </TextCell> </DataTemplate> </ListView.ItemTemplate> </ListView>

    This part tells it where to find my Command (in my ViewModel) by using the same BindingContext that my ListView is using.
    BindingContext="{Binding Source={x:Reference projectListView}, Path=BindingContext}"

    This part tells it what object to supply back to my Command, by setting the Source to be that of the TextCell
    CommandParameter="{Binding Source={x:Reference textCell}, Path=BindingContext}"

    @TomSoderling Excellent this worked for me.

    RE: https://forums.xamarin.com/discussion/81486/cant-get-context-of-menuitem-in-listview-viewcell#latest

Sign In or Register to comment.