2012-03-21 12 views
11

Sono ancora in difficoltà con la convalida in WPF.Binding di convalida al primo caricamento

Ho una regola di convalida personalizzata che richiede che il testo venga visualizzato in una casella di testo, cioè applica un vincolo di campo obbligatorio.

<TextBox local:Masking.Mask="^[a-zA-Z0-9]*$" x:Name="CameraIdCodeTextBox" Grid.Row="1" Grid.Column="1"> 
    <Binding Path="CameraIdCode" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged" NotifyOnValidationError="True" ValidatesOnExceptions="True"> 
    <Binding.ValidationRules> 
     <localValidation:RequiredFieldRule /> 
    </Binding.ValidationRules> 
    </Binding> 
</TextBox> 

Il problema è che quando i primi carichi di finestra, non c'è nessun testo nel controllo TextBox (come ci si aspetterebbe). Ma la proprietà Text viene associata a una proprietà sul ViewModel e, come tale, la regola di convalida viene attivata, a indicare che c'è un problema con la finestra - prima che l'utente abbia persino avuto l'opportunità di violare una regola aziendale.

Si tratta di un problema risolto in precedenza? Non posso essere stato il primo a sperimentarlo. Sono sicuro che è una trappola per i giovani giocatori.

+0

Puoi provare ... UpdateSourceTrigger = "LostFocus" –

+0

Si può essere in grado di creare un gruppo di convalida, e solo gli consentono, ove utente effettua prima una modifica a qualche campo. –

+0

@AngelWPF L'ho provato. Si convalida ancora sul bind iniziale quando viene caricata la finestra. – onefootswill

risposta

0

E 'stato un po' e ho dovuto aggiornato a questa domanda. Ho risolto utilizzando una classe che ho trovato in un libro di Ian WPF Griffths (un libro di O'Reilly):

public static class Validator 
{ 
    /// <summary> 
    /// This method forces WPF to validate the child controls of the control passed in as a parameter. 
    /// </summary> 
    /// <param name="parent">Type: DependencyObject. The control which is the descendent root control to validate.</param> 
    /// <returns>Type: bool. The validation result</returns> 
    public static bool IsValid(DependencyObject parent) 
    { 
     // Validate all the bindings on the parent 
     bool valid = true; 
     LocalValueEnumerator localValues = parent.GetLocalValueEnumerator(); 
     while (localValues.MoveNext()) 
     { 
      LocalValueEntry entry = localValues.Current; 
      if (BindingOperations.IsDataBound(parent, entry.Property)) 
      { 
       Binding binding = BindingOperations.GetBinding(parent, entry.Property); 
       foreach (ValidationRule rule in binding.ValidationRules) 
       { 
        ValidationResult result = rule.Validate(parent.GetValue(entry.Property), null); 
        if (!result.IsValid) 
        { 
         BindingExpression expression = BindingOperations.GetBindingExpression(parent, entry.Property); 
         Validation.MarkInvalid(expression, new ValidationError(rule, expression, result.ErrorContent, null)); 
         valid = false; 
        } 
       } 
      } 
     } 

     // Validate all the bindings on the children 
     for (int i = 0; i != VisualTreeHelper.GetChildrenCount(parent); ++i) 
     { 
      DependencyObject child = VisualTreeHelper.GetChild(parent, i); 
      if (!IsValid(child)) 
      { 
       valid = false; 
      } 
     } 

     return valid; 
    } 

} 

Poi, la vista, ho avuto la seguente configurazione:

<TextBox local:Masking.Mask="^[0-9]*$" IsEnabled="{Binding Path=LocationNumberEnabled}" Grid.Row="1" Grid.Column="3"> 
    <Binding Path="LocationNumber" Mode="TwoWay" UpdateSourceTrigger="LostFocus" NotifyOnValidationError="True" ValidatesOnExceptions="True"> 
     <Binding.ValidationRules> 
      <localValidation:PositiveNumberRule /> 
      <localValidation:RequiredFieldRule /> 
     </Binding.ValidationRules> 
    </Binding>      
</TextBox> 

Ha funzionato come un fascino! Ho appena chiamato il metodo IsValid ogni volta che volevo convalidare manualmente, ad es. su un pulsante premere.

0

Ci sono un paio di modelli per questo. Di solito implemento sulla classe/Modello l'interfaccia ISupportInitialize, che richiede la creazione di BeginInit() e EndInit() in questi metodi, ho semplicemente impostato un valore booleano privato _isInitializing su true o false.

Nel modello vista o dove/quando si crea/popolare il modello/classe di avvolgerlo con inizio e init fine:

var o = new SampleObject(); 
o.BeginInit() 
o.StartDate = DateTime.Now; //just some sample property... 
o.EndInit(); 

Così poi a seconda di come il vostro ValidationRule viene richiamato, si potrebbe verificare lo stato di il tuo _isInitializing per vedere se è necessario convalidare.

Ultimamente sto usando attributo validatori che il fuoco su PropertyChanged così si potrebbe fare qualcosa di simile:

[CustomValidator("ValidateStartDate")] 
public DateTime StartDate 
{ get ... 
{ 
    set 
    { 
     if(_startDate == value) return; 
     _startDate = value; 
     if(_isInitializing) return; 
     RaisePropertyChange(() => StartDate); 
     }.. 

Se non si vuole perdere tempo con ISupportInitialize, poi passano tutti i valori che avete bisogno in le tue proprietà durante la costruzione non la proprietà. Vincolante sarà interrogare i getter su di voi proprietà prima volta e avranno i loro valori, e dopo tutto ciò passerà attraverso la proprietà setter e ottenere convalidato:

//c-tor 
public MyObject(DateTime start) 
{ 
    _startDate = start; 
} 
Problemi correlati