Вопрос по wpf – Привязка ElementName из MenuItem в ContextMenu

64

Кто-нибудь еще заметил, что привязки с ElementName не разрешаются правильно дляMenuItem объекты, которые содержатся вContextMenu объекты? Проверьте этот образец:

<Window x:Class="EmptyWPF.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300"
    x:Name="window">
    <Grid x:Name="grid" Background="Wheat">
        <Grid.ContextMenu>
            <ContextMenu x:Name="menu">
                <MenuItem x:Name="menuItem" Header="Window" Tag="{Binding ElementName=window}" Click="MenuItem_Click"/>
                <MenuItem Header="Grid" Tag="{Binding ElementName=grid}" Click="MenuItem_Click"/>
                <MenuItem Header="Menu" Tag="{Binding ElementName=menu}" Click="MenuItem_Click"/>
                <MenuItem Header="Menu Item" Tag="{Binding ElementName=menuItem}" Click="MenuItem_Click"/>
            </ContextMenu>
        </Grid.ContextMenu>
        <Button Content="Menu" 
                HorizontalAlignment="Center" VerticalAlignment="Center" 
                Click="MenuItem_Click" Tag="{Binding ElementName=menu}"/>
        <Menu HorizontalAlignment="Center" VerticalAlignment="Bottom">
            <MenuItem x:Name="anotherMenuItem" Header="Window" Tag="{Binding ElementName=window}" Click="MenuItem_Click"/>
            <MenuItem Header="Grid" Tag="{Binding ElementName=grid}" Click="MenuItem_Click"/>
            <MenuItem Header="Menu" Tag="{Binding ElementName=menu}" Click="MenuItem_Click"/>
            <MenuItem Header="Menu Item" Tag="{Binding ElementName=anotherMenuItem}" Click="MenuItem_Click"/>
        </Menu>
    </Grid>
</Window>

Все привязки работают отлично, за исключением привязок, содержащихся в ContextMenu. Они выводят ошибку в окно вывода во время выполнения.

Кто-нибудь знает какие-нибудь обходные пути? Что здесь происходит?

Определены ли в ContextMenus собственные области имен по умолчанию? Josh G
Проблема, очевидно, имеет отношение к именным областям ... Josh G

Ваш Ответ

6   ответов
1

зачем прибегать к волшебным трюкам, чтобы избежать одной строки кода внутри обработчика событий для щелчка мышью, который вы уже обрабатываете:

    private void MenuItem_Click(object sender, System.Windows.RoutedEventArgs e)
    {
        // this would be your tag - whatever control can be put as string intot he tag
        UIElement elm = Window.GetWindow(sender as MenuItem).FindName("whatever control") as UIElement;
    }
Error: User Rate Limit Exceeded
20

«ContextMenu» не содержится в визуальном дереве и "ElementName"; привязка не сработает. Установка контекстного меню «s» NameScope » как предполагает принятый ответ, работает, только если контекстное меню не определено в «DataTemplate». Я решил это с помощьюРасширение разметки {x: Reference} который аналогичен «ElementName»; связывание, но разрешает связывание по-другому, минуя визуальное дерево. Я считаю, что это намного удобнее для чтения, чем использование PlacementTarget. Вот пример:

<Image Source="{Binding Image}">       
    <Image.ContextMenu>
        <ContextMenu>
            <MenuItem Header="Delete" 
                      Command="{Binding Source={x:Reference Name=Root}, Path=DataContext.RemoveImage}"
                      CommandParameter="{Binding}" />
        </ContextMenu>
    </Image.ContextMenu>
</Image>

Согласно MSDN-документации

x:Reference is a construct defined in XAML 2009. In WPF, you can use XAML 2009 features, but only for XAML that is not WPF markup-compiled. Markup-compiled XAML and the BAML form of XAML do not currently support the XAML 2009 language keywords and features.

что бы это ни значило ... Хотя у меня работает.

Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
4

Сделать верхний уровеньWindow/UserControl воплощать в жизньINameScope и установитьNameScope изContextMenu на верхний уровень контроля.

public class Window1 : Window, INameScope
{
    public Window1()
    {
        InitializeComponent();
        NameScope.SetNameScope(contextMenu, this);
    }

    // Event handlers and etc...

    // Implement INameScope similar to this:
    #region INameScope Members

    Dictionary<string, object> items = new Dictionary<string, object>();

    object INameScope.FindName(string name)
    {
        return items[name];
    }

    void INameScope.RegisterName(string name, object scopedElement)
    {
        items.Add(name, scopedElement);
    }

    void INameScope.UnregisterName(string name)
    {
        items.Remove(name);
    }

    #endregion
}

Это позволяет контекстному меню находить именованные элементы внутриWindow, Есть еще варианты?

52

В коде для UserControl:

NameScope.SetNameScope(contextMenu, NameScope.GetNameScope(this));
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
5

вашего элемента управления, поэтому они не могут найти имя вашего элемента.

Попробуйте установить текстовый контекст вашего контекстного меню в качестве цели размещения. Вы должны использовать RelativeSource.

<ContextMenu 
   DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource Self}}"> ...
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded Josh G
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded Josh G
Error: User Rate Limit Exceeded Josh G
19

что вы хотите, чтобы внутриDataContextнапример, выMVVMing Это)

Вариант первый, где родительский элементContextMenu не вDataTemplate:

Command="{Binding PlacementTarget.DataContext.MyCommand, 
         RelativeSource={RelativeSource AncestorType=ContextMenu}}"

Это сработало бы для вопроса OP. Это не сработает, если вы находитесь внутриDataTemplate, В этих случаяхDataContext часто является одним из многих в коллекции, иICommand вы хотите связать это одноуровневое свойство коллекции в той же модели представления (DataContext из окна, скажем).

В этих случаях вы можете воспользоватьсяTag временно держать родителяDataContext которая содержит как коллекцию, так и вашу ICommand:

class ViewModel
{
    public ObservableCollection<Derp> Derps { get;set;}
    public ICommand DeleteDerp {get; set;}
} 

и в XAML

<!-- ItemsSource binds to Derps in the DataContext -->
<StackPanel
    Tag="{Binding DataContext, ElementName=root}">
    <StackPanel.ContextMenu>
        <ContextMenu>
            <MenuItem
                Header="Derp"                       
                Command="{Binding PlacementTarget.Tag.DeleteDerp, 
                RelativeSource={RelativeSource 
                                    AncestorType=ContextMenu}}"
                CommandParameter="{Binding PlacementTarget.DataContext, 
                RelativeSource={RelativeSource AncestorType=ContextMenu}}">
            </MenuItem>
Error: User Rate Limit Exceeded Josh G
Error: User Rate Limit Exceeded Josh G

Похожие вопросы