2010-02-10 5 views
6

Mon projet WPF sera organisé comme ceci:découpler les écrans sans cordes magiques

Screens 
    Group1 
     Screen1 
     View.xaml 
     ViewModel.cs 
    Group2 
     Screen2 
     View.xaml 
     ViewModel.cs 

Pour afficher la Screen1 du Screen2 je vais utiliser quelque chose comme ceci: ScreenManager.Show("Group1.Screen1") Cette apparence (réflexion à l'aide) dans le Screens.Group1.Screen1 namespace pour une vue et un ViewModel et les instancie.

Comment puis-je éliminer la chaîne magique sans accouplement Screen1 et Screen2 (je ne veux pas que les classes Screen2 d'utiliser l'espace de noms Screen1). Aussi je voudrais une sorte de découverte d'écran (autocompletion/intellisense)

Ou peut-être d'une certaine manière (automatiser le test) pour vérifier que tous les appels à ScreenManager.Show sont valides.

Mise à jour: je suis venu avec ceci:

public class ScreenNames 
{ 
    public Group1Screens Group1; 

    public class Group1Screens 
    { 
     public ScreenName Screen1; 
    } 
} 

public sealed class ScreenName 
{ 
    private ScreenName() { } 
} 

public class ScreenManager : IScreenManager 
{ 
    public void Show(Expression<Func<ScreenNames, ScreenName>> x) {} 
} 

Utilisation:

screenManager.Show(x=>x.Group1.Screen1); 

Pas idéal mais je suppose violais DRY est encore mieux que les chaînes magiques. Et je peux automatiquement tester (avec réflexion) que tous les appels sont valides.

+0

Pourquoi Screen2 a besoin de savoir à propos de Screen1? Le gestionnaire d'écran n'existe-t-il pas en dehors de chacun des écrans? Et par Intellisense, vous dites que pendant le développement, vous voulez que chaque nom de l'écran apparaisse dans la liste déroulante quand vous commencez à taper ScreenManager.Show()? La liste des écrans est-elle statique ou dynamique (chargée à l'exécution)? – Dave

+0

Finalement, je vais passer un paramètre; ScreenManager sera une propriété de ViewModel; Pour intellisense je pense qu'une liste statique est un must:/ Je pense que je pourrais avoir quelque chose comme ceci: ScreenManager.Show (x => x.Group1.Screen1) d'utilisation –

+0

comme ScreenManager.Show (x => x.Group1.Screen1) voudrait dire que je vais devoir garder et maintenir une liste séparée des écrans, mais je pense qu'il n'y a pas d'autre façon, si je veux IntelliSense –

Répondre

3

Vous n'avez pas besoin de tout ce truc de ScreenManager dans WPF, car le moteur DataTemplate peut s'en occuper pour vous avec un balisage pur.

Vous pouvez simplement numéroter une zone particulière de votre application avec un ContentPresenter et un ensemble de DataTemplates. Liez la zone à une propriété d'un ViewModel 'root' et laissez le ViewModel 'root' implémenter INotifyPropertyChanged afin que WPF sache si vous modifiez le ViewModel dans cette zone.

public class RootViewModel : INotifyPropertyChanged 
{ 
    public object Screen1ViewModel { get; } 

    public object Screen2ViewModel { get; } 
} 

Databind un contrôle ContentPresenter à la propriété Screen1ViewModel utilisant

<ContentControl Content="{Binding Path=Screen1ViewModel}" /> 

et de même pour la suivante. Lorsque vous avez besoin de modifier le contenu de Screen1, vous réattribuez simplement Screen1ViewModel à partir du code et, à cause de l'événement PropertyChanged déclenché, WPF le récupère et lie le nouveau ViewModel à une nouvelle vue.

Les DataTemplates peuvent être aussi simple que cela:

<Window.Resources> 
    <DataTemplate DataType="{x:Type foo:MyViewModel}"> 
     <self:MyControl /> 
    </DataTemplate> 
    <DataTemplate DataType="{x:Type foo:MyOtherViewModel}"> 
     <self:MyOtherControl /> 
    </DataTemplate> 
</Window.Resources> 

Si vous n'êtes pas familier avec elle, this article on MVVM in WPF est une excellente introduction.

+0

Je vous remercie de votre réponse, je ne suis pas sûr complètement compris mais ça ne répond pas à ma question: comment garder le ViewModel découplé? Je ne veux pas le Screen2 ViewModel à utiliser directement les Screen1 –

+0

@ Mark: J'aime cette approche beaucoup, mais je ne suis pas sûr que ce soit ce que Catalin est après. J'ai presque l'impression qu'il veut mettre en place une sorte de navigation et pouvoir passer d'une page à l'autre. Si je comprends votre approche, vous proposez de changer le viewmodel via la liaison de données, ce qui est vraiment cool, mais je ne sais pas si c'est ce qu'il cherche. BTW, pouvez-vous recommander une bonne lecture en ligne qui passe par des exemples où vous voulez changer ViewModels? J'ai toujours une relation 1: 1: 1 entre View: ViewModel: Model et ne change jamais le ViewModel (parce que je ne connais pas ses avantages). – Dave

+0

@Catalin Dicu: Dans ce modèle, Screen1ViewModel sait rien Screen2ViewModel et vice versa. RootViewModel connaît évidemment les deux, mais remarquez que les deux propriétés sont déclarées comme le type System.Object, ce qui signifie qu'elles peuvent vraiment être n'importe quoi. Si vous ne souhaitez pas mettre en place des connaissances sur les types de machine virtuelle dans RootViewModel, vous pouvez utiliser DI pour injecter les machines virtuelles dans RootViewModel. Cela lui donnerait un constructeur comme 'RootViewModel (objet vm1, objet vm2)'. –