C # WinForms Model-View-Presenter (Passive View)
je développe une application WinForms en C#. J'ai peu d'expérience en programmation D'interface graphique, et je dois apprendre beaucoup à la volée. Ceci étant dit, Voici ce que je construis.
Voir l'interface graphique générale regardez le lien suivant:
GUI http://img227.imageshack.us/img227/1084/program0.jpg
Maintenant, j'ai fait beaucoup de travail déjà, mais dans le très mauvais Autonome, modèle de conception. Je ne savais pas que le projet serait jamais atteindre une certaine taille, et, comme tel, il est temps de faire quelques modification majeure.
j'ai beaucoup étudié les modèles de conception de GUI, et le modèle que je souhaite mettre en œuvre est la vue Passive (voir http://martinfowler.com/eaaDev/PassiveScreen.html). Je cherche de l'aide pour rassembler tout ça.
Contexte:
1) Selon ce que l'utilisateur clique dans la "TreeView", "Liste" dans le coin inférieur gauche affichera une liste d'objets qui peuvent peupler la zone "Editeur". Ces objets peuvent être une boîte de texte ou un DataGridView. L'utilisateur bascule la Liste pour choisir ce qu'il/elle veut voir dans "l'Éditeur"
2) Le modèle est essentiellement un dossier avec des données et des fichiers de configuration. Il y a un programme externe qui tourne sur un répertoire donné, crée des fichiers/dossiers de sortie, etc. Ce programme que je développe est conçu pour gérer/configurer efficacement ces objets dans un
3) le problème avec la façon dont j'ai fait les choses est qu'il est presque impossible de tester, et donc le passage à la MVP-esque Passive View design pattern
j'essaie de faire en sorte que le programme fonctionne indépendamment de la vue. Je n'ai trouvé aucun exemple où une vue interactive plus complexe est utilisée avec le modèle de vue Passive.
Questions:
1) Ai - je besoin de mettre en place une interface/vue de grande taille pour le "look" complet du programme, puis mettre en œuvre des sous-interfaces/sous-vues pour chacun des TreeView, Editor, Logger, etc.? Ou y a-t-il une meilleure "structure" pour faire ça?
2) Quand il s'agit de "passer" des événements de la vue au présentateur/contrôleur (quelle que soit la terminologie que vous souhaitez utiliser W. R. T. Le schéma de conception de la vue Passive), Comment dois-je procéder? Parfois j'ai des propriétés simples qui doivent être mises à jour, et parfois j'ai besoin d'une série d'étapes pour dérouler.
j'aimerais des suggestions et des conseils sur ce sujet. J'ai parcouru Internet, et je n'ai pas trouvé d'exemples adéquats pour m'aider à poursuivre ce projet.
Merci d'avance!
Daniel
2 réponses
voici un exemple simple qui démontre le concept de vues passives en utilisant le modèle de conception MVP. Parce que nous utilisons des vues passives la vue n'a aucune connaissance du présentateur. Le présentateur s'abonnera simplement aux événements publiés par The view et agira en conséquence.
pour commencer nous avons besoin de définir un contrat pour notre vue. Cela est généralement réalisé à l'aide d'une interface, essentiellement, nous voulons avoir un couplage très lâche à notre avis. Nous voulons la capacité de changer pour différentes vues ou événement créer des vues simulées pour le test de l'unité.
voici un contrat qui décrit une vue simple qui sera utilisée pour afficher des informations client
public interface ICustomerManagementView
{
void InitializeCustomers(ICustomer[] customers);
void DisplayCustomer(ICustomer customer);
event EventHandler<EventArgs<ICustomer>> SelectedCustomerChanged;
}
Il expose une méthode unique InitializeCustomers qui sera utilisé pour initialiser notre vue avec des objets de notre modèle.
Nous avons aussi un événement SelectedCustomerChanged qui sera utilisé par notre présentateur pour recevoir notification qu'une action a eu lieu dans la vue.
une Fois que nous avons notre contrat, nous pouvons commencer à gérer ces interactions dans notre présentateur.
public class CustomerManagementPresenter
{
private ICustomer _selectedCustomer;
private readonly ICustomerManagementView _managementView;
private readonly ICustomerRepository _customerRepository;
public CustomerManagementPresenter(ICustomerManagementView managementView, ICustomerRepository customerRepository)
{
_managementView = managementView;
_managementView.SelectedCustomerChanged += this.SelectedCustomerChanged;
_customerRepository = customerRepository;
_managementView.InitializeCustomers(_customerRepository.FetchCustomers());
}
private void SelectedCustomerChanged(object sender, EventArgs<ICustomer> args)
{
// Perform some logic here to update the view
if(_selectedCustomer != args.Value)
{
_selectedCustomer = args.Value;
_managementView.DisplayCustomer(_selectedCustomer);
}
}
}
Dans le présentateur nous pouvons utiliser un autre modèle appelé injection de dépendance pour fournir l'accès à notre vue et à toutes les classes de modèles dont nous avons besoin. Dans cet exemple, j'ai un CustomerRepository qui est responsable de récupérer les détails du client.
dans le constructeur nous avons deux lignes importantes de code, d'abord nous avons souscrit à L'événement SelectedCustomerChanged à notre avis, c'est ici que nous pouvons effectuer des actions associées. Deuxièmement, nous avons appelé InitilaizeCustomers avec les données du dépôt.
a ce point nous n'avons pas encore défini une implémentation concrète pour notre vue, tout ce que nous avons à faire est de créer un objet qui implémente ICustomerManagementView. Par exemple, dans une application Windows Forms, nous pouvons faire ce qui suit
public partial class CustomerManagementView : Form, ICustomerManagementView
{
public CustomerManagementView()
{
this.InitializeComponents();
}
public void InitializeCustomers(ICustomer[] customers)
{
// Populate the tree view with customer details
}
public void DisplayCustomer(ICustomer customer)
{
// Display the customer...
}
// Event handler that responds to node selection
private void CustomerTreeViewAfterSelect(object sender, TreeViewEventArgs e)
{
var customer = e.Node.Tag as ICustomer;
if(customer != null)
{
this.OnSelectedCustomerChanged(new EventArgs<ICustomer>(customer));
}
}
// Protected method so that we can raise our event
protected virtual void OnSelectedCustomerChanged(EventArgs<ICustomer> args)
{
var eventHandler = this.SelectedCustomerChanged;
if(eventHandler != null)
{
eventHandler.Invoke(this, args);
}
}
// Our view will raise an event each time the selected customer changes
public event EventHandler<EventArgs<ICustomer>> SelectedCustomerChanged;
}
Si nous voulions pour tester notre logique de présentation, nous pourrions nous moquer de notre point de vue et effectuer quelques assertions.
EDIT : Inclus événement personnalisé args
public class EventArgs<T> : EventArgs
{
private readonly T _value;
public EventArgs(T value)
{
_value = value;
}
public T Value
{
get { return _value; }
}
}
je les décomposerais en vues séparées avec leurs propres cadeaux, et utiliserais un présentateur / point de vue" de contrôle " pour gérer la délégation de message entre eux tous. Non seulement cette aide testera-t-elle la testabilité, mais elle gardera vos contrôles remplir SRP, aussi.
donc dans votre cas vous pouvez avoir un IFormManager que votre fenêtre principale implémentera, et puis un IFileManager, ILoggerWindow etc. etc.
bien que cela puisse être un peu exagéré à utiliser, je suggérerais que vous avez un regardez Smart Client Software Factory (de L'équipe de Microsoft Patterns and Practices) - il n'est plus activement développé, mais il a une bonne mise en œuvre de MVP et fait ce genre de truc de composition de vue assez bien, donc pourrait vous donner quelques bonnes idées.