2009-04-16 29 views
21

He adoptado lo que parece ser la forma estándar de validar cuadros de texto en WPF utilizando la interfaz IDataErrorInfo y los estilos que se muestran a continuación. Sin embargo, ¿cómo puedo desactivar el botón Guardar cuando la página no es válida? ¿Esto se hace de alguna manera a través de los factores desencadenantes?Deshabilitar el botón Guardar en WPF si la validación falla

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> 

Respuesta

37

Un par de cosas:

En primer lugar, yo recomendaría usar el RoutedCommand ApplicationCommands.Save para implementar el manejo del botón de guardar.

Si no ha comprobado el modelo de comando de WPF, puede obtener la primicia here.

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

Ahora, para implementar la funcionalidad, se puede agregar un enlace a la ventana/control de usuario o para el propio botón de comando:

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

implementar estas en el código detrás:

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

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

En Save_CanExecute, establezca e.CanExecute en función de la validez del enlace en el cuadro de texto.

Si desea implementar utilizando el patrón de diseño MVVM (Model-View-ViewModel), consulte la publicación de Josh Smith en CommandSinkBinding.

Una nota final: Si desea que la activación/desactivación se actualice tan pronto como se cambie el valor en TextBox, configure UpdateSourceTrigger="PropertyChanged" en el enlace para TextBox.

EDITAR: Si desea validar/invalidar en función de todos los enlaces en el control, aquí hay algunas sugerencias.

1) Ya está implementando IDataErrorInfo. Intente implementar la propiedad IDataErrorInfo.Error de modo que devuelva la cadena que no es válida para todas las propiedades a las que está vinculando. Esto solo funcionará si todo su control se vincula a un solo objeto de datos. Establecer e.CanExecute = string.IsNullOrEmpty(data.Error);

2) Utilice la reflexión para obtener todas las DependencyProperties públicas estáticas en los controles relevantes. Luego llame al BindingOperations.GetBindingExpression(relevantControl, DependencyProperty) en un bucle en cada propiedad para que pueda probar la validación.

3) En el constructor, cree manualmente una colección de todas las propiedades enlazadas en los controles anidados. En CanExecute, itere a través de esta colección y valide cada combinación de DependencyObject/DepencyProperty utilizando para obtener expresiones y luego examine BindingExpression.HasError.

+1

funciona muy bien muchas gracias. Otra cosa sin embargo. Puedo verificar los controles individuales con el siguiente código If Validation.GetHasError (myTextbox) Then e.CanExecute = False ¿Hay alguna manera de verificar la validez de todos los controles en lugar de verificarlos individualmente? – Mitch

+0

Lo edité para incluir algunas ideas sobre esto. –

+2

+1 para el uso del comando sugerido. Los comandos son los que hacen que WPF comience a hacer clic genuinamente en mí. –

1

He creado propiedad adjunta sólo para esto:

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); 
     } 
    } 
} 

lo estoy usando como este en mis formas:

<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> 
Cuestiones relacionadas