2011-12-06 9 views
5

Ho riscontrato un problema con le convalide tra più campi. Ad esempio, ho ottenuto un ViewModel denominato RangeDateViewModel che contiene 2 istanze di una classe denominata DateViewModel, ognuna delle quali rappresenta rispettivamente una data di inizio e una data di fine.Convalida tra più campi in diversi livelli

Quindi il mio aspetto di legame come questo -

<TextBox Text="{Binding StartDate.Date, ValidateOnDataError=True}"> 
<TextBox Text="{Binding EndDate.Date, ValidateOnDataError=True}"> 

La mia classe RangeDateViewModel implementa l'interfaccia IDataErrorInfo. Nel mio piano, il RangeDateViewModel sarebbe verificare che la data di inizio è precedente alla data di fine, applicando la logica di convalida nel IDataErrorInfo [ "propertyName"] funzione come questa -

public string this[string columnName] 
    { 
     get 
     { 
      return ValidationError(); 
     } 
    } 

Il problema è che questo non è mai chiamato, e invece vengono chiamate le proprietà IDataErrorInfo che risiedono in ciascuna classe DateViewModel.

Suppongo che ciò avvenga perché la proprietà associata non è nello stesso livello di RangeDateViewModel, ma all'interno del figlio DateViewModel.

Penso che il mio bisogno sia piuttosto semplice e che ci sia una soluzione facile per questo problema. Ho provato a utilizzare ValidationRules invece di IDataErrorInfo, ma poi avrei problemi a far conoscere al ViewModel lo stato di convalida corrente da ValidationRules.

risposta

1

Provare a utilizzare il seguente approccio:

  1. Creare un DataTemplate per DateViewModel:

    <DataTemplate DataType="{x:Type ViewModels:DateViewModel}"> 
        <TextBox Text="{Binding Date}"> 
    </DataTemplate> 
    
  2. Bind le istanze di questo ViewModel ad un ContentControl e impostare ValidateOnDataError a true su quel legame:

    <ContentControl Content="{Binding StartDate, ValidateOnDataError=True}" /> 
    <ContentControl Content="{Binding EndDate, ValidateOnDataError=True}" /> 
    
  3. In RangeDateViewModel iscriversi a tale PropertyChanged caso di StartDate e EndDate e quando è sollevato, sollevare un evento PropertyChanged con StartDate/EndDate:

    StartDate.PropertyChanged += (s, e) => InvokePropertyChanged("StartDate"); 
    EndDate.PropertyChanged += (s, e) => InvokePropertyChanged("EndDate"); 
    
+0

Grazie Daniel! Ho provato quello che avevi suggerito ma a quanto pare non è ancora abbastanza buono. La proprietà IDataErrorInfo è infatti l'accesso, ma solo all'inizializzazione del modello e non più tardi quando i dati effettivi vengono modificati. Immagino che questo sia dovuto al fatto che StartDate e EndDate sono oggetti complessi che loro stessi non sono stati modificati, ma proprietà al loro interno, e questo non è sufficiente per aumentare il PropertyChanged. Forse dovrei in qualche modo aumentare un evento quando cambiano le proprietà della data interna? – Dror

+0

@Dror: hai ragione. Vedi il terzo passaggio nella risposta aggiornata. –

+0

Grazie ancora Daniel! Questo ha funzionato anche se ho ancora un ultimo problema minore che sto affrontando. Con questa soluzione il risultato sono i due controlli contrassegnati in rosso quando il valore non è valido. Mi piacerebbe che invece di essere marcati in rosso, lo Stackpanel che contiene questi due campi sarà segnato in lettura. Preferibilmente questi due campi non saranno contrassegnati, ma non è obbligatorio. Ho provato ad applicare un DataTrigger su una proprietà bool su RangeDateViewModel denominata "HasErrors" che imposterà "Validation.HasError" su true, ma sfortunatamente è una proprietà di sola lettura. Spero che tu mi possa aiutare anche con questo problema. – Dror

1

ho avuto il problema che public string this[string columnName] non era semplicemente chiamato proprio l'altro settimana.

La soluzione era semplice. Il motore di associazione WPF di binding non ha potuto seguire l'annidamento dei miei ViewModels.

avevo pensato che avevo bisogno di implementare la proprietà del ViewModel che è l'attuale DataContext, ma invece esso deve essere implementato nel ViewModel che associato al controllo.

Esempio:

<TextBox Text="{Binding Path=ProductViewModel.DescriptionViewModel.ProductName, 
            Mode=TwoWay, 
            ValidatesOnDataErrors=True, 
            NotifyOnValidationError=True}" /> 

Qui DescriptionViewModel è la classe che contiene la proprietà bound.IDataErrorInfo deve essere implementato in quella classe (non in ProductViewModel o in un'altra classe nella gerarchia che può contenerlo), quindi tutto funzionerà correttamente.

+0

Ciao Sensei, grazie per il tuo commento. Sono consapevole che IDataErrorInfo sta funzionando bene quando è implementato nella classe inferiore nella gerarchia di binding, ma ho avuto 2 problemi con esso. 1 - Ho avuto due classi coinvolte nella logica di validazione ma ognuna di queste classi di livello inferiore non si conoscono. 2 - Non voglio che solo uno dei campi sia contrassegnato in rosso se il valore non è valido. Voglio che l'intero controllo sia contrassegnato in rosso. – Dror