2014-04-16 3 views
0

Dans mon ViewModel, j'ai implémenté l'interface IDataErrorInfo (avec INotifyPropertyChanged). La validation d'entrée fonctionne comme prévu, je n'ai aucun problème là-bas.Liaison de commandes WPF avec validation d'entrée - comment activer le bouton "Enregistrer" uniquement si toutes les entrées sont valides

Je cette propriété dans le cadre de IDataErrorInfo public string Error { get { return this[null]; } } À ma connaissance, Error devrait être vide si toutes les entrées validées passent la validation, donc je passe ce que ma méthode de CanExecute

return !string.IsNullOrEmpty(Error); 

Mais, mon bouton « Enregistrer » n'est jamais activé. Mon point de vue est que CanExecuteChanged ne devient jamais truite. Si c'est vrai, où et comment devrais-je le déclencher?


Ceci est ma classe RelayCommand. J'ai essayé d'autres moyens de mise en œuvre, mais les résultats étaient les mêmes. Je pense que cela fonctionne, car le bouton "Enregistrer" est activé si je ne passe pas la méthode CanExecute au constructeur.

public class RelayCommand : ICommand 
{ 
    private readonly Action execute; 
    private readonly Func<bool> canExecute; 

    public RelayCommand(Action execute, Func<bool> canExecute = null) 
    { 
     this.execute = execute; 
     this.canExecute = canExecute; 
    } 

    public bool CanExecute(object parameter) 
    { 
     return canExecute == null || canExecute();  
    } 

    public event EventHandler CanExecuteChanged 
    { 
     add { CommandManager.RequerySuggested += value; } 
     remove { CommandManager.RequerySuggested -= value; } 
    } 

    public void Execute(object parameter) { execute(); } 
} 

Le bouton "Enregistrer":

<Button Content="Save" Command="{Binding InsertCommand}"/> 

InsertCommand:

public RelayCommand InsertCommand { get; internal set; } 

Dans le constructeur ViewModel:

InsertCommand = new RelayCommand(ExecuteInsert, CanExecuteInsert); 

CanExecute:

bool CanExecuteInsert() 
{ 
    return !string.IsNullOrEmpty(Error); 
} 
+0

Afficher InsertCommand comment il a été initialisé et son CanExecute –

+0

@lll Ajouté à la fin. –

Répondre

4

Vous n'avez pas vraiment ajouté suffisamment de code pour que nous puissions vous dire exactement quel est votre problème. Cependant, vous prenez la bonne approche. Je l'utilise aussi l'interface IDataErrorInfo, mais j'ai ajouté quelques propriétés supplémentaires dans ma classe de base qui l'implémente:

public string Error // actual IDataErrorInfo Member 
{ 
    get 
    { 
     if (!HasError) return string.Empty; 
     StringBuilder errors = new StringBuilder(); 
     foreach (string error in Errors) errors.AppendUniqueOnNewLineIfNotEmpty(error); 
     return errors.ToString(); 
    } 
} 

public virtual ObservableCollection<string> Errors 
{ 
    get { return errors; } 
} 

public virtual bool HasError 
{ 
    get { return Errors != null && Errors.Count > 0; } 
} 

La collection Errors me permet simplement de maintenir plusieurs erreurs simultanément et HasError me dit s'il y a des erreurs ou ne pas. La collection Errors est rempli à l'aide du IDataErrorInfo indexeur dans chaque type de données:

public override ObservableCollection<string> Errors 
{ 
    get 
    { 
     errors = new ObservableCollection<string>(); 
     errors.AddUniqueIfNotEmpty(this["Title"]); 
     errors.AddUniqueIfNotEmpty(this["Artist"]); 
     ... 
     errors.AddUniqueIfNotEmpty(this["DealerPrice"]); 
     return errors; 
    } 
} 

Donc, pour répondre à votre question réelle, je serais gérer la fonctionnalité CanExecute du Save Command comme ceci:

public override ICommand Save 
    { 
     get { return new ActionCommand(action => SaveCommand(), canExecute => 
      CanSave(DigitalServiceProviderPriceTier)); } 
    } 
... 
    private bool CanSave(DigitalServiceProviderPriceTier digitalServiceProviderPriceTier) 
    { 
     return digitalServiceProviderPriceTier != null && 
      digitalServiceProviderPriceTier.HasChanges && 
      !digitalServiceProviderPriceTier.HasError; // <-- Important part 
    } 

Ainsi, il semble comme si vous le faites en presque de la même manière - mes propriétés supplémentaires sont bien sûr facultatives. Si votre propriété Error n'est jamais vide, je dirais que que est votre problème. Commencez par le déboguer et voir quelle valeur il a réellement ... peut-être y a-t-il toujours une erreur qui ne devrait pas l'être?

Ahhhh ... J'ai juste noté votre code de propriété Error ...que est votre problème:

public string Error { get { return this[null]; } } 

Vous appelez l'indexeur avec une valeur de null, de sorte que le code dans votre indexeur revient est en fait ce que votre Error valeur de la propriété sera. Un indexeur doit aussi retourner une chaîne vide s'il n'y a pas d'erreurs de validation:

public override string this[string propertyName] 
{ 
    get 
    { 
     string error = string.Empty; 
     if (propertyName == "SomePropertyName" && SomePropertyName.IsNullOrEmpty()) 
      error = "You must enter some property."; 
     if (propertyName == "OtherPropertyName" && OtherPropertyName.Length != 3) 
      error = "The OtherPropertyName must be 3 characters long."; 
     ... 
     return error; 
    } 
} 

Puis dans votre propriété Error, vous devez appeler les noms réels de propriété que vous souhaitez valider comme je l'ai fait dans ma Errors propriété et pasnull. Ainsi, dans l'exemple ci-dessus, vous pouvez appeler quelque chose comme ça dans votre propriété Error:

string error = this["SomePropertyName"]; 
if (error == string.Empty) error = this["OtherPropertyName"]; 
return error; 

Encore une fois, je l'ai écrit trop d'informations ... J'espère juste pour vous tout sens et vous êtes ne va pas revenir avec des dizaines de nouvelles questions. J'espère que c'est assez clair.

+0

J'ai commencé récemment avec WPF, et dans tous les exemples de IDataErrorInfo, la propriété Error a été implémentée comme ça. Cela fonctionne pour la validation, donc je ne pensais pas que c'était un problème. Je vais essayer vos suggestions quand je rentre à la maison. –

+0

Ça a marché, merci! J'ai aussi eu un défaut de logique ici 'return! String.IsNullOrEmpty (Error);' Il devrait aller sans la négation, bien sûr. –

Questions connexes