WPF ViewModel Commandes CanExecute problème
Je vais avoir quelques difficultés avec les commandes de Menu Contextuel sur mon Modèle de Vue.
Je suis la mise en œuvre de l'interface ICommand pour chaque commande dans le Modèle de Vue, puis la création d'un ContextMenu dans le cadre des ressources de la Vue (MainWindow), et à l'aide d'un CommandReference de la MVVMToolkit pour accéder au DataContext (ViewModel) Commandes.
Quand je debug de l'application, il apparaît que la méthode CanExecute sur la commande n'est pas appelé, sauf à la création de la fenêtre, donc mon Contexte MenuItems ne sont pas activées ou désactivées je me serais attendu.
J'ai concocté un exemple simple (ci-joint) qui est indicatif de mon application effective et résumées ci-dessous. Toute aide serait grandement appréciée!
C'est le ViewModel
namespace WpfCommandTest
{
public class MainWindowViewModel
{
private List<string> data = new List<string>{ "One", "Two", "Three" };
//This is to simplify this example - normally we would link to
//Domain Model properties
public List<string> TestData
{
get { return data; }
set { data = value; }
}
//Bound Property for listview
public string SelectedItem { get; set; }
//Command to execute
public ICommand DisplayValue { get; private set; }
public MainWindowViewModel()
{
DisplayValue = new DisplayValueCommand(this);
}
}
}
La DisplayValueCommand est telle:
public class DisplayValueCommand : ICommand
{
private MainWindowViewModel viewModel;
public DisplayValueCommand(MainWindowViewModel viewModel)
{
this.viewModel = viewModel;
}
#region ICommand Members
public bool CanExecute(object parameter)
{
if (viewModel.SelectedItem != null)
{
return viewModel.SelectedItem.Length == 3;
}
else return false;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
MessageBox.Show(viewModel.SelectedItem);
}
#endregion
}
Et enfin, la vue est définie dans le code Xaml:
<Window x:Class="WpfCommandTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfCommandTest"
xmlns:mvvmtk="clr-namespace:MVVMToolkit"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<mvvmtk:CommandReference x:Key="showMessageCommandReference" Command="{Binding DisplayValue}" />
<ContextMenu x:Key="listContextMenu">
<MenuItem Header="Show MessageBox" Command="{StaticResource showMessageCommandReference}"/>
</ContextMenu>
</Window.Resources>
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Grid>
<ListBox ItemsSource="{Binding TestData}" ContextMenu="{StaticResource listContextMenu}"
SelectedItem="{Binding SelectedItem}" />
</Grid>
</Window>
OriginalL'auteur LiamV | 2010-04-06
Vous devez vous connecter pour publier un commentaire.
Remplir réponse, voici un "standard" de la mise en œuvre de la
CanExecuteChanged
événement :(à partir de Josh Smith
RelayCommand
classe)Par le chemin, vous devriez probablement envisager d'utiliser
RelayCommand
ouDelegateCommand
: vous allez vite fatigué de la création de nouvelles classes de commandes pour chaque commande de vous Viewmodel...OriginalL'auteur Thomas Levesque
Vous devez garder trace du moment où le statut de CanExecute a changé et le feu de la ICommand.Événement CanExecuteChanged.
Également, vous trouverez peut-être que ça ne fonctionne pas toujours, et dans ces cas, un appel à
CommandManager.InvalidateRequerySuggested()
est nécessaire pour lancer la commande gestionnaire dans le cul.Si vous trouvez que cela prend trop de temps, découvrez la réponse à cette question.
OriginalL'auteur Will
Je vous remercie pour la rapidité des réponses. Cette approche ne fonctionnera que si vous lient les commandes d'un Bouton standard dans la Fenêtre (qui a accès à la Vue du Modèle par l'intermédiaire de son DataContext), par exemple; CanExecute est montré pour être appelé assez fréquemment lors de l'utilisation du Gestionnaire de commandes comme vous le suggérez, sur ICommand la mise en œuvre de classes ou à l'aide de RelayCommand et DelegateCommand.
Cependant, la liaison de la même commandes via un CommandReference dans le ContextMenu
ne pas agir de la même manière.
Pour le même comportement, je dois aussi mentionner le Gestionnaire de Josh Smith RelayCommand, comme dans CommandReference, mais en le faisant, donc, je dois commenter un peu de code à partir de l'intérieur de la OnCommandChanged Méthode. Je ne suis pas entièrement sûr de savoir pourquoi il est là, c'est peut-être prévenir les cas de fuites de mémoire (à l'suppose!)?
OriginalL'auteur LiamV
Que c'est un bug dans CommandReference mise en œuvre. Il résulte de ces deux points:
Le commun des implémentations de RelayCommand et DelegateCommand respecter (1). Le CommandReference mise en œuvre n'est pas respecter (2) lorsqu'il s'abonne à newCommand.CanExecuteChanged. De sorte que le gestionnaire d'objet est collecté et après que CommandReference n'est plus reçoit toutes les notifications qu'il comptait sur.
Le correctif est de maintenir une forte ref pour le gestionnaire dans CommandReference:
Noter que votre approche de transfert d'abonnement pour le Gestionnaire de commandes.RequerySuggested élimine également le bug (il n'y a plus non référencées gestionnaire pour commencer), mais c'est un handicap pour la CommandReference fonctionnalité. La commande à laquelle CommandReference est associé est libre de poser CanExecuteChanged directement (au lieu de compter sur le Gestionnaire de commandes à l'émission d'un requery demande), mais cet événement serait d'ingestion et de ne jamais atteindre la source de la commande lié à la CommandReference. Cela devrait également répondre à votre question de savoir pourquoi CommandReference est mis en œuvre en vous abonnant à newCommand.CanExecuteChanged.
Mise à JOUR: soumis une question sur CodePlex
OriginalL'auteur alexei
Une solution plus facile pour moi, a été de définir le CommandTarget sur le MenuItem.
Plus d'infos: http://www.wpftutorial.net/RoutedCommandsInContextMenu.html
OriginalL'auteur Bendik August Nesbø