Команда привязки MVVM к элементу contextmenu
Я пытаюсь связать команду с элементом меню в WPF. Я использую тот же метод, который работал для всех моих других привязок команд, но я не могу понять, почему он здесь не работает.
В настоящее время я связываю свои команды следующим образом:
Command = "{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.MyCommand}"
Вот где это происходит неправильно (это внутри UserControl)
<Button Height="40" Margin="0,2,0,0" CommandParameter="{Binding Name}"
Command = "{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.ConnectCommand}">
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Remove" CommandParameter="{Binding Name}"
Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.RemoveCommand}"/>
</ContextMenu>
</Button.ContextMenu>
...
Первое связывание команд работает так, как должно, но второе отказывается что-либо делать.
Я попытался изменить уровень предка и называть мой элемент управления, чтобы получить доступ к нему через ElementName вместо RelativeSource, но все равно никаких изменений. Он продолжает говорить "Не могу найти источник для привязки со ссылкой..."
Что мне не хватает?
Ответы
Ответ 1
(Edit) Поскольку вы упомянули, что это в шаблоне ItemsControl, все по-другому:
1) Получите класс BindingProxy из этого блога (и прочитайте блог, поскольку это интересная информация): Как привязываться к данным, когда DataContext не унаследован.
В основном элементы в ItemsControl (или ContextMenu) не являются частью визуального или логического дерева и поэтому не могут найти DataContext вашего UserControl. Приносим извинения за то, что вы не писали больше об этом здесь, но автор проделал хорошую работу, объясняя это шаг за шагом, поэтому я не мог дать полное объяснение всего в нескольких строках.
2) Сделайте что-то вроде этого: (вам, возможно, придется немного приспособить его, чтобы заставить его работать под вашим контролем):
а. Это даст вам доступ к DataContext UserControl с помощью StaticResource:
<UserControl.Resources>
<BindingProxy
x:Key="DataContextProxy"
Data="{Binding}" />
</UserControl.Resources>
б. Это использует DataContextProxy, определенный в (a):
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Remove" CommandParameter="{Binding Name}"
Command="{Binding Path=Data.RemoveCommand, Source={StaticResource DataContextProxy}}"/>
</ContextMenu>
Это сработало для нас в таких вещах, как деревья и datagrids.
Ответ 2
ContextMenu находится в другом логическом дереве, поэтому RelativeSource не работает. Но контекстное меню наследует DataContext из своего "контейнера", в этом случае это Button. Это достаточно в общем случае, но в вашем случае вам нужны два "контекста данных", элемента ItemsControl и самого элемента ItemsControl.
Я думаю, у вас нет другого выбора, кроме как объединить ваши модели взглядов в один, внедрить пользовательский класс, который будет использоваться в качестве контекста данных элемента ItemsControl и содержать как "Имя", так и "Удалить команду", или ваша модель представления элементов может определить "прокси" RemoveCommand, что будет вызывать родительскую команду внутри
EDIT:
Я немного изменил код Baboon, он должен работать следующим образом:
<Button Height="40" Margin="0,2,0,0" CommandParameter="{Binding Name}"
Tag="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}"
Command = "{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.ConnectCommand}">
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Remove"
CommandParameter="{Binding Name}"
Command="{Binding Path=PlacementTarget.Tag.DataContext.RemoveCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}"/>
</ContextMenu>
</Button.ContextMenu>
Ответ 3
koshdim находится на месте, он работает как шарм!! Спасибо Кошдим
Я изменил свой код, чтобы он поместился в моем контекстном меню
<DataGrid
AutoGenerateColumns="False"
HeadersVisibility="Column"
Name="dgLosses"
SelectedItem="{Binding SelectedItem, Mode= TwoWay}"
AllowDrop="True"
ItemsSource="{Binding Losses}"
Tag="{Binding DataContext,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Window}}">
<DataGrid.ContextMenu >
<ContextMenu >
<MenuItem Header="Move to Top " Command="{Binding PlacementTarget.Tag.MoveToTopCommand,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ContextMenu}}" ></MenuItem>
<MenuItem Header="Move to Period 1" Command="{Binding PlacementTarget.Tag.MoveToPeriod1Command,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ContextMenu}}" ></MenuItem>
<MenuItem Header="Move to Period 2" Command="{Binding PlacementTarget.Tag.MoveToPeriod2Command,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ContextMenu}}" ></MenuItem>
<MenuItem Header="Move to Period 3" Command="{Binding PlacementTarget.Tag.MoveToPeriod3Command,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ContextMenu}}" ></MenuItem>
</ContextMenu>
</DataGrid.ContextMenu>
Ответ 4
Это сложная проблема, конечно, вы найдете быстрое обходное решение, но здесь нет-волшебное решение:
<Button Height="40" Margin="0,2,0,0" CommandParameter="{Binding Name}"
Tag={Binding}
Command = "{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.ConnectCommand}">
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Remove"
CommandParameter="{Binding Path=PlacementTarget.Tag.Name, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}"
Command="{Binding Path=PlacementTarget.Tag.RemoveCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}"/>
</ContextMenu>
</Button.ContextMenu>
...
Это сводится к использованию Tag
PlacementTarget
(здесь Button
).