2011-06-22 15 views
4

J'ai un TreeView que j'ai (enfin) pu remplir à partir d'une base de données en utilisant la liaison de données.WPF: TreeView dans MVVM

Il y a 2 objets qui vivent dans l'arbre:

  • FavoriteFolder - un objet qui peut avoir des enfants: soit des dossiers ou des rapports.
  • FavoriteReport - un objet qui ne peut pas avoir d'enfants: lorsqu'un utilisateur clique sur cet élément, il va générer un rapport.

Actuellement, j'ai une configuration de type Model-View (je pense), et je voudrais changer à MVVM pour que je puisse faire des choses avec les TreeView -articles plutôt que de les afficher simplement.

J'ai regardé de nombreux exemples, mais je suis encore nouveau pour MVVM et WPF en général, de sorte que toute orientation pour mon exemple particulier serait très apprécié

Mes deux classes qui existent dans le TreeView sont:

éléments du dossier:

public class FavoriteFolder 
{ 
    private string _connectionString = new ServerInfo().ConnectionString; 
    private string _folderID; 
    private string _parentID; 
    private string _folderTitle; 

    private ObservableCollection<FavoriteFolder> _folders; 
    private ObservableCollection<FavoriteReport> _reports; 
    private ObservableCollection<object> _children; 

    public FavoriteFolder() 
    { 

    } 

    public ObservableCollection<object> Children 
    { 
     get 
     { 
      _getChildren(); 
      return _children; 
     } 
    } 

    public string FolderID 
    { 
     get { return _folderID; } 
     set { _folderID = value; } 
    } 

    public string ParentID 
    { 
     get { return _parentID; } 
     set { _parentID = value; } 
    } 

    public string FolderTitle 
    { 
     get { return _folderTitle; } 
     set { _folderTitle = value; } 
    } 

    private void _getChildren() 
    { 
     _folders = new ObservableCollection<FavoriteFolder>(); 
     _reports = new ObservableCollection<FavoriteReport>(); 

     using (SqlConnection cnn = new SqlConnection(_connectionString)) 
     { 
      cnn.Open(); 
      string sql = "SELECT * FROM tbl_report_folders where fdr_parent_id =" + _folderID; 
      SqlCommand cmd = new SqlCommand(sql, cnn); 

      SqlDataReader reader = cmd.ExecuteReader(); 

      while (reader.Read()) 
      { 
       FavoriteFolder folder = new FavoriteFolder(); 

       folder.FolderID = reader["fdr_folder_id"].ToString(); 
       folder.FolderTitle = reader["fdr_folder_name"].ToString(); 
       folder.ParentID = reader["fdr_parent_id"].ToString(); 

       _folders.Add(folder); 
      } 

      reader.Close(); 

      sql = "SELECT * FROM tbl_reports where rpt_folder_id =" + _folderID; 
      cmd = new SqlCommand(sql, cnn); 

      reader = cmd.ExecuteReader(); 

      while (reader.Read()) 
      { 
       FavoriteReport report = new FavoriteReport(); 

       report.ReportID = reader["rpt_report_id"].ToString(); 
       report.ReportTitle = reader["rpt_report_name"].ToString(); 
       report.ParentID = reader["rpt_folder_id"].ToString(); 

       _reports.Add(report); 
      } 
     } 

     //add the children to the collection 
     foreach (var folder in this._folders) 
      _children.Add(folder); 

     foreach (var report in this._reports) 
      _children.Add(report); 
    } 
} 

éléments de rapport:

public class FavoriteReport 
{ 
    private string _reportID; 
    private string _parentID; 
    private string _reportTitle; 

    public FavoriteReport() 
    { 

    } 

    public string ReportID 
    { 
     get { return _reportID; } 
     set { _reportID = value; } 
    } 

    public string ParentID 
    { 
     get { return _parentID; } 
     set { _parentID = value; } 
    } 

    public string ReportTitle 
    { 
     get { return _reportTitle; } 
     set { _reportTitle = value; } 
    } 
} 

Et les MainWindow.xaml.cs -

public partial class MainWindow : Window 
{ 
    public MainWindow() 
    { 
     InitializeComponent(); 

     ObservableCollection<object> items = new ObservableCollection<object>(); 

     FavoriteFolder fdr = new FavoriteFolder(); 

     fdr.FolderID = "0"; 

     items = fdr.Children; 

     this.DataContext = items; 
    } 
} 
+0

Je ne suis pas non plus un expert en conception, mais cela n'aurait-il pas beaucoup de sens que votre logique d'accès aux données soit séparée de votre modèle? –

+0

Je suis d'accord, pensez-vous de créer une classe séparée qui s'interface avec la base de données qui avait des méthodes pour renvoyer des éléments enfants ou quelque chose? – ChandlerPelhams

Répondre

3

Ma première recommandation serait de déposer dans une boîte à outils MVVM, car il est plus facile que d'avoir à tout faire vous-même (par exemple implémenter l'interface INotifyPropertyChanged). J'utilise MVVM Light by Laurent Bungion.

En supposant que vous utilisez MVVM Light (vous pouvez extrapoler si vous utilisez une autre boîte à outils) ...

Alors, pour convertir en vous MVVM besoin de faire quelques petites choses.

  1. Créer défini Models. J'utilise généralement POCO, en définissant simplement les propriétés d'un modèle. Cela signifie que vous devez extraire votre couche d'accès aux données (plus de détails ci-dessous). Créez votre ViewModel C'est là que vous avez vos propriétés auxquelles vous vous engagez dans votre veiw. Votre ObservableCollections serait assis ici. Initialiser une instance des classes que vous avez créées irait ici. Les appels à votre couche DAL iraient ici. (au lieu de dans le constructeur de votre vue, par exemple).

    Ajoutez ce ViewModel à votre ViewModelLocator (j'utilise l'extrait de code mvvmlocatorproperty fourni avec MVVM Light). Liez votre View au ViewModel en utilisant le Locator. Dans votre UserControl, vous mettiez quelque chose comme ça dans la déclaration:

    DataContext="{Binding YourViewModel, Source={StaticResource Locator}}" 
    
  2. je suivais les conseils de Brennan Vincent. Je crée généralement une interface de service (note: pas une classe, mais une interface) qui définit les méthodes que mon DAL (couche d'accès aux données) aura.Je fais ceci pour permettre Blendability, alias des données de temps de conception. Si vous n'êtes pas familier avec les interfaces, peut-être qu'une simple classe DAL est une bonne façon de commencer. Ensuite, j'utilise l'injection de dépendance (DI) pour injecter une instance de mon service DAL. DI est assez simple - dans le ViewModelLocator vous devez ajouter (instancier) votre classe de service DAL & passer à travers votre appel vm = New ViewModel(MyDALService dalService). Vous devez également, de toute évidence, accepter une référence MyDALService dans votre constructeur ViewModel. Voici un exemple de mon constructeur EquipmentViewModel sur un projet que j'ai travaillé sur:

    public EquipmentViewModel(Services.IEquipmentService equipmentService) 
    { 
        EquipmentService = equipmentService; 
        LoadData(); 
    } 
    

Ce ViewModel accepte un paramètre de type IEquipmentService (ce qui est mon interface). Dans la méthode LoadData, j'appelle la méthode EquipmentService.GetEquipment() de ma couche d'accès aux données qui touche ma couche de base de données.

Toutes les questions me le font savoir. MVVM peut être une douleur, mais je suis très content d'y être resté. Bonne chance. :)

+0

Une chose que je ne comprends pas à propos de MVVM; le modèle. Disons que j'ai un modèle avec une collection de 'Foo'. Maintenant, je veux lier la collection à mon contrôle d'interface utilisateur, et alors, je dois créer une autre collection dans mon ViewModel et la garder en phase avec la collection du modèle? J'espère qu'il me manque quelque chose, parce que cela me semble ridicule. –

+0

Pourquoi auriez-vous un modèle avec juste une collection de 'Foo'? Créez un modèle appelé 'Foo' (alias une seule instance de Foo, pas une collection) et dans votre modèle de vue, créez une nouvelle collection de votre classe' Foo'. –

+0

Évidemment c'était un exemple ... Ok, j'ai un modèle avec un id, une collection de Foo, et un nom qui est une chaîne. Est ce que ça aide? Votre exemple est sémantiquement différent de celui que j'ai demandé de toute façon. Ne pouvez-vous pas imaginer un modèle avec un type de collection? Cela s'applique à tout type vraiment. Je ne vois pas le but d'avoir un modèle, puis un ViewModel qui imite essentiellement toutes ces propriétés et fournit des notifications PropertyChanged pour la vue. Comme je l'ai dit, il me manque quelque chose, parce que ce serait juste ... bête. –

1

Josh Smith a donné la description définitive de comment utiliser MVVM to simplify the TreeView Je ne peux pas ajouter grand-chose.

+0

J'ai lu cet article (plusieurs fois), mais comme j'apprends encore certains aspects n'ont pas de sens pour moi ... Selon sa solution, je devrais créer un modèle de vue treeviewerem que mon modèle de vue de dossier et de rapport les classes dériveraient de? En outre, je ne comprends pas très bien ce que le "DummyChild" dans la classe TreeViewItemViewModel Josh utilisé était pour – ChandlerPelhams

+0

Le dummychild était pour le chargement paresseux. Les enfants d'un treeviewitem sont seulement chargés sur demande dans ce scénario. Bea Stollnitz (née Costa) discute de la virtualisation des données sur son blog. –