0
votes

In my WPF application using Prism in .NET 4.6, I have a User control with a ListBox on it. I want to attach a Context menu to the Items in the list box and when clicked, want to execute a command in the View Model.

The Context Menu is showing up alright. But nothing seems to happen when I click on the menu.

Here is my Xaml (removed all the code that are irrelevant).

    <UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
                 xmlns:prism="http://prismlibrary.com/" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
                 xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions" 
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                 xmlns:syncfusion="http://schemas.syncfusion.com/wpf" 
                 x:Class="DynaProPOS.WPF.Views.UserAuthorization" 
                 prism:ViewModelLocator.AutoWireViewModel="True" 
                 mc:Ignorable="d" d:DesignHeight="600" d:DesignWidth="1000">
    <Grid>
        <ListBox Margin="15,5" x:Name="lbxGroups" Grid.Row="2" Grid.Column="2" Grid.RowSpan="1"
                    ItemsSource="{ Binding Groups }" SelectionMode="Single" SelectedValuePath="IdKey" 
                    DisplayMemberPath="GroupName" SelectedItem="{Binding SelectedGroup, Mode=TwoWay}">
            <ListBox.ItemContainerStyle>
                <Style TargetType="ListBoxItem">
                    <Setter Property="ContextMenu">
                        <Setter.Value>
        <ContextMenu>
            <MenuItem Header="Move to User Group" Command="{Binding AddGroupToUserGroupsCommand}"/>
        </ContextMenu>

                        </Setter.Value>
                    </Setter>
                </Style>
            </ListBox.ItemContainerStyle>
        </ListBox>
    </Grid>
</UserControl>

I also tried one of the following (found these samples on StackOverflow)

                        <ContextMenu DataContext="{Binding PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
                            <MenuItem Header="Move to User Group" Command="{Binding AddGroupToUserGroupsCommand}"
                                         CommandParameter="{Binding}"/>
                        </ContextMenu>

and this:

<ContextMenu>
<MenuItem Header="Move to User Group" Command="{Binding AddGroupToUserGroupsCommand}" 
                                        CommandParameter="{Binding}"/>

and here is my View Model (again, all irrelevant code removed).

public class UserAuthorizationViewModel : BindableBase
{
    private IAuthenticationService authService;
    private User selectedUser;
    private Users2Groups selectedUserGroup;
    private Group selectedGroup;
    private ObservableCollection<User> users;
    private ObservableCollection<Users2Groups> users2Groups;
    private ObservableCollection<Group> groups;
    private ObservableCollection<Group> selectedGroups;
    private ObservableCollection<Users2Groups> selectedUserGroups;
    private ObservableCollection<Groups2Permissions> groupPermissions;
    private ObservableCollection<Permission> allPermissions;
    private IRegionManager regionMgr;
    private ObservableCollection<Permission> selectedPermissions;

    public DelegateCommand AddGroupToUserGroupsCommand { get; private set; }

    public UserAuthorizationViewModel( IAuthenticationService _authService, IRegionManager _regionMgr )
    {
        authService = _authService;
        regionMgr = _regionMgr;
        AddGroupToUserGroupsCommand = new DelegateCommand( async () => await AddGroupToUserGroups() );
    }

    private async Task AddGroupToUserGroups()
    {
        if ( SelectedUser == null )
            return;

        foreach ( var sg in SelectedGroups )
        {
            if ( !UserGroups.Any( x => x.Group.IdKey == sg.IdKey ) )
            {
                var newUg = new Users2Groups();
                newUg.User = SelectedUser;
                newUg.Group = sg;
                newUg.ObjectStateEnum = ObjectStateEnum.Added;
                await Task.Run( () => UserGroups.Add( newUg ) );
            }
        }

    }
}

Break point in the command handler never gets hit. Can somebody help me please?

EDIT OK. I found a way to make the context menu fire the attached command. But the problem is, now, the context menu appears anywhere by right clicking anywhere.

<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
             xmlns:prism="http://prismlibrary.com/" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
             xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:syncfusion="http://schemas.syncfusion.com/wpf" 
             x:Class="DynaProPOS.WPF.Views.UserAuthorization" 
             prism:ViewModelLocator.AutoWireViewModel="True" 
             mc:Ignorable="d" d:DesignHeight="600" d:DesignWidth="1000">
<Grid>
    <ListBox x:Name="lbxPermissions" HorizontalAlignment="Stretch" Grid.Row="2" 
                    Grid.Column="3" Grid.RowSpan="3" Margin="15,10" VerticalAlignment="Stretch" 
                    ItemsSource="{Binding AllPermissions}" SelectedValuePath="IdKey" 
                    ScrollViewer.VerticalScrollBarVisibility="Auto">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding PermissionName}"/>
            </DataTemplate>
        </ListBox.ItemTemplate>
        <ListBox.ContextMenu>
            <ContextMenu>
                <MenuItem Header="Add to Group" Command="{Binding AddPermissionToGroupsCommand}"/>
            </ContextMenu>
        </ListBox.ContextMenu>
    </ListBox>
</Grid>

What I need is to confine the context menu to appear only when I right click on a ListBox item. So, I found this sample code on SO which I used. This does make the context menu appear only on ListBox item. But the problem is, now, my command is not firing when I click the context menu item.

        <ListBox x:Name="lbxPermissions" HorizontalAlignment="Stretch" Grid.Row="2" 
                    Grid.Column="3" Grid.RowSpan="3" Margin="15,10" VerticalAlignment="Stretch" 
                    ItemsSource="{Binding AllPermissions}" SelectedValuePath="IdKey" 
                    ScrollViewer.VerticalScrollBarVisibility="Auto">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding PermissionName}"/>
            </DataTemplate>
        </ListBox.ItemTemplate>
        <ListBox.ItemContainerStyle>
            <Style TargetType="ListBoxItem">
                <Setter Property="ContextMenu">
                    <Setter.Value>
                        <ContextMenu>
                            <MenuItem Header="Add to Group" Command="{Binding AddPermissionToGroupsCommand}"/>
                        </ContextMenu>
                    </Setter.Value>
                </Setter>
            </Style>
        </ListBox.ItemContainerStyle>
    </ListBox>

Any help? Anyone? Please?

2

2 Answers

1
votes

Bind the Tag property of the ListBoxItem to the view model and bind to the command using the PlacementTarget property of the ContextMenu:

<ListBox Margin="15,5" x:Name="lbxGroups" Grid.Row="2" Grid.Column="2" Grid.RowSpan="1"
        ItemsSource="{Binding Groups}" SelectionMode="Single" SelectedValuePath="IdKey" 
        DisplayMemberPath="GroupName" SelectedItem="{Binding SelectedGroup, Mode=TwoWay}">
    <ListBox.ItemContainerStyle>
        <Style TargetType="ListBoxItem">
            <Setter Property="Tag" Value="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType=ListBox}}" />
            <Setter Property="ContextMenu">
                <Setter.Value>
                    <ContextMenu>
                        <MenuItem Header="Move to User Group"
                                  Command="{Binding PlacementTarget.Tag.AddGroupToUserGroupsCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"/>
                    </ContextMenu>
                </Setter.Value>
            </Setter>
        </Style>
    </ListBox.ItemContainerStyle>
</ListBox>

This should work assuming that the AddGroupToUserGroupsCommand property belongs to the same view model class as the Groups property.

0
votes

ListBox Tag property needs Binding. Since you are trying to do the PlacementTarget.Tag for command Binding.

 <ListBox Margin="15,5" x:Name="lbxGroups" Grid.Row="2" 
Tag={Binding} Grid.Column="2" Grid.RowSpan="1"
                ItemsSource="{ Binding Groups }" SelectionMode="Single" SelectedValuePath="IdKey" 
                DisplayMemberPath="GroupName" SelectedItem="{Binding SelectedGroup, Mode=TwoWay}">
        <ListBox.ItemContainerStyle>
            <Style TargetType="ListBoxItem">
                <Setter Property="ContextMenu">
                    <Setter.Value>
                        <ContextMenu DataContext="{Binding PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
                            <MenuItem Header="Move to User Group" Command="{Binding AddGroupToUserGroupsCommand}"
                                         CommandParameter="{Binding}"/>
                        </ContextMenu>
                    </Setter.Value>
                </Setter>
            </Style>
        </ListBox.ItemContainerStyle>
    </ListBox>