c# - команды - wpf mvvm кнопки



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

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

XAML:

<ContextMenu ContextMenuOpening="ContextMenu_ContextMenuOpening">
    <MenuItem Command="Save"/>
    <Separator></Separator>
    <MenuItem Command="Close"/>
    ...

Код позади:

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;
        } 
    }
}

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

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

<Window x:Class="Window1" ... x:Name="myWindow">
    <Menu>
        <MenuItem Command="{Binding MyCommand, ElementName=myWindow}" Header="Test" />
    </Menu>
</Window>

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

<Window x:Class="Window1" ... x:Name="myWindow">
    <Grid>
        <Grid.ContextMenu>
            <ContextMenu>
                <MenuItem Command="{Binding MyCommand, ElementName=myWindow}" Header="Test" />
            </ContextMenu>            
        </Grid.ContextMenu>
    </Grid>
</Window>

Я получаю сообщение об ошибке:

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

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


Answer #1

Исходя из ответа HCL , вот что я в итоге использовал:

<Window x:Class="Window1" ... x:Name="myWindow">
    ...
    <Grid Tag="{Binding ElementName=myWindow}">
        <Grid.ContextMenu>
            <ContextMenu>
                <MenuItem Command="{Binding Parent.PlacementTarget.Tag.MyCommand, 
                                            RelativeSource={RelativeSource Self}}"
                          Header="Test" />
            </ContextMenu>
        </Grid.ContextMenu>
    </Grid>
</Window>

Answer #2

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

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


Answer #3

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

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

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

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

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

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

    public string MyString
    {
        get;
        set;

    }
}

    <Button Content="Test Button" Tag="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}}">
        <Button.ContextMenu>
            <ContextMenu DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}" >
                <MenuItem Header="{Binding MyString}"/>
            </ContextMenu>
        </Button.ContextMenu>
    </Button>

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

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





mvvm