Ich bin auf ein Problem mit WPF und Befehlen gestoßen, die an eine Schaltfläche in der DataTemplate eines ItemsControl gebunden sind. Das Szenario ist recht einfach. Das ItemsControl ist an eine Liste von Objekten gebunden, und ich möchte in der Lage sein, jedes Objekt in der Liste durch Klicken auf eine Schaltfläche zu entfernen. Die Schaltfläche führt einen Befehl aus, und der Befehl kümmert sich um das Löschen. Der CommandParameter ist an das Objekt gebunden, das ich löschen möchte. Auf diese Weise weiß ich, worauf der Benutzer geklickt hat. Ein Benutzer sollte nur in der Lage sein, seine "eigenen" Objekte zu löschen. Daher muss ich beim Aufruf "CanExecute" des Befehls einige Überprüfungen durchführen, um zu überprüfen, ob der Benutzer über die richtigen Berechtigungen verfügt.
Das Problem ist, dass der an CanExecute übergebene Parameter beim ersten Aufruf NULL ist. Daher kann ich die Logik zum Aktivieren / Deaktivieren des Befehls nicht ausführen. Wenn ich es jedoch immer aktiviert mache und dann auf die Schaltfläche klicke, um den Befehl auszuführen, wird der CommandParameter korrekt übergeben. Das bedeutet also, dass die Bindung gegen den CommandParameter funktioniert.
Die XAML für das ItemsControl und das DataTemplate sieht folgendermaßen aus:
<ItemsControl
x:Name="commentsList"
ItemsSource="{Binding Path=SharedDataItemPM.Comments}"
Width="Auto" Height="Auto">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button
Content="Delete"
FontSize="10"
Command="{Binding Path=DataContext.DeleteCommentCommand, ElementName=commentsList}"
CommandParameter="{Binding}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Wie Sie sehen können, habe ich eine Liste von Kommentarobjekten. Ich möchte, dass der CommandParameter des DeleteCommentCommand an das Command-Objekt gebunden wird.
Meine Frage ist also: Hat jemand dieses Problem schon einmal erlebt? CanExecute wird in meinem Befehl aufgerufen, aber der Parameter ist beim ersten Mal immer NULL - warum ist das so?
Update: Ich konnte das Problem ein wenig eingrenzen. Ich habe einen leeren Debug ValueConverter hinzugefügt, damit ich eine Nachricht ausgeben kann, wenn der CommandParameter datengebunden ist. Es stellt sich heraus, dass das Problem darin besteht, dass die CanExecute-Methode ausgeführt wird, bevor der CommandParameter an die Schaltfläche gebunden wird. Ich habe versucht, den CommandParameter vor dem Command festzulegen (wie vorgeschlagen) - aber es funktioniert immer noch nicht. Irgendwelche Tipps zur Steuerung.
Update2: Gibt es eine Möglichkeit zu erkennen, wann die Bindung "abgeschlossen" ist, damit ich eine Neubewertung des Befehls erzwingen kann? Auch - ist es ein Problem, dass ich mehrere Schaltflächen habe (eine für jedes Element im ItemsControl), die an dieselbe Instanz eines Befehlsobjekts gebunden sind?
Update3: Ich habe eine Reproduktion des Fehlers auf mein SkyDrive hochgeladen: http://cid-1a08c11c407c0d8e.skydrive.live.com/self.aspx/Code%20samples/CommandParameterBinding.zip
Antworten:
Ich bin auf ein ähnliches Problem gestoßen und habe es mit meinem vertrauenswürdigen TriggerConverter gelöst.
Dieser Wertekonverter verwendet eine beliebige Anzahl von Parametern und gibt den ersten als konvertierten Wert zurück. Bei Verwendung in einem MultiBinding in Ihrem Fall sieht es wie folgt aus.
Sie müssen TriggerConverter irgendwo als Ressource hinzufügen, damit dies funktioniert. Jetzt wird die Command-Eigenschaft erst festgelegt, wenn der Wert für den CommandParameter verfügbar ist. Sie können stattdessen auch an RelativeSource.Self und CommandParameter binden. um den gleichen Effekt zu erzielen.
quelle
Ich hatte das gleiche Problem, als ich versuchte, mich an einen Befehl in meinem Ansichtsmodell zu binden.
Ich habe es geändert, um eine relative Quellbindung zu verwenden, anstatt auf das Element mit Namen zu verweisen, und das hat den Trick getan. Die Parameterbindung hat sich nicht geändert.
Alter Code:
Neuer Code:
Update : Ich bin gerade auf dieses Problem gestoßen, ohne ElementName zu verwenden. Ich binde an einen Befehl in meinem Ansichtsmodell und mein Datenkontext der Schaltfläche ist mein Ansichtsmodell. In diesem Fall musste ich einfach das CommandParameter-Attribut vor das Command-Attribut in der Button-Deklaration (in XAML) verschieben.
quelle
CommandParameter
undCommand
macht mir Angst.Ich habe festgestellt, dass die Reihenfolge, in der ich Command und CommandParameter einstelle, einen Unterschied macht. Durch das Festlegen der Command-Eigenschaft wird CanExecute sofort aufgerufen, sodass CommandParameter bereits zu diesem Zeitpunkt festgelegt werden soll.
Ich habe festgestellt, dass das Ändern der Reihenfolge der Eigenschaften in der XAML tatsächlich Auswirkungen haben kann, obwohl ich nicht sicher bin, ob es Ihr Problem lösen wird. Es ist jedoch einen Versuch wert.
Sie scheinen vorzuschlagen, dass die Schaltfläche niemals aktiviert wird, was überraschend ist, da ich erwarten würde, dass der CommandParameter in Ihrem Beispiel kurz nach der Command-Eigenschaft festgelegt wird. Wird durch das Aufrufen von CommandManager.InvalidateRequerySuggested () die Schaltfläche aktiviert?
quelle
Ich habe mir eine andere Option ausgedacht, um dieses Problem zu umgehen, das ich teilen wollte. Da die CanExecute-Methode des Befehls ausgeführt wird, bevor die CommandParameter-Eigenschaft festgelegt wird, habe ich eine Hilfsklasse mit einer angehängten Eigenschaft erstellt, die erzwingt, dass die CanExecute-Methode erneut aufgerufen wird, wenn sich die Bindung ändert.
Und dann auf der Schaltfläche möchten Sie einen Befehlsparameter an ...
Ich hoffe, das hilft vielleicht jemand anderem bei dem Problem.
quelle
Dies ist ein alter Thread, aber da Google mich hierher gebracht hat, als ich dieses Problem hatte, werde ich mit einer Schaltfläche hinzufügen, was für mich für eine DataGridTemplateColumn funktioniert hat.
Ändern Sie die Bindung von:
zu
Ich bin mir nicht sicher, warum es funktioniert, aber es hat bei mir funktioniert.
quelle
Möglicherweise können Sie meine verwenden
CommandParameterBehavior
, die ich gestern in den Prisma-Foren gepostet habe. Es fügt das fehlende Verhalten hinzu, bei dem eine Änderung derCommandParameter
UrsacheCommand
erneut abgefragt wird.Es gibt hier eine gewisse Komplexität, die durch meine Versuche verursacht wird, den Speicherverlust zu vermeiden, der verursacht wird, wenn Sie
PropertyDescriptor.AddValueChanged
ohne späteren Anruf anrufenPropertyDescriptor.RemoveValueChanged
. Ich versuche das zu beheben, indem ich die Registrierung des Handlers beim Entladen des Ekements aufhebe.Sie müssen das Material wahrscheinlich entfernen, es
IDelegateCommand
sei denn, Sie verwenden Prism (und möchten die gleichen Änderungen wie ich an der Prism-Bibliothek vornehmen). Beachten Sie auch, dass wirRoutedCommand
hier im Allgemeinen nicht s verwenden (wir verwenden PrismenDelegateCommand<T>
für so ziemlich alles).CommandManager.InvalidateRequerySuggested
Machen Sie mich also bitte nicht verantwortlich, wenn mein Aufruf, eine Art Kollaps-Kaskade für Quantenwellenfunktionen auszulösen, die das bekannte Universum oder irgendetwas zerstört.quelle
Ich bin kürzlich auf das gleiche Problem gestoßen (für mich war es für die Menüpunkte in einem Kontextmenü), und obwohl es möglicherweise nicht für jede Situation eine geeignete Lösung ist, habe ich einen anderen (und viel kürzeren!) Weg gefunden, dies zu lösen Problem:
Ignorieren der
Tag
umgangene Problemumgehung für den speziellen Fall des Kontextmenüs , besteht der Schlüssel hier darin, dieCommandParameter
regelmäßig zu binden , aber dieCommand
mit der zusätzlichen zu bindenIsAsync=True
. Dies verzögert die Bindung des eigentlichen Befehls (und damit seinesCanExecute
Aufrufs) etwas, sodass der Parameter bereits verfügbar ist. Dies bedeutet jedoch, dass für einen kurzen Moment der aktivierte Zustand möglicherweise falsch ist, aber für meinen Fall war dies vollkommen akzeptabel.quelle
Es gibt eine relativ einfache Möglichkeit, dieses Problem mit DelegateCommand zu "beheben", obwohl die DelegateCommand-Quelle aktualisiert und die Microsoft.Practices.Composite.Presentation.dll neu kompiliert werden muss.
1) Laden Sie den Prism 1.2-Quellcode herunter und öffnen Sie die Datei CompositeApplicationLibrary_Desktop.sln. Hier ist ein Composite.Presentation.Desktop-Projekt, das die DelegateCommand-Quelle enthält.
2) Ändern Sie unter dem öffentlichen Ereignis EventHandler CanExecuteChanged Folgendes wie folgt:
3) Ändern Sie es unter "Geschützte virtuelle Leere OnCanExecuteChanged () wie folgt:
4) Kompilieren Sie die Lösung neu und navigieren Sie zum Ordner Debug oder Release, in dem sich die kompilierten DLLs befinden. Kopieren Sie die Dateien Microsoft.Practices.Composite.Presentation.dll und .pdb (falls gewünscht) an die Stelle, an der Sie auf Ihre externen Assemblys verweisen, und kompilieren Sie dann Ihre Anwendung neu, um die neuen Versionen abzurufen.
Danach sollte CanExecute jedes Mal ausgelöst werden, wenn die Benutzeroberfläche Elemente rendert, die an den betreffenden DelegateCommand gebunden sind.
Pass auf dich auf, Joe
Schiedsrichter bei Google Mail
quelle
Nachdem ich einige gute Antworten auf ähnliche Fragen gelesen hatte, habe ich in Ihrem Beispiel den DelegateCommand leicht geändert, damit er funktioniert. Anstatt zu verwenden:
Ich habe es geändert in:
Ich habe die folgenden zwei Methoden entfernt, weil ich zu faul war, um sie zu beheben
und
Und das ist alles ... dies scheint sicherzustellen, dass CanExecute aufgerufen wird, wenn sich die Bindung ändert und nach der Execute-Methode
Es wird nicht automatisch ausgelöst, wenn das ViewModel geändert wird. Wie in diesem Thread erwähnt, ist dies jedoch durch Aufrufen des CommandManager.InvalidateRequerySuggested im GUI-Thread möglich
quelle
DispatcherPriority.Normal
zu hoch ist, um zuverlässig zu arbeiten (oder in meinem Fall überhaupt). Die VerwendungDispatcherPriority.Loaded
funktioniert gut und scheint angemessener zu sein (dh es wird ausdrücklich angegeben, dass der Delegat erst aufgerufen werden soll, wenn die dem Ansichtsmodell zugeordneten UI-Elemente tatsächlich geladen wurden).Hey Jonas, ich bin mir nicht sicher, ob dies in einer Datenvorlage funktioniert, aber hier ist die Bindungssyntax, die ich in einem ListView-Kontextmenü verwende, um das aktuelle Element als Befehlsparameter abzurufen:
CommandParameter = "{Binding RelativeSource = {RelativeSource AncestorType = ContextMenu}, Path = PlacementTarget.SelectedItem, Mode = TwoWay}"
quelle
Ich habe dies als Fehler gegen WPF in .NET 4.0 protokolliert, da das Problem in Beta 2 weiterhin besteht.
https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=504976
quelle
Bei einigen dieser Antworten geht es um die Bindung an den DataContext, um den Befehl selbst abzurufen. Die Frage lautete jedoch, dass der CommandParameter null ist, wenn dies nicht der Fall sein sollte. Das haben wir auch erlebt. Wir haben eine sehr einfache Möglichkeit gefunden, dies in unserem ViewModel zum Laufen zu bringen. Dies gilt speziell für das vom Kunden gemeldete CommandParameter-Nullproblem mit einer Codezeile. Beachten Sie den Dispatcher.BeginInvoke ().
quelle
Es ist ein langer Schuss. Um dies zu debuggen, können Sie
Folgendes versuchen: - Überprüfen Sie das PreviewCanExecute-Ereignis.
- Verwenden Sie snoop / wpf mole, um nach innen zu schauen und den Befehlsparameter zu sehen.
HTH,
quelle
Der BefehlManager.InvalidateRequerySuggested funktioniert auch für mich. Ich glaube, der folgende Link spricht über ein ähnliches Problem, und M $ dev hat die Einschränkung in der aktuellen Version bestätigt, und der BefehlManager.InvalidateRequerySuggested ist die Problemumgehung. http://social.expression.microsoft.com/Forums/en-US/wpf/thread/c45d2272-e8ba-4219-bb41-1e5eaed08a1f/
Was wichtig ist, ist der Zeitpunkt des Aufrufs des commandManager.InvalidateRequerySuggested. Dies sollte aufgerufen werden, nachdem die entsprechende Wertänderung gemeldet wurde.
quelle
Stellen Sie neben Ed Balls Vorschlag zum Festlegen von CommandParameter vor Command sicher, dass Ihre CanExecute- Methode einen Parameter vom Objekttyp enthält .
Ich hoffe, es verhindert, dass jemand so viel Zeit damit verbringt, herauszufinden, wie SelectedItems als CanExecute-Parameter empfangen werden
quelle