2012-11-13 11 views
5

Sono nuovo a MVVM, solo recentemente ho iniziato il mio primo progetto seguendo il modello MVVM. Ho un problema nel tentativo di convalidare un ObservableCollection utilizzando l'interfaccia IDataErrorInfo. Il mio ObservableCollection assomiglia a questo:Convalida ObservableCollection associata in ViewModel utilizzando MVVM Pattern

ObservableCollection<Magazine> magazineRepository; 
    public ObservableCollection<Magazine> MagazineRepository 
    { 
     get { return magazineRepository; } 
     set 
     { 
      if (value != null) 
      { 
       bladRepository = value; 
       OnPropertyChanged("MagazineRepository"); 
      } 
     } 
    } 

E il mio XAML in questo modo:

<ListBox x:Name="listMagazineRepository" 
       Grid.ColumnSpan="2" 
       ItemsSource="{Binding}" 
       DataContext="{Binding MagazineRepository}" 
       DisplayMemberPath="Navn" 
       SelectedItem="{Binding Path=SelectedItem}"/> 

     <TextBox x:Name="txtName" Grid.Row="1" Grid.Column="0" 
        Text="{Binding ElementName=listMagazineRepository, Path=SelectedItem.Navn, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" /> 
     <TextBox x:Name="txtPrice" Grid.Row="2" Grid.Column="0" 
        Text="{Binding ElementName=listMagazineRepository, Path=SelectedItem.Pris, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" /> 

E 'solo un semplice listBox contenente oggetti, quando si seleziona un oggetto, le proprietà degli oggetti selezionati vengono visualizzati nelle caselle di testo, e viene quindi associato all'oggetto listbox.

Il mio problema è che quando ho impostato il mio codice in questo modo, l'unico modo per capire come convalidare i miei dati è nel modello di dominio, che in realtà non è una buona pratica, mi piacerebbe convalidare nel ViewModel prima che arrivi lì. Fondamentalmente voglio convalidare ogni proprietà nel MagazineRepository, nel ViewModel, come faresti a fare questo?

PS: Sono nuovo di postare su questo forum (e schede di programmazione in generale) se la mia domanda è carente di informazioni, per favore fatemelo sapere e fornirò i dettagli necessari.

Grazie mille.

+0

Beh, non vedo un ViewModel nel codice che hai postato, ma il modo tipico sarebbe per il tuo ViewModel implementare IDataErrorInfo – Alan

+0

Spiacente, pensavo fosse implicito.La collezione è nel mio ViewModel, che effettivamente implementa IDataErrorInfo, ma quando la casella di testo cambia una proprietà nella raccolta, devo convalidare su quella proprietà e non riesco a capire come farlo esattamente. Quindi ci sono 2 caselle di testo ciascuna associata a una proprietà nella raccolta e quando tale modifica di proprietà devo eseguire la convalida su di essa, facendolo nel modo in cui ho imparato con IDataErrorInfo, posso solo convalidare l'intera raccolta. –

risposta

3

Se ho capito correttamente, vuoi convalidare l'oggetto Magazine. Se questo è il caso, un modo per farlo è quello di avvolgere quella classe in un viewmodel, chiamiamola MagazineVM, che implementa IDataErrorInfo e mantieni aggiornato l'oggetto rivista. Quindi leghi alla vista un elenco di MagazineVM. Come esempio molto semplice:

public class MagazineVM : IDataErrorInfo, INotifyPropertyChanged 
{ 
    private Magazine _magazine; 

    public int FirstMagazineProperty 
    { 
     get { return _magazine.FirstMagazineProperty; } 
     set { _magazine.FirstMagazineProperty = value; RaisePropertyChanged("FirstMagazineProperty"); } 
    } 

    //INotifyPropertyChanged implementation 

    //IDataErrorInfo implementation 
} 
3

In primo luogo, come dice Dtex, è necessario utilizzare una classe MagazineViewModel anziché una classe Magazine. PER ESEMPIO.

public class MagazineViewModel : INotifyPropertyChanged, IDataErrorInfo 
{ 
    private string navn; 
    private string pris; 
    private string error; 

    public string Navn 
    { 
    get { return navn; } 
    set 
    { 
     if (navn != value) 
     { 
     navn = value; 
     RaisePropertyChanged("Navn"); 
     } 
    } 
    } 
    public string Pris 
    { 
    get { return pris; } 
    set 
    { 
     if (pris != value) 
     { 
     pris = value; 
     RaisePropertyChanged("Pris"); 
     } 
    } 
    } 
    public string Error 
    { 
    get { return error; } 
    set 
    { 
     if (error != value) 
     { 
     error = value; 
     RaisePropertyChanged("Error"); 
     } 
    } 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 

    public string this[string columnName] 
    { 
    get 
    { 
     var result = string.Empty; 

     switch (columnName) 
     { 
     case "Pris": 
      if (string.IsNullOrWhiteSpace(Pris)) 
      { 
      result = "Pris is required"; 
      } 
      break; 
     case "Navn": 
      if (string.IsNullOrWhiteSpace(Navn)) 
      { 
      result = "Navn is required"; 
      } 
      break; 
     } 

     return result; 

    } 
    } 

    private void RaisePropertyChanged(string PropertyName) 
    { 
    var e = PropertyChanged; 
    if (e != null) 
    { 
     e(this, new PropertyChangedEventArgs(PropertyName)); 
    } 
    } 

} 

La proprietà importante da notare è "public string this [string columnName]". ColumnName sarà una delle proprietà associate e questo è il punto in cui è possibile eseguire la convalida.

La prossima cosa da considerare è il tuo MainViewModel (Your DataContext). PER ESEMPIO.

public class MainViewModel : INotifyPropertyChanged 
{ 
    //Use a readonly observable collection. If you need to reset it use the .Clear() method 
    private readonly ObservableCollection<MagazineViewModel> magazines = new ObservableCollection<MagazineViewModel>(); 

    private MagazineViewModel selectedItem; 

    //Keep the item being edited separate to the selected item 
    private MagazineViewModel itemToEdit; 

    public ObservableCollection<MagazineViewModel> Magazines { get { return magazines; } } 
    public MagazineViewModel SelectedItem 
    { 
    get { return selectedItem; } 
    set 
    { 
     if (selectedItem != value) 
     { 
     selectedItem = value; 
     RaisePropertyChanged("SelectedItem"); 
     //When the selected item changes. Copy it to the ItemToEdit 
     //This keeps the the copy you are editing separate meaning that invalid data isn't committed back to your original view model 
     //You will have to copy the changes back to your original view model at some stage) 
     ItemToEdit = Copy(SelectedItem); 
     } 
    } 
    } 
    public MagazineViewModel ItemToEdit 
    { 
    get { return itemToEdit; } 
    set 
    { 
     if (itemToEdit != value) 
     { 
     itemToEdit = value; 
     RaisePropertyChanged("ItemToEdit"); 
     } 
    } 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 

    public MainViewModel() 
    { 
    //Ctor... 
    } 

    //Create a copy of a MagazineViewModel 
    private MagazineViewModel Copy(MagazineViewModel ToCopy) 
    { 
    var vm = new MagazineViewModel(); 
    vm.Navn = ToCopy.Navn; 
    vm.Pris = ToCopy.Pris; 
    return vm; 
    } 

    private void RaisePropertyChanged(string PropertyName) 
    { 
    //... 
    } 
} 

L'unica cosa che manca qui è come si copiano le modifiche al modello di vista originale. Potresti farlo prima che l'elemento selezionato cambi (se ItemToEdit è valido) o avere un pulsante Commit che è abilitato solo quando ItemToEdit è valido. Se è possibile consentire ai modelli di visualizzazione originali di entrare in uno stato non valido, non è necessario preoccuparsi della copia.

Infine il codice XAML

Uno stile implicita per mostrare il tooltip errore

<Style 
    TargetType="{x:Type TextBox}"> 
    <Setter 
    Property="ToolTip" 
    Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}" /> 
</Style> 

E i controlli e attacchi

<ListBox 
    ItemsSource="{Binding Magazines}" 
    DisplayMemberPath="Navn" 
    SelectedItem="{Binding Path=SelectedItem, Mode=TwoWay}" /> 
<TextBox 
    Margin="5" 
    x:Name="txtName" 
    Grid.Row="1" 
    Grid.Column="0" 
    Text="{Binding ItemToEdit.Navn, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" /> 
<TextBox 
    Margin="5" 
    x:Name="txtPrice" 
    Grid.Row="2" 
    Grid.Column="0" 
    Text="{Binding ItemToEdit.Pris, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" /> 

caselle di testo si legano a ItemToEdit. ItemToEdit sarà una copia in-sync di SelectedItem.

Problemi correlati