Почему этот связанный с WPF RoutedCommand контекстный MenuItem отключен? - PullRequest
6 голосов
/ 18 января 2009

Сейчас я все еще перебираю WPF и не могу понять, почему этот пункт контекстного меню отключен:

<Window x:Class="DisabledMenuItemProblem.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:DisabledMenuItemProblem"
        Title="Window1" Height="300" Width="300">
    <TextBlock Text="fooooobaaaaaar">
        <TextBlock.ContextMenu>
            <ContextMenu>
                <MenuItem Header="Foo" Command="{x:Static local:MyCommands.FooBar}" />
            </ContextMenu>
        </TextBlock.ContextMenu>
    </TextBlock>
</Window>

using System.Windows;
using System.Windows.Input;

namespace DisabledMenuItemProblem
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
            CommandBindings.Add(new CommandBinding(MyCommands.FooBar, FooExecuted, CanFooExecute));
        }

        public void FooExecuted(object sender, ExecutedRoutedEventArgs e)
        { MessageBox.Show("Foo!"); }

        public void CanFooExecute(object sender, CanExecuteRoutedEventArgs e)
        { e.CanExecute = true; }
    }

    public static class MyCommands
    { 
        public static RoutedCommand FooBar = new RoutedCommand(); 
    }
}

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

Что меня также озадачивает, так это то, что если я выбрасываю кнопку в окне и устанавливаю ее команду FooBar, она работает, и после ее запуска контекстное меню становится доступным!

Ура, ребята, Крис.

Ответы [ 5 ]

10 голосов
/ 19 января 2009

вот общая схема, которую я использую ....

Во-первых, держите свои команды в своем собственном статическом классе, это способствует повторному использованию и т. Д ....

public static class MyCommands
{
    public static RoutedUICommand CmdFoo = new RoutedUICommand("CmdFoo", 
                                                               "CmdFoo", 
                                                               typeof(MyCommands));
}

секунда, зарегистрируйте команду в control / window / etc. вы хотите использовать его, как правило, в конструкторе

public MyControl
{
    public MyControl()
    {
        CommandBindings.Add( 
            new CommandBinding( MyCommands.CmdFoo,   // this is the command object
                                XCutFooCommand,      // execute
                                CanXCuteFooCommand));// can execute?
    }

в-третьих, создайте ваши обработчики в элементе управления / окне / и т.д .....

  public void CanExecuteRerollCommand(object sender, CanExecuteRoutedEventArgs e)
    {
        e.CanExecute = true;  // can this command be executed?
        e.Handled = true;     // has this event been handled?
    }
    public void ExecuteRerollCommand(object sender, ExecutedRoutedEventArgs e)
    {
    // do stuff
    }
}

наконец, ваш xaml должен выглядеть так:

    <ContextMenu>
        <ContextMenu.CommandBindings>
            <CommandBinding Command="foo:MyCommands.CmdFoo" 
                            CanExecute="CanExecuteRerollCommand" 
                            Executed="ExecuteRerollCommand" />
        </ContextMenu.CommandBindings>
        <MenuItem Header="Reroll"  Command="foo:MyCommands.CmdFoo"/>
    </ContextMenu>

обратите внимание, что привязка отсутствует. Также обратите внимание на <CommandBinding> в <ContextMenu>. вот ссылка .... http://www.wiredprairie.us/journal/2007/04/commandtarget_menuitem_context.html

отключаемая команда адресована на этот сайт

8 голосов
/ 12 сентября 2011

Для всех, кто ищет ответ на этот вопрос. После обхода Интернета я нашел наиболее эффективный ответ - включить следующее в любое объявление элемента MenuItem, для которого его команды должны быть услышаны его «владельцем».

С точки зрения непрофессионала; если вы хотите, чтобы команды вашего контекстного меню были услышаны нажатием правой кнопки мыши. Добавьте этот код:

CommandTarget = "{Binding Path = PlacementTarget, RelativeSource = {RelativeSource AncestorType = ContextMenu}}"

Пример:

    <ContextMenu>
        <MenuItem Header="Close" Command="Application.Close" CommandTarget="{Binding Path=PlacementTarget, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
    </ContextMenu>

Это также будет работать в шаблонах (я обнаружил, что многие другие решения не поддерживают). Вот объяснение значения заявления, взятого из другого места (я ужасен при объяснении вещей):

Каждый FrameworkElement имеет DataContext, который является произвольным объектом. Источником привязки данных по умолчанию является DataContext. Вы можете использовать RelativeSource.Self, чтобы изменить источник для привязки к самому FrameworkElement вместо его DataContext. Таким образом, часть RelativeSource просто перемещает вас «на один уровень» вверх от DataContext FrameworkElement до самого FrameworkElement. Когда вы находитесь в FrameworkElement, вы можете указать путь к любому из его свойств. Если FrameworkElement является Popup, у него будет свойство PlacementTarget, которое является другим FrameworkElement, к которому Popup относится.

Короче говоря, если у вас есть всплывающее окно, помещенное, например, относительно TextBox, это выражение устанавливает DataContext из Popup в TextBox, и в результате {Binding Text} где-нибудь в теле Popup будет привязываться к тексту. текстового поля.

Я искренне надеюсь, что эта информация спасет кого-то, кто плохо знаком с WPF, от головной боли, которую я пережил на этих выходных ... хотя она многому меня научила!

Steve

2 голосов
/ 19 января 2009

Насколько я понимаю, так и происходит. Когда отображается ContextMenu, оно отображается во всплывающем окне, которое в основном является отдельным окном. Всплывающее окно не относится к тому же визуальному дереву, что и основное содержимое вашего окна, и поэтому команда не «всплывает» в главное окно. Вот почему ваш метод CanExecute никогда не вызывается. Например, если вы прикрепите CommandBindings к самому ContextMenu, CanExecute будет вызван правильно.

Однако я вспоминаю, что где-то читал, что Popup в некоторых случаях не должен вести себя как обычное окно, а некоторые вещи должны «пузыриться».

Я думаю, что должна происходить какая-то внутренняя магия. Если вы просто измените TextBlock на TextBox, например, он, кажется, будет работать. Бьюсь об заклад, Reflector покажет вам дополнительную логику в TextEditorBase или что-то в этом роде.

Если вам действительно нужно использовать TextBlock, я, вероятно, вручную добавлю CommandBinding к самому ContextMenu, а не к окну.

1 голос
/ 25 сентября 2013

Я нашел самый простой способ преодолеть эту проблему - переместить контекстное меню в ресурс окна и сослаться на него оттуда

<ContextMenu x:Key="ControlContextMenu">
    <ContextMenu.CommandBindings>
        <CommandBinding Command="{StaticResource CloseCommand}" Executed="CloseExecuted" CanExecute="CloseCanExecute" />
    </ContextMenu.CommandBindings>          
    <MenuItem Command="{StaticResource CloseCommand}" />
</ContextMenu>

и затем в UIElement просто установите свойство ContextMenu

<TextBlock ContextMenu="{StaticResource ControlContextMenu}"/>
0 голосов
/ 18 февраля 2009

Еще более простым ответом было бы добавить вызов к Focus () в конструкторе окна. Я столкнулся с этим вопросом вчера и потратил немало времени, чтобы выяснить, что происходит. Я написал об этом здесь: http://cebla5.spaces.live.com/blog/cns!1B8262ED00250003!206.entry

В блоге объясняется, почему вызов Focus () в конструкторе работает.

...