Frage

(Diese Frage bezieht sich auf die eine andere , aber unterschiedlich genug, dass ich denke, es ist garantiert Platzierung hier.)

Hier ist eine (stark snipped) Window:

<Window x:Class="Gmd.TimeTracker2.TimeTrackerMainForm"
    xmlns:local="clr-namespace:Gmd.TimeTracker2"
    xmlns:localcommands="clr-namespace:Gmd.TimeTracker2.Commands"
    x:Name="This"
    DataContext="{Binding ElementName=This}">
    <Window.CommandBindings>
        <CommandBinding Command="localcommands:TaskCommands.ViewTaskProperties" 
                        Executed="HandleViewTaskProperties" 
                        CanExecute="CanViewTaskPropertiesExecute" />
    </Window.CommandBindings>
    <DockPanel>
<!-- snip stuff -->
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
<!-- snip more stuff -->
            <Button Content="_Create a new task" Grid.Row="1" x:Name="btnAddTask" Click="HandleNewTaskClick" />
        </Grid>
    </DockPanel>
</Window>

und hier ist eine (stark snipped) UserControl:

<UserControl x:Class="Gmd.TimeTracker2.TaskStopwatchControl"
             xmlns:local="clr-namespace:Gmd.TimeTracker2"
             xmlns:localcommands="clr-namespace:Gmd.TimeTracker2.Commands"
             x:Name="This"
             DataContext="{Binding ElementName=This}">
    <UserControl.ContextMenu>
        <ContextMenu>
            <MenuItem x:Name="mnuProperties" Header="_Properties" Command="{x:Static localcommands:TaskCommands.ViewTaskProperties}" 
                      CommandTarget="What goes here?" />
        </ContextMenu>
    </UserControl.ContextMenu>
    <StackPanel>
        <TextBlock MaxWidth="100" Text="{Binding Task.TaskName, Mode=TwoWay}" TextWrapping="WrapWithOverflow" TextAlignment="Center" />
        <TextBlock Text="{Binding Path=ElapsedTime}" TextAlignment="Center" />
        <Button Content="{Binding Path=IsRunning, Converter={StaticResource boolToString}, ConverterParameter='Stop Start'}" Click="HandleStartStopClicked" />
    </StackPanel>
</UserControl>

Durch verschiedene Techniken kann ein UserControl dynamisch den Window hinzugefügt werden. Vielleicht über den Button im Fenster. Vielleicht problematische, von einem dauerhaften Sicherungsspeicher, wenn die Anwendung gestartet wird.

Wie aus der XAML zu sehen ist, habe ich beschlossen, dass es Sinn macht für mich Befehle zu versuchen, als eine Möglichkeit, verwenden, um verschiedene Operationen handhaben, dass der Benutzer mit Tasks durchführen kann. Ich tue dies mit dem Endziel alle Befehlslogik in eine formal definierte Controller-Schicht von Factoring, aber ich versuche, einen Schritt zu einer Zeit, Refactoring.

Das Problem, das mir begegnet ist zwischen dem Befehl im UserControl des ContextMenu auf die Interaktion im Zusammenhang und der CanExecute Befehls im Fenster definiert. Wenn die Anwendung zum ersten Mal gestartet und die gespeicherten Aufgaben gestellt werden in TaskStopwatches auf dem Fenster, werden keine aktuellen UI-Elemente ausgewählt. Wenn ich dann sofort eine UserControl im Window in einem Versuch-r klicken Sie auf den ViewTaskProperties Befehl, der CanExecute Handler nie auszuführen läuft und der Menüpunkt bleibt deaktiviert. Wenn ich dann einig UI-Element klicken Sie einfach auf (zum Beispiel der Taste) etwas Schärfe zu geben, werden die CanExecute Handler laufen mit dem Source-Eigenschaft des CanExecuteRoutedEventArgs auf das UI-Element gesetzt, das den Fokus hat.

In gewisser Hinsicht scheint dieses Verhalten zu sein known-- ich gelernt habe, dass Menüs leiten die Veranstaltung durch das Element, das zuletzt konzentrieren mußte immer zu vermeiden, das Ereignis aus dem Menüpunkt sendet. Was ich denke, ich möchte aber ist für die Quelle des Ereignisses die Steuerung selbst, oder die Aufgabe zu sein, dass die Steuerung selbst ist Umwickeln (aber Task ist kein Element, so dass ich glaube nicht, dass es sein kann, eine Quelle).

dachte ich, dass vielleicht war ich auf der CommandTarget im MenuItem die UserControl Eigenschaft fehlt, und mein erster Gedanke war, dass ich den Befehl kommen aus dem Usercontrol, so natürlich ich zuerst versucht wollte:

<MenuItem x:Name="mnuProperties" 
          Header="_Properties" 
          Command="{x:Static localcommands:TaskCommands.ViewTaskProperties}" 
          CommandTarget="{Binding ElementName=This}" />

Dies scheiterte als ungültige Bindung. Ich bin mir nicht sicher warum. Dann dachte ich: „Hmmm, ich suche den Baum, so vielleicht, was ich brauche, ist ein Relative“ und ich versuchte dies:

<MenuItem x:Name="mnuProperties" 
          Header="_Properties" 
          Command="{x:Static localcommands:TaskCommands.ViewTaskProperties}" 
          CommandTarget="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:TaskStopwatchControl}}}" />

Das scheitert auch, aber wenn ich an meinem XAML wieder sah, wurde mir klar, dass der ContextMenu in einer Eigenschaft des Usercontrol ist, es ist nicht ein untergeordnetes Element. So vermuten ich (und an dieser Stelle war es eine Vermutung):

<MenuItem x:Name="mnuProperties" 
          Header="_Properties" 
          Command="{x:Static localcommands:TaskCommands.ViewTaskProperties}" 
          CommandTarget="{Binding RelativeSource={x:Static RelativeSource.Self}}" />

Und das auch fehlgeschlagen.

Eine gescheiterte Vermutung-and-Check wie das ist genug, um mich wieder weg zu machen und erkennt, dass ich eine Art Grundkonzept fehle bin obwohl hier. Also, was soll ich tun?

  1. Ist mein Verständnis Re: die Rolle der CommandTarget korrigieren, dass dies einen Mechanismus bereit, um die Quelle eines Befehls zu ändern
  2. Wie binde ich von einem MenuItem in UserControl.ContextMenu an den besitzenden UserControl? Oder mache ich etwas falsch, nur weil ich das Bedürfnis wahrnehmen?
  3. Ist mein Wunsch, den Kontext eines Befehls durch das Element zu haben, auf den geklickt wurde das Kontextmenü zu erzeugen, wie mit dem Element im Gegensatz, das den Fokus vor dem Kontextmenü hatte, nicht richtig? Vielleicht muss ich stattdessen meinen eigenen Befehl schreiben, der die RoutedUICommand mit:

    private static RoutedUICommand viewTaskPropertiesCommand = new RoutedUICommand("View a task's details.", "ViewTaskProperties", typeof(TaskCommands));
    public static RoutedUICommand ViewTaskProperties
    {
        get { return viewTaskPropertiesCommand; }
    }
    
  4. Gibt es eine tiefere fundamentalen Fehler in meinem Design? Dies ist mein erstes bedeutendes WPF-Projekt, und ich mache es auf meiner eigenen Zeit als Lernerfahrung, also ist ich auf jeden Fall nicht opposed zu einer überlegenen Lösungsarchitektur zu lernen.

War es hilfreich?

Lösung

1: Ja, Command steuert, wo der RoutedCommand aus startet Routing.

2: ContextMenu hat einen Placement Eigenschaft, der Zugang zu Ihrem Usercontrol ermöglicht:

<MenuItem x:Name="mnuProperties" Header="_Properties"
          Command="{x:Static localcommands:TaskCommands.ViewTaskProperties}"
          CommandTarget="{Binding PlacementTarget,
                                  RelativeSource={RelativeSource FindAncestor,
                                                                 AncestorType={x:Type ContextMenu}}}"/>

Um zu vermeiden, dies in jeder Wiederholung MENUITEM könnten Sie einen Style verwenden.

3 & 4: Ich würde sagen, Ihr Wunsch, sinnvoll ist. Da das Handler Ausführen auf dem Fenster ist es jetzt egal ist, aber wenn man die verschiedenen Regionen der Anwendung hat, jede mit ihrer eigenen Execute-Handler für den gleichen Befehl, wäre es egal, wo der Fokus war.

Andere Tipps

ähnliche Lösung, die ich gefunden wurde, die Tag-Eigenschaft des Mutter mit der Datacontext greifen:

<Grid Tag="{Binding Path=DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}">
    <Grid.ContextMenu>
        <ContextMenu DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
            <MenuItem 
                Header="{Binding Path=ToolbarDelete, Mode=Default, Source={StaticResource Resx}}" 
                Command="{Binding RemoveCommand}" 
                CommandParameter="{Binding DataContext.Id, RelativeSource={RelativeSource TemplatedParent}}"/>
        </ContextMenu>
    </Grid.ContextMenu>

    <TextBlock Text="{Binding Name}" Padding="2" />

</Grid>
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top