WPF: привязка ContextMenu к команде MVVM
Предположим, у меня есть Окно с возвращающим команду свойством (на самом деле это UserControl с Command в classе ViewModel, но давайте как можно проще воспроизвести проблему).
Следующие работы:
Но следующее не работает.
- Image UriSource и привязка данных
- Когда следует использовать # и = в элементах управления ASP.NET?
- ElementName привязка из MenuItem в ContextMenu
- Заполнить Combobox из базы данных
- ItemsControl с несколькими DataTemplates для viewmodel
Сообщение об ошибке, которое я получаю, это
Ошибка System.Windows.Data: 4: Невозможно найти источник для привязки со ссылкой «ElementName = myWindow». BindingExpression: Path = МояКоманда; DataItem = NULL; целевым элементом является «MenuItem» (Name = ”); target является «Command» (тип «ICommand»)
Зачем? И как мне это исправить? Использование DataContext
не является вариантом, так как эта проблема встречается вниз по визуальному дереву, где DataContext уже содержит фактические данные, которые отображаются. Я уже пытался использовать {RelativeSource FindAncestor, ...}
вместо этого, но это дает аналогичное сообщение об ошибке.
- WPF перед записью
- Для чего нужен DataContext?
- Обнаружение ошибок проверки WPF
- Почему привязка данных WPF к исключению ласточки?
- Принуждение WPF TextBox больше не работает в .NET 4.0
- Перетаскивание из источника данных в окно WPF не работает
- Как правильно привязать xml к WPF DataGrid?
- Каковы различные режимы привязки WPF?
Проблема заключается в том, что 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; } } }