Perché questo menu contestuale associato a RoutingCommand di WPF è disabilitato?

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

  •  19-08-2019
  •  | 
  •  

Domanda

Al momento sto ancora armeggiando con WPF e non riesco a capire perché questa voce di menu contestuale sia disabilitata:

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

Cosa mi sto perdendo?

Quello che mi sconcerta anche è che se lancio un pulsante nella finestra e imposto il suo comando su FooBar funziona, e una volta eseguito, il menu contestuale viene abilitato!

Saluti ragazzi, Chris.

È stato utile?

Soluzione

ecco lo schema generale che uso ....

per prima cosa, mantieni i tuoi comandi nella loro classe statica, questo promuove il riutilizzo, ecc ....

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

secondo, registra il comando in control / window / etc. in cui vuoi usarlo, normalmente nel costruttore

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

terzo, crea i tuoi gestori in control / window / 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
    }
}

infine, il tuo xaml dovrebbe assomigliare a questo:

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

nota che non c'è nessun legame. Inoltre, notare <CommandBinding> in <ContextMenu>. ecco un riferimento .... http://www.wiredprairie.us/journal/2007/04/commandtarget_menuitem_context.html

il comando da disabilitare è indirizzato a questo sito

Altri suggerimenti

Per chiunque cerchi una risposta a questo problema - Dopo aver esplorato Internet ho trovato la risposta più efficace per includere quanto segue in qualsiasi dichiarazione di un MenuItem che necessita dei suoi comandi per essere ascoltato dal suo " proprietario quot &;.

In parole povere; se vuoi che i comandi del tuo menu contestuale siano ascoltati dalla cosa su cui fai clic destro. Aggiungi questo codice:

CommandTarget = " {Percorso di associazione = PlacementTarget, RelativeSource = {RelativeSource AncestorType = ContextMenu}} "

Esempio:

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

Funzionerà anche con i template (qualcosa che ho trovato molte altre soluzioni da non supportare). Ecco una spiegazione del significato dell'affermazione presa altrove (sto spaventando nello spiegare le cose):

  

Ogni FrameworkElement ha un DataContext che è un oggetto arbitrario. L'origine predefinita per un'associazione dati è DataContext. È possibile utilizzare RelativeSource.Self per modificare l'origine per un'associazione a FrameworkElement stesso anziché a DataContext. Quindi la parte RelativeSource ti sposta solo & Quot; su di un livello & Quot; dal DataContext di FrameworkElement allo stesso FrameworkElement. Una volta che sei in FrameworkElement puoi specificare un percorso per una qualsiasi delle sue proprietà. Se FrameworkElement è un Popup, avrà una proprietà PlacementTarget che è l'altro FrameworkElement al quale il Popup è posizionato rispetto.

     

In breve, se hai un Popup posizionato relativamente ad un TextBox, ad esempio, quell'espressione imposta il DataContext del Popup sul TextBox e di conseguenza {Binding Text} da qualche parte nel corpo del Popup si legherebbe al testo della casella di testo.

Spero sinceramente che questa informazione salvi qualcuno che è nuovo in WPF il mal di testa che ho passato questo fine settimana ... anche se mi ha insegnato molto!

Steve

Per quanto ho capito, questo è ciò che accade. Quando viene mostrato il ContextMenu, questo viene mostrato in un popup che è sostanzialmente una finestra separata. Il popup non appartiene allo stesso albero visivo del contenuto principale della finestra e pertanto il comando non si "riempie" nella finestra principale. Ecco perché il tuo metodo CanExecute non viene mai chiamato. Se ad esempio si collegano i CommandBindings sul ContextMenu stesso, CanExecute verrà chiamato correttamente.

Comunque ricordo di aver letto da qualche parte che il Popup in alcuni casi non dovrebbe comportarsi come una normale finestra e che certe cose dovrebbero "gonfiarsi".

Penso che ci debba essere un po 'di magia interna in corso. Ad esempio, se si modifica semplicemente TextBlock in TextBox, sembra che funzioni. Scommetto che Reflector ti mostrerebbe qualche logica in più in TextEditorBase o qualcosa del genere.

Se hai davvero bisogno di usare un TextBlock probabilmente aggiungerei manualmente CommandBinding al ContextMenu stesso piuttosto che alla finestra.

Ho trovato il modo più semplice per superare questo problema è spostare il menu di scelta rapida in una finestra e fare riferimento da lì

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

e quindi su UIElement basta impostare la proprietà ContextMenu

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

Una risposta ancora più semplice sarebbe quella di aggiungere una chiamata a Focus () nel costruttore della finestra. Mi sono imbattuto in questo problema ieri e ho trascorso un bel po 'di tempo a capire cosa stesse succedendo. Ho scritto sul blog qui: http://cebla5.spaces.live. com / blog / CNS! 1B8262ED00250003! 206.entry

Il post sul blog spiegherà perché la chiamata Focus () nel costruttore funziona.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top