Y at-il une sorte de stratégie systématique pour la conception et la mise en œuvre des interfaces graphiques?

18

J'utilise Visual Studio pour créer une application GUI en C #. La boîte à outils fait office de palette élément astucieuse qui me permet facilement touches de glisser-déposer et d'autres éléments (pour plus de clarté, je vais le dire quand je touche veux dire « contrôle ») sur ma forme, ce qui rend les formes statiques assez facile à faire.

  • La création des boutons en premier lieu demande beaucoup de travail. Lorsque je une forme qui est pas fixe (ie. Touches ou d' autres contrôles sont créés lors de l' exécution d' après ce que l'utilisateur ne) , je ne peux pas utiliser la palette du tout. Au lieu de cela , je dois créer manuellement chaque bouton, en appelant le constructeur de tout méthode que je me sers, et initialize manuellement en spécifiant la hauteur du bouton, la largeur, la position, l' étiquette, gestionnaire d'événements et ainsi de suite. Cela est extrêmement fastidieux parce que je dois deviner tous ces paramètres sans pouvoir cosmétiques pour voir ce que la forme ressemble, et il engendre aussi de nombreuses lignes de code répétitives pour chaque bouton.
  • Gérer les événements dans une application complète est une énorme douleur. La seule façon dont je sais comment faire est de sélectionner un bouton, d'aller dans l'onglet événements dans ses propriétés, de cliquer sur l' OnClickévénement afin qu'il génère l'événement dans le Formcode de, puis de remplir le corps de l'événement. Mais l'utiliser pour de nombreux boutons (par exemple, imaginez le nombre de boutons présents dans une application comme MS Word) pollue le code de my Formavec des dizaines de méthodes de gestionnaire d'événements standard et il est difficile de le maintenir.

En raison de ces derniers, un programme d'interface graphique plus compliqué que Bonjour tout le monde est très pratique pour réellement faire pour moi. Pour être clair, je n'ai aucun problème à gérer la complexité des programmes que j'écris qui ont une interface utilisateur minimale - je sens que je suis capable d'utiliser la POO avec un bon degré de compétence pour structurer soigneusement mon code de logique métier. Mais lors de l'élaboration du graphique, je suis bloqué.

Suis-je en train de manquer quelque chose? Ou tous les développeurs C # acceptent-ils simplement les listes interminables de gestionnaires d'événements répétitifs et le code de création de boutons?


En tant qu'indice (espérons-le utile), je m'attends à ce qu'une bonne réponse parle de:

  • Utilisation de techniques de POO (telles que le modèle d'usine) pour simplifier la création répétée de boutons
  • Combiner de nombreux gestionnaires d'événements en une seule méthode qui vérifie Senderquel bouton l'a appelé et se comporte en conséquence
  • XAML et utilisation de WPF au lieu de Windows Forms

Vous ne devez mentionner toutes ces choses , bien sûr. C'est juste ma meilleure estimation quant au type de réponse que je recherche.

Superbest
la source
Comme je le vois oui usine serait un bon modèle , mais je vois beaucoup plus un modèle Modèle-Vue-Contrôleur avec commandes accrochée à des contrôles au lieu des événements directement. WPF est généralement MVVM mais MVC est bien possible. Nous avons actuellement un énorme logiciel dans WPF utilisant MVC à 90%. Toutes les commandes sont transmises au contrôleur et il gère tout
Franck
Vous ne pouvez pas créer la forme dynamique avec toutes les touches possibles avec un éditeur graphique, et juste faire ce que vous ne dynamiquement pas besoin invisible? Cela ressemble à beaucoup moins de travail.
Hans-Peter Störr
@hstoerr Mais il serait difficile d'obtenir la mise en page au travail. Beaucoup de boutons se chevaucheraient.
Superbe

Réponses:

22

Il existe plusieurs méthodes qui ont évolué au fil des ans pour faire face à ces questions que vous avez mentionnées, je suis d'accord, les deux principales questions que les cadres UI ont dû faire face ces dernières années. Issu d'un milieu WPF, ceux-ci sont abordés comme suit:

Conception déclarative plutôt qu'impérative

Lorsque vous décrivez soigneusement l'écriture de code pour instancier des contrôles et définir leurs propriétés, vous décrivez le modèle impératif de conception d'interface utilisateur. Même avec le concepteur WinForms, vous utilisez simplement un wrapper sur ce modèle - ouvrez le fichier Form1.Designer.cs et vous voyez tout ce code assis là.

Avec WPF et XAML - et des modèles similaires dans d'autres cadres, bien sûr, à partir de HTML - vous êtes censé décrire votre mise en page et laisser le cadre faire le gros du travail de sa mise en œuvre. Vous utilisez des contrôles plus intelligents tels que les panneaux (Grid, ou WrapPanel, etc.) pour décrire la relation entre les éléments de l'interface utilisateur, mais vous vous attendez à ne pas les positionner manuellement. Des concepts flexibles tels que les contrôles répétables - comme le Répéteur d'ASP.NET ou le ItemsControl de WPF - vous aident à créer une interface utilisateur à mise à l'échelle dynamique sans écrire de code répétitif, permettant aux entités de données à croissance dynamique d'être représentées dynamiquement sous forme de contrôles.

Les DataTemplates de WPF vous permettent de définir - encore une fois de manière déclarative - de petites pépites de qualité de l'interface utilisateur et de les faire correspondre à vos données; par exemple, une liste d'objets de données client peut être liée à un ItemsControl et un modèle de données différent invoqué selon qu'il est un employé régulier (utilisez un modèle de ligne de grille standard avec nom et adresse) ou un client principal, alors qu'un modèle différent , avec une image et des boutons faciles d'accès sont affichés. Encore une fois, sans écrire de code dans la fenêtre spécifique , juste des contrôles plus intelligents qui sont conscients de leur contexte de données et vous permettent ainsi de simplement les lier à vos données et de les laisser effectuer des opérations pertinentes.

Liaison de données et séparation des commandes

En touchant la liaison de données, la liaison de données de WPF (et, encore une fois, dans d'autres cadres, comme AngularJS) vous permet de déclarer votre intention en liant un contrôle (par exemple, une zone de texte) à une entité de données (par exemple, le nom du client) et laissez le cadre gère la plomberie. La logique est gérée de la même manière. Au lieu de câbler manuellement les gestionnaires d'événements code-behind à la logique métier basée sur Controller, vous utilisez le mécanisme de liaison de données pour lier le comportement d'un contrôleur (par exemple, la propriété Command d'un bouton) à un objet Command qui représente le nugget d'activité.

Il permet à cette commande d'être partagée entre les fenêtres sans réécrire à chaque fois les gestionnaires d'événements.

Niveau d'abstraction plus élevé

Ces deux solutions à vos deux problèmes représentent un passage à un niveau d'abstraction plus élevé que le paradigme événementiel de Windows Forms que vous trouvez à juste titre fastidieux.

L'idée est que vous ne voulez pas définir, dans le code, chaque propriété unique du contrôle et chaque comportement à partir du clic du bouton et au-delà. Vous voulez que vos contrôles et votre infrastructure sous-jacente fassent plus de travail pour vous et vous permettent de penser dans des concepts plus abstraits de liaison de données (qui existent dans WinForms, mais qui sont loin d'être aussi utiles que dans WPF) et le modèle de commande pour définir les liens entre l'interface utilisateur et un comportement qui ne nécessite pas de passer au métal.

Le modèle MVVM est l'approche de Microsoft à ce paradigme. Je suggère de lire à ce sujet.

Ceci est un exemple de la façon dont grossière ce modèle ressemblerait et comment il vous faire gagner du temps et des lignes de code. Ce ne sera pas compilé, mais c'est un pseudo-WPF. :)

Quelque part dans les ressources de votre application, vous définissez des modèles de données:

<DataTemplate x:DataType="Customer">
   <TextBox Text="{Binding Name}"/> 
</DataTemplate>

<DataTemplate x:DataType="PrimeCustomer">
   <Image Source="GoldStar.png"/>
   <TextBox Text="{Binding Name}"/> 
</DataTemplate>

Maintenant, vous liez votre écran principal à un ViewModel, une classe qui expose vos données. Say, une collection de clients (simple List<Customer>) et un objet de commande (encore une fois, un simple propriété publique de type ICommand). Ce lien permet de lier:

public class CustomersnViewModel
{
     public List<Customer> Customers {get;}
     public ICommand RefreshCustomerListCommand {get;}  
}

et l'interface utilisateur:

<ListBox ItemsSource="{Binding Customers}"/>
<Button Command="{Binding RefreshCustomerListCommand}">Refresh</Button>

Et c'est tout. La syntaxe de la zone de liste saisira la liste des clients hors du ViewModel et tenter de les rendre à l'interface utilisateur. En raison des deux DataTemplates que nous avons définis précédemment, il importera le modèle pertinent (basé sur le DataType, en supposant que PrimeCustomer hérite de Customer) et le placera comme contenu de ListBox. Pas de boucle, pas de génération dynamique de contrôles via le code.

De même, le bouton a une syntaxe préexistante pour lier son comportement à une implémentation ICommand, qui sait vraisemblablement mettre à jour la Customerspropriété - invitant le cadre de liaison de données à mettre à jour automatiquement l'interface utilisateur.

J'ai pris quelques raccourcis ici, bien sûr, mais c'est l'essentiel.

Avner Shahar-Kashtan
la source
5

Tout d'abord, je recommande cette série d'articles: http://codebetter.com/jeremymiller/2007/07/26/the-build-your-own-cab-series-table-of-contents/ - il ne résout pas les problèmes de détail avec votre code de bouton, mais il vous donne une stratégie systématique pour concevoir et implémenter votre propre cadre d'application, en particulier pour les applications GUI, vous montrant comment appliquer MVC, MVP, MVVM à votre application Forms typique.

Aborder votre question au sujet de « faire face aux événements »: si vous avez beaucoup de formes avec des boutons de travail de la même manière, il sera probablement payer pour mettre en œuvre la mission de gestionnaire d'événements dans votre « cadre d'application » par vous - même. Au lieu d'attribuer des événements de clic en utilisant le concepteur graphique, créez une convention de nommage comment un gestionnaire « clic » doit être nommé pour un bouton d'un nom spécifique. Par exemple - pour le bouton MyNiftyButton_ClickHandlerdu bouton MyNiftyButton. Alors il est vraiment difficile d'écrire une (réflexion en utilisant) de routine réutilisable qui itère sur tous les boutons d'un formulaire, vérifiez si le formulaire contient un gestionnaire de clic connexes et affecter le gestionnaire à l'événement automatiquement. Voir ici pour un exemple.

Et si vous voulez juste utiliser un seul gestionnaire d'événements pour un tas de boutons, ce sera également possible. Étant donné que chaque gestionnaire d'événements reçoit l'expéditeur passé, et l'expéditeur est toujours le bouton qui a été pressé, vous pouvez envoyer le code de l'entreprise en utilisant la propriété « Name » de chaque bouton.

Pour la création dynamique de boutons (ou d' autres contrôles): vous pouvez créer des boutons ou autres commandes avec le concepteur sur un formulaire utilisé uniquement dans le but de beeing un modèle . , Vous utilisez ensuite la réflexion (voir ici pour un exemple ) pour cloner le contrôle du modèle et modifier uniquement les propriétés comme emplacement ou la taille. Ce sera probablement beaucoup plus simple que d'initialiser deux ou trois douzaine de propriétés manuellement dans le code.

J'ai écrit ce ci-dessus en ayant à l'esprit Winforms (tout comme vous, je suppose), mais les principes généraux devraient s'appliquer à d'autres environnements de GUI comme WPF avec des capacités similaires.

Doc Brown
la source
Effectivement possible de faire en WPF et c'est encore plus simple. Vous remplissez simplement une liste de commandes (fonctions spécifiques précodées) et définissez la source sur votre fenêtre. Vous créez un joli modèle visuel simple et tout ce dont vous avez besoin pour vous occuper est de remplir une collection avec les commandes que vous voulez et la grande liaison WPF s'occupe du reste pour vous visuellement.
Franck