Porque é que esta Contexto limite WPF RoutedCommand MenuItem desativado?

StackOverflow https://stackoverflow.com/questions/455551

  •  19-08-2019
  •  | 
  •  

Pergunta

Ainda estou tateando meu caminho de volta WPF no momento, e não consegue descobrir por que esse item de menu de contexto está desativado:

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

O que eu estou ausente?

O que também me desconcertante é que se eu jogar um botão na janela e definir o seu comando para FooBar ele funciona, e uma vez que a sua foi executado, em seguida, o menu de contexto fica habilitado!

Elogios caras, Chris.

Foi útil?

Solução

aqui é o padrão geral que eu uso ....

Em primeiro lugar, manter seus comandos em relação á classe estática própria, isso promove a reutilização, etc ....

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

Em segundo lugar, registrar o comando no controle / janela / etc. você quiser usá-lo em, normalmente no construtor

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

Em terceiro lugar, criar seus manipuladores no controle / janela / etc .....

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

Finalmente, o XAML deve ficar assim:

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

aviso de que não há é vinculativa. Além disso, observe o <CommandBinding> na <ContextMenu>. aqui é uma referência .... http://www.wiredprairie.us/journal/2007/04/commandtarget_menuitem_context.html

o comando sendo está desativado abordados em neste site

Outras dicas

Para quem procura uma resposta para esta questão - Depois de arrasto na internet eu encontrei a resposta mais eficaz a ser para incluir o seguinte em qualquer declaração de um MenuItem que precisa de seus comandos para ser ouvido por ele é "dono" <. / p>

Em termos leigos; se quiser que os comandos do seu menu de contexto para ser ouvido pela coisa que você botão direito sobre. Adicione este código:

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

Exemplo:

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

Este trabalho também dentro de Templates (algo que eu encontrei um monte de outras soluções não suporte). Aqui está uma explicação sobre o significado da declaração tirada de outro lugar (estou chocante no explicar as coisas):

Cada FrameworkElement tem um DataContext que é um objeto arbitrário. A fonte padrão para uma ligação de dados é que DataContext. Você pode usar RelativeSource.Self para alterar a fonte para uma ligação para o próprio FrameworkElement ao invés de seu DataContext. Assim, a parte RelativeSource apenas se move você "um nível acima" do DataContext do FrameworkElement ao próprio FrameworkElement. Uma vez que você está no FrameworkElement você pode especificar um caminho para qualquer uma de suas propriedades. Se o FrameworkElement é um Popup, ele terá uma propriedade PlacementTarget que é o outro FrameworkElement que o Popup está posicionado em relação a.

Em suma, se você tem um Popup colocado em relação a um TextBox por exemplo, que a expressão define o DataContext do Popup à caixa de texto e, como resultado {Binding Texto} em algum lugar do corpo do Popup ligaria ao texto do TextBox.

Eu sinceramente espero que esta informação salva alguém que é novo para WPF a dor de cabeça que eu já passei por este fim de semana ... embora me ensinou muito!

Steve

Tanto quanto eu entendo que isso é o que acontece. Quando o ContextMenu é mostrado é mostrado em um Popup que é basicamente uma janela separada. O pop-up não pertencem à mesma árvore visual como o conteúdo principal em sua janela e, portanto, o comando não 'bolha'-se em sua janela principal. É por isso que o seu método de CanExecute nunca é chamado. Se por exemplo você anexar as CommandBindings no próprio ContextMenu o CanExecute será chamado corretamente.

No entanto, eu lembro de ter lido em algum lugar que o Popup, em certos casos não deve se comportar como uma janela comum e certas coisas devem 'bolha' up.

Eu acho que deve haver alguma magia interna acontecendo. Se você simplesmente mudar o TextBlock para uma caixa de texto, por exemplo, parece funcionar. Aposto refletor iria mostrar-lhe alguma lógica extra no TextEditorBase ou algo parecido.

Se você realmente precisa usar um TextBlock eu provavelmente adicionar manualmente o CommandBinding para o próprio ContextMenu em vez de na janela.

Eu encontrei a maneira mais fácil de superar esse problema é mover o menu de contexto em um recurso janela e referenciá-lo de lá

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

e em seguida, no UIElement apenas definir a propriedade ContextMenu

<TextBlock ContextMenu="{StaticResource ControlContextMenu}"/>

Uma resposta ainda mais simples seria adicionar uma chamada para Focus () no construtor do Window. Topei com esta questão ontem e passou um pouco de tempo para descobrir o que estava acontecendo. Eu escrevi sobre isso aqui: http://cebla5.spaces.live. com / blog / CNS! 1B8262ED00250003! 206.entry

O post vai explicar por que chamar Focus () em obras do construtor.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top