Подтвердить что ты не робот

Команда привязки 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, но все равно никаких изменений. Он продолжает говорить "Не могу найти источник для привязки со ссылкой..."

Что мне не хватает?

4b9b3361

Ответ 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).