MVVM: rigidità delle viste che fanno riferimento ai modelli

1

Un collaboratore e io abbiamo opinioni divergenti su quanto dovremmo aderire rigorosamente ai concetti MVVM. Cerco di seguirlo il più vicino possibile dove prende scorciatoie quasi tutte le possibilità che ottiene. Uno dei miei più grandi fastidi è quando ha punti di vista vincolanti per i modelli. La sua giustificazione è che un viewmodel sarebbe solo una reimplementazione del modello, ma con l'aggiunta della complessità di dover catturare e rilanciare gli eventi (la maggior parte dei nostri modelli e modelli di visualizzazione implementano entrambi INotifyPropertyChanged).

Ad esempio, in un caso, c'è TheViewModel che espone TheModel come una proprietà. Il datacontext di TheView è impostato su TheViewModel . TheView contiene un TreeView who's ItemSource è una raccolta su TheModel . Ogni elemento figlio in TreeView è un tipo di modello che contiene proprietà direttamente associate a TreeView.

Public Class TheViewModel
  Public Property TheModel as ModelObjectA
End Class
Public Class ModelObjectA
  Inherits ModelBase
  Public Property ModelItems as ObservableCollection(Of ModelObjectB)
End Class
Public Class ModelObjectB
  Inherits ModelBase
  Public Property Items as ObservableCollection(Of ModelBase)
  Public Property IsValid as Boolean
  Public Property Is as Boolean
End Class
Public Class ModelBase
  Public Property Name as String
  Public Property Type as String
End Class
<UserControl x:Class="TheView" DataContext="{StaticResource TheViewModel}">
  <TreeView x:Name="ModelTree" ItemsSource="{Binding Path=TheViewModel.ModelItems}" >
    <TreeView.ItemContainerStyle>
      <Style TargetType="TreeViewItem">
        <Style.Triggers>
          <DataTrigger Binding="{Binding IsValid}" Value="False">
            <Setter Property="Background" Value="Red" />
          </DataTrigger>
          <DataTrigger Binding="{Binding IsModified}" Value="True">
            <Setter Property="Foreground" Value="Magenta" />
          </DataTrigger>
        </Style.Triggers>
      </Style>
    </TreeView.ItemContainerStyle>

    <TreeView.Resources>

      <!--ModelObjectB-->
      <HierarchicalDataTemplate DataType="{x:Type mdl:ModelObjectC}" ItemsSource="{Binding Items}">
        <StackPanel>
          <TextBlock Text="{Binding Name}" />
          <TextBlock Text="{Binding Type}" />
        </StackPanel>
      </HierarchicalDataTemplate>

      <!--ModelObjectC-->
      <HierarchicalDataTemplate DataType="{x:Type mdl:ModelObjectC}" ItemsSource="{Binding Items}">
        <StackPanel>
          <TextBlock Text="{Binding Name}" />
          <TextBlock Text="{Binding Type}" />
        </StackPanel>
      </HierarchicalDataTemplate>

      <!--ModelObjectD-->
      <HierarchicalDataTemplate DataType="{x:Type mdl:ModelObjectD}" ItemsSource="{Binding Items}">
        <StackPanel>
          <TextBlock Text="{Binding Name}" />
          <TextBlock Text="{Binding Type}" />
        </StackPanel>
      </HierarchicalDataTemplate>

      <!--Model Base-->
      <DataTemplate DataType="{x:Type mdl:ModelBase}">
        <StackPanel>
          <TextBlock Text="{Binding Name}" />
          <TextBlock Text="{Binding Type}" />
        </StackPanel>
      </DataTemplate>

    </TreeView.Resources>
  </TreeView>

  <ContentPresenter Content="{Binding Path=SelectedItem, ElementName=ModelTree, Converter={StaticResource ModelToViewModelConverter}}" />
</UserControl>

Nota: Questa è una versione semplificata del nostro vero programma, e potrei aver dimenticato alcuni dei dettagli banali (come UserControl che ha due elementi figlio e setter / getter di proprietà).

Non credo che quanto sopra sia corretto. Penso che le collezioni dovrebbero essere esposte alla vista come raccolte di viewmodels (piuttosto che collezioni di modelli). Soprattutto se si considera che ognuno dei modelli ha un modello di visualizzazione corrispondente e, alla fine, l'elemento selezionato viene trasformato in un modello di vista (tramite un convertitore).

Che cosa pensano tutti? Va bene avere la vista vincolante per i modelli in casi come questo, o le collezioni dovrebbero essere viewmodels? Ho bisogno di buoni argomenti per convincere il mio collega che dovrebbe essere cambiato, o per convincermi che l'attuale implementazione è l'approccio giusto.

    
posta AXG1010 15.07.2015 - 16:23
fonte

1 risposta

5

Hai entrambi ragione.

In un mondo teorico perfetto, Views conoscerebbe solo ViewModels e ViewModels avrebbe avvolto i modelli al 100%.

Il problema è che qui nel mondo reale pratico, per farlo è necessario scrivere una serie di codice che:

  1. Non fornisce vantaggi tangibili
  2. Consuma tempo che potrebbe essere usato per fare altre cose
  3. È una potenziale fonte di bug

Abbiamo una grande app MVVM e posso dirti per esperienza che mantenere le collezioni ViewModel in sincronia con le raccolte del modello è una delle aree più soggette a errori di implementazione di MVVM.

La maggior parte dei nostri modelli supporta INotifyPropertyChanged ed esporta ObservableCollections perché possono essere modificati da più posizioni nell'applicazione e tutti i consumatori dei modelli devono essere informati delle modifiche.

A quel punto, le Views possono legarsi a loro semplicemente bene. Avvolgere quella roba con ViewModels diventa solo un mucchio di noioso plumbing che è una fonte di bug. In queste situazioni, è comune per noi esporre l'istanza del modello come una proprietà sul ViewModel.

Un altro approccio che ho usato che funziona molto bene in alcuni scenari è quello di avere ereditare ViewModel dal Modello. Quando ci pensi, il ViewModel è abbastanza spesso semplicemente il modello + una logica di interfaccia utente. Utilizzando l'ereditarietà anziché la composizione, si evita la strana situazione di legare a volte la vista a ViewModel.Foo e altre volte vincolanti a ViewModel.Model.Bar.

    
risposta data 15.07.2015 - 17:10
fonte

Leggi altre domande sui tag