2009-04-16 5 views
21

J'ai adopté ce qui semble être la méthode standard de validation des zones de texte dans WPF en utilisant l'interface IDataErrorInfo et les styles comme indiqué ci-dessous. Cependant, comment puis-je désactiver le bouton Enregistrer lorsque la page devient invalide? Est-ce que cela est fait en quelque sorte par des déclencheurs?Désactiver le bouton Enregistrer dans WPF si la validation échoue

Default Public ReadOnly Property Item(ByVal propertyName As String) As String Implements IDataErrorInfo.Item 
    Get 
     Dim valid As Boolean = True 
     If propertyName = "IncidentCategory" Then 
      valid = True 
      If Len(IncidentCategory) = 0 Then 
       valid = False 
      End If 
      If Not valid Then 
       Return "Incident category is required" 
      End If 
     End If 

     Return Nothing 

    End Get 
End Property 

<Style TargetType="{x:Type TextBox}"> 
    <Setter Property="Margin" Value="3" /> 
    <Setter Property="Height" Value="23" /> 
    <Setter Property="HorizontalAlignment" Value="Left" /> 
    <Setter Property="Validation.ErrorTemplate"> 
     <Setter.Value> 
      <ControlTemplate> 
       <DockPanel LastChildFill="True"> 
        <Border BorderBrush="Red" BorderThickness="1"> 
         <AdornedElementPlaceholder Name="MyAdorner" /> 
        </Border> 
       </DockPanel> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
    <Style.Triggers> 
     <Trigger Property="Validation.HasError" Value="true"> 
      <Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}" /> 
     </Trigger> 
    </Style.Triggers> 
</Style> 

Répondre

37

Quelques choses:

D'abord, je vous conseille d'utiliser la RoutedCommand ApplicationCommands.Save pour la mise en œuvre de la manipulation du bouton Enregistrer.

Si vous n'avez pas vérifié le modèle de commande WPF, vous pouvez obtenir le scoop here.

<Button Content="Save" Command="Save"> 

Maintenant, pour implémenter la fonctionnalité, vous pouvez ajouter une commande de liaison à la fenêtre/UserControl ou le bouton lui-même:

<Button.CommandBindings> 
     <CommandBinding Command="Save" 
         Executed="Save_Executed" CanExecute="Save_CanExecute"/> 
    </Button.CommandBindings> 
</Button> 

implémentent en code derrière:

private void Save_Executed(object sender, ExecutedRoutedEventArgs e) 
{ 
} 

private void Save_CanExecute(object sender, CanExecuteRoutedEventArgs e) 
{ 
} 

Dans Save_CanExecute, définissez e.CanExecute en fonction de la validité de la liaison dans la zone de texte.

Si vous souhaitez implémenter à l'aide du modèle de conception MVVM (Model-View-ViewModel), consultez la publication de Josh Smith sur CommandSinkBinding. Une dernière remarque: Si vous souhaitez que l'activation/désactivation soit mise à jour dès que la valeur du TextBox est modifiée, définissez UpdateSourceTrigger="PropertyChanged" sur la liaison pour le TextBox.

EDIT: Si vous voulez valider/invalider en fonction de toutes les liaisons dans le contrôle, voici quelques suggestions.

1) Vous implémentez déjà IDataErrorInfo. Essayez d'implémenter la propriété IDataErrorInfo.Error de sorte qu'elle renvoie la chaîne non valide pour toutes les propriétés auxquelles vous liez. Cela ne fonctionnera que si votre contrôle entier est lié à un seul objet de données. Set e.CanExecute = string.IsNullOrEmpty(data.Error);

2) Utiliser la réflexion pour obtenir tous les DependencyProperties statiques publics sur les contrôles pertinents. Puis appelez BindingOperations.GetBindingExpression(relevantControl, DependencyProperty) dans une boucle sur chaque propriété afin que vous puissiez tester la validation.

3) Dans le constructeur, créez manuellement une collection de toutes les propriétés liées sur les contrôles imbriqués. Dans CanExecute, parcourez cette collection et validez chaque combinaison DependencyObject/DepencyProperty en utilisant pour obtenir des expressions, puis en examinant BindingExpression.HasError.

+1

fonctionne très grand merci. Une autre chose cependant. Je peux vérifier les contrôles individuels avec le code suivant Si Validation.GetHasError (myTextbox) Puis e.CanExecute = False Existe-t-il un moyen de vérifier la validité de tous les contrôles plutôt que individuellement? – Mitch

+0

J'ai édité pour inclure quelques idées à ce sujet. –

+2

+1 pour l'utilisation de commande suggérée. Les commandes sont ce qui fait que WPF commence vraiment à cliquer pour moi. –

1

J'ai créé propriété attachée juste pour ceci:

public static class DataErrorInfoHelper 
{ 
    public static object GetDataErrorInfo(ButtonBase obj) 
    { 
     return (object)obj.GetValue(DataErrorInfoProperty); 
    } 

    public static void SetDataErrorInfo(ButtonBase obj, object value) 
    { 
     obj.SetValue(DataErrorInfoProperty, value); 
    } 

    // Using a DependencyProperty as the backing store for DataErrorInfo. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty DataErrorInfoProperty = 
     DependencyProperty.RegisterAttached("DataErrorInfo", typeof(object), typeof(DataErrorInfoHelper), new PropertyMetadata(null, OnDataErrorInfoChanged)); 

    private static void OnDataErrorInfoChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     var button = d as ButtonBase; 

     if (button.Tag == null) 
      button.Tag = new DataErrorInfoContext { Button = button }; 

     var context = button.Tag as DataErrorInfoContext; 

     if(e.OldValue != null) 
     { 
      PropertyChangedEventManager.RemoveHandler(((INotifyPropertyChanged)e.OldValue), context.Handler, string.Empty); 
     } 

     var inotify = e.NewValue as INotifyPropertyChanged; 
     if (inotify != null) 
     { 
      PropertyChangedEventManager.AddHandler(inotify, context.Handler, string.Empty); 
      context.Handler(inotify, new PropertyChangedEventArgs(string.Empty)); 
     } 
    } 

    private class DataErrorInfoContext 
    { 
     public ButtonBase Button { get; set; } 

     public void Handler(object sender, PropertyChangedEventArgs e) 
     { 
      var dei = sender as IDataErrorInfo; 

      foreach (var property in dei.GetType().GetProperties()) 
      { 
       if (!string.IsNullOrEmpty(dei[property.Name])) 
       { 
        Button.IsEnabled = false; 
        return; 
       } 
      } 
      Button.IsEnabled = string.IsNullOrEmpty(dei.Error); 
     } 
    } 
} 

Je l'utilise comme ceci sur mes formes:

<TextBlock Margin="2">e-mail:</TextBlock> 
<TextBox Margin="2" Text="{Binding Email, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"/> 
<!-- other databindings---> 
<Button Margin="2" local:DataErrorInfoHelper.DataErrorInfo="{Binding}" Commands="{Binding SaveCommand}">Create account</Button>