2009-04-16 9 views
21

Ho adottato quello che sembra essere il modo standard di convalidare le caselle di testo in WPF usando l'interfaccia IDataErrorInfo e gli stili come mostrato di seguito. Tuttavia, come posso disattivare il pulsante Salva quando la pagina non è più valida? Questo è fatto in qualche modo attraverso i trigger?Disabilita pulsante Salva in WPF se la convalida fallisce

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> 

risposta

37

Un paio di cose:

In primo luogo, mi consiglia di utilizzare il RoutedCommand ApplicationCommands.Save per l'attuazione della gestione del pulsante di salvataggio.

Se non si è verificato il modello di comando WPF, è possibile ottenere lo scoop here.

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

Ora, per implementare la funzionalità, è possibile aggiungere un comando vincolante alla finestra/UserControl o al pulsante stesso:

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

implementare queste in codice dietro:

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

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

In Save_CanExecute, impostare e.CanExecute in base alla validità del binding sulla casella di testo.

Se si desidera implementare utilizzando il modello di progettazione MVVM (Model-View-ViewModel), controllare il post di Josh Smith su CommandSinkBinding.

Un'ultima nota: se si desidera che l'abilitazione/disabilitazione venga aggiornata non appena il valore nello TextBox viene modificato, impostare UpdateSourceTrigger="PropertyChanged" sull'associazione per lo TextBox.

MODIFICA: se si desidera convalidare/invalidare in base a tutti i binding nel controllo, ecco alcuni suggerimenti.

1) Si sta già implementando IDataErrorInfo. Provare a implementare la proprietà IDataErrorInfo.Error in modo che restituisca la stringa non valida per tutte le proprietà a cui si sta vincolando. Funzionerà solo se l'intero controllo è vincolante per un singolo oggetto dati. Imposta e.CanExecute = string.IsNullOrEmpty(data.Error);

2) Utilizzare la riflessione per ottenere tutte le Proprietà DependencyProperties pubbliche sui controlli rilevanti. Quindi chiama BindingOperations.GetBindingExpression(relevantControl, DependencyProperty) in un ciclo su ciascuna proprietà in modo da poter verificare la convalida.

3) Nel costruttore, creare manualmente una raccolta di tutte le proprietà associate sui controlli nidificati. In CanExecute, scorrere questa raccolta e convalidare ogni combinazione DependencyObject/DepencyProperty utilizzando per ottenere espressioni e quindi esaminare BindingExpression.HasError.

+1

grandi opere molte grazie. Un'altra cosa però. Posso controllare i singoli controlli con il seguente codice Se Validation.GetHasError (myTextbox) Then e.CanExecute = False Esiste un modo per verificare la validità di tutti i controlli piuttosto che individualmente? – Mitch

+0

Ho modificato per includere alcune idee su questo. –

+2

+1 per l'utilizzo del comando suggerito. I comandi sono ciò che ha spinto WPF a fare clic veramente per me. –

1

Ho creato proprietà associata proprio per questo:

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

sto usando in questo modo le mie forme:

<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> 
Problemi correlati