Как правильно связать мою команду ContextMenu с RelayCommand?

Итак, у меня есть несколько кнопок, настроенных на RelayCommand, и они работают отлично, но при попытке привязать свойство Command к ContextMenu Menu Item он просто не реагирует на это. Я что-то читал о том, что нужно установить уровень AncestorType или что-то в этом роде, но это было очень обширное описание, не объясняющее, почему и как.

Итак, у меня есть свой ListView

<ListView x:Name="PlayerListView"
                  Width="200"
                  Height="330"
                  VerticalAlignment="Top"
                  Margin="0,80,15,0"
                  HorizontalAlignment="Right"
                  Background="#252525"
                  VerticalContentAlignment="Center"
                  ItemsSource="{Binding ServerViewModel.Players}">

            <ListView.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal" 
                                VerticalAlignment="Stretch" 
                                HorizontalAlignment="Stretch"
                                Width="190"
                                Background="#222222">

                        <StackPanel.ContextMenu>
                            <ContextMenu>
                                <MenuItem Header="Command One">
                                    <MenuItem.Icon>
                                        <Image Source="../../Assets/image.png"
                                               RenderOptions.BitmapScalingMode="Fant"/>
                                    </MenuItem.Icon>
                                </MenuItem>

                                <MenuItem Header="Command Two"
                                          Command="{Binding ServerViewModel.MyCommand,
                                    RelativeSource={RelativeSource AncestorType=ListViewItem}}">
                                    <MenuItem.Icon>
                                        <Image Source="../../Assets/image.png"
                                               RenderOptions.BitmapScalingMode="Fant"/>
                                    </MenuItem.Icon>
                                </MenuItem>
                            </ContextMenu>
                        </StackPanel.ContextMenu>

                        <Image Source="../../Assets/image.png"
                               Width="20"
                               Height="20"/>

                        <TextBlock Text="{Binding Username}" 
                                   Foreground="White"
                                   HorizontalAlignment="Stretch"
                                   VerticalAlignment="Center" 
                                   Margin="5"/>
                    </StackPanel>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>

И, как вы можете видеть во втором элементе, я пытаюсь связать его с моей RelayCommand. Это должно работать, так как модели просмотра и контекст данных правильно настроены для моих кнопок.

public class BaseViewModel : ObservableObject
    {
        public ServerViewModel ServerViewModel { get; set; } = new ServerViewModel();
    }

ViewModel

public RelayCommand MyCommand { get; set; }

public ServerViewModel()
        {
            MyCommand = new RelayCommand(DoSomething);
        }
public void DoSomething(object parameter)
        {
            MessageBox.Show("Working!");
        }

И, конечно же, сама RelayCommand. Опять же, RelayCommands работают для кнопок, но не для элементов ContextMenu.

public class RelayCommand : ObservableObject, ICommand
    {
        private readonly Action<object> _execute;
        private readonly Predicate<object> _canExecute;

        public RelayCommand(Action<object> execute, Predicate<object> canExecute)
        {
            if (execute == null)
                throw new ArgumentException("execute");

            _execute = execute;
            _canExecute = canExecute;
        }

        public RelayCommand(Action<object> execute) : this(execute, null)
        {

        }

        public bool CanExecute(object parameter)
        {
            return _canExecute == null ? true : _canExecute(parameter);
        }

        public void Execute(object parameter)
        {
            _execute.Invoke(parameter);
        }

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
    }

И здесь я устанавливаю DataContext

public MainWindow()
        {
            InitializeComponent();
            DataContext = new BaseViewModel();
        }

person Mark Denom    schedule 11.11.2018    source источник
comment
Насколько я знаю, ContextMenu не являются частью визуального дерева, и поэтому они не наследуют DataContext родительского элемента управления. Ваш подход с RelativeSource должен помочь. Но ваш текущий код определяет источник привязки как ListViewItem, но у ListViewItem нет свойства ServerViewModel для привязки, или они есть? Вместо этого попробуйте выполнить привязку к DataContext.ServerViewModel.MyCommand.   -  person Nostromo    schedule 11.11.2018
comment
вроде ничего не менял   -  person Mark Denom    schedule 11.11.2018


Ответы (1)


Этот код

Command="{Binding ServerViewModel.MyCommand, RelativeSource={RelativeSource AncestorType=ListViewItem}}"

пытается найти свойство ServerViewModel в классе ListViewItem и не находит его. Если объекты, возвращаемые ServerViewModel.Players, сами являются объектами ViewModel, вы можете удалить часть RelativeSource и добавить свою команду в Player ViewModel.

Пример:

Command="{Binding MyCommand}"

и в PlayerViewModel:

    public RelayCommand MyCommand { get; set; }

    public PlayerViewModel()
    {
        MyCommand = new RelayCommand(DoSomething);
    }
    public void DoSomething(object parameter)
    {
        MessageBox.Show("Player Working!");
    }

Альтернативные решения см. здесь: WPF: привязка ContextMenu к команде MVVM

person Klaus Gütter    schedule 11.11.2018
comment
вроде ничего не менял - person Mark Denom; 11.11.2018