WPF: привязка ContextMenu к команде MVVM

Предположим, у меня есть Окно с возвращающим команду свойством (на самом деле это UserControl с Command в classе ViewModel, но давайте как можно проще воспроизвести проблему).

Следующие работы:

     

Но следующее не работает.

          

Сообщение об ошибке, которое я получаю, это

Ошибка System.Windows.Data: 4: Невозможно найти источник для привязки со ссылкой «ElementName = myWindow». BindingExpression: Path = МояКоманда; DataItem = NULL; целевым элементом является «MenuItem» (Name = ”); target является «Command» (тип «ICommand»)

Зачем? И как мне это исправить? Использование DataContext не является вариантом, так как эта проблема встречается вниз по визуальному дереву, где DataContext уже содержит фактические данные, которые отображаются. Я уже пытался использовать {RelativeSource FindAncestor, ...} вместо этого, но это дает аналогичное сообщение об ошибке.

Проблема заключается в том, что ContextMenu это не в визуальном дереве, поэтому вам в основном нужно сообщить контекстному меню о том, какой контекст данных использовать.

Посмотрите этот blogpost с очень приятным решением Томаса Левеска.

Он создает class Proxy, который наследует Freezable и объявляет свойство зависимостей данных.

 public class BindingProxy : Freezable { protected override Freezable CreateInstanceCore() { return new BindingProxy(); } public object Data { get { return (object)GetValue(DataProperty); } set { SetValue(DataProperty, value); } } public static readonly DependencyProperty DataProperty = DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null)); } 

Затем он может быть объявлен в XAML (на месте в визуальном дереве, где известен правильный DataContext):

    

И используется в контекстном меню за пределами визуального дерева:

    

Ура для web.archive.org ! Вот недостающее сообщение в блоге :

Привязка к элементу MenuItem в контекстном меню WPF

Среда, 29 октября 2008 г. – jtango18

Поскольку ContextMenu в WPF не существует в визуальном дереве вашей страницы / windows / элемента управления как таковой, привязка данных может быть немного сложной. Я искал высоко и низко в Интернете для этого, и наиболее распространенный ответ кажется «просто сделайте это в коде позади». НЕПРАВИЛЬНО! Я не пришел в замечательный мир XAML, чтобы вернуться к тому, чтобы делать что-то в коде.

Вот мой пример, который позволит вам привязать к строке, которая существует как свойство вашего windows.

 public partial class Window1 : Window { public Window1() { MyString = "Here is my string"; } public string MyString { get; set; } }  

Важной частью является тег на кнопке (хотя вы можете так же легко установить DataContext кнопки). Здесь хранится ссылка на родительское окно. ContextMenu может получить доступ к этому через свойство PlacementTarget. Затем вы можете передать этот контекст через пункты меню.

Я признаю, что это не самое элегантное решение в мире. Тем не менее, это превосходит настройку в коде позади. Если у кого-то есть еще лучший способ сделать это, я бы с удовольствием это услышал.

Я узнал, что это не работает для меня из-за того, что элемент меню является вложенным, что означает, что мне пришлось пройти дополнительный «Родитель», чтобы найти PlacementTarget.

Лучше всего найти сам ContextMenu как RelativeSource, а затем просто привязать его к цели размещения. Кроме того, поскольку тег является самим окном, а ваша команда находится в режиме просмотра, вам также необходимо установить набор DataContext.

Я закончил с чем-то вроде этого

  ...         

Это означает, что если вы закончите сложное контекстное меню с подменю и т. Д., Вам не нужно добавлять «родительский» к командам каждого уровня.

— РЕДАКТИРОВАТЬ —

Также появилась эта альтернатива для установки тега на каждом элементе ListBoxItem, который привязывается к окну / Usercontrol. Я закончил это, потому что каждый ListBoxItem был представлен их собственной ViewModel, но мне нужны команды меню для выполнения через верхний уровень ViewModel для элемента управления, но передайте их список ViewModel в качестве параметра.

   >  ...    

См. Эту статью от Джастина Тейлора для обходного пути.

Обновить
К сожалению, ссылка на блог больше недоступна. Я попытался объяснить ход в другом SO-ответе. Его можно найти здесь .

Основываясь на ответах HCL , вот что я в итоге использовал:

  ...         

Если (как и я) у вас есть отrotation к уродливым сложным выражениям привязки, вот простое решение для решения этой проблемы. Этот подход по-прежнему позволяет сохранять чистые декларации команд в вашем XAML.

XAML:

     ... 

Код позади:

 private void ContextMenu_ContextMenuOpening(object sender, ContextMenuEventArgs e) { foreach (var item in (sender as ContextMenu).Items) { if(item is MenuItem) { //set the command target to whatever you like here (item as MenuItem).CommandTarget = this; } } } 
  • Какие подходы доступны для фиктивных данных времени разработки в WPF?
  • Должен ли я привязываться к ICollectionView или ObservableCollection
  • Обход проблемы из-за отсутствия оператора 'nameof' в C # для безопасного хранения данных по типу?
  • Управление несколькими выборами с помощью MVVM
  • WPF MVVM INotifyPropertyChanged Implementation - модель или ViewModel
  • Есть ли способ определить, где объявляется / создается привязка WPF?
  • Связывание в WPF с элементом массива, заданным свойством
  • Что означает «{Binding Path =.}» В привязке WPF?
  • Связывающие свойства в коде
  • WPF привязка к локальной переменной
  • Как обнаружить сломанные привязки данных WPF?
  • Давайте будем гением компьютера.