2010-08-12 9 views
5

Haftungsausschluss: In dieser Frage Registerkarte, Seite, ein Dialogfeld bedeutet eigentlich die gleiche Sache, sorry. Meine Ausrede: Ich bin mir nicht sicher, wie das Endprodukt aussehen soll - ein paar einzelne Fenster oder alles in einem.Suchen Sie nach einem großartigen Assistenten-Beispiel, das mit WinForms und/oder einem Ratgeberentwurf implementiert wurde

Ich bin auf der Suche nach einem bestehenden, schwer zu pflegen Assistenten mit WinForms gebacken. Ich muss versuchen, das Aussehen und das Gefühl in etwa gleich zu halten, aber ich muss die interne Logik aufräumen. Insgesamt gibt es 5 Dialoge, die nacheinander (nach Anklicken der Schaltfläche Weiter) nacheinander in einer riesigen Methode angezeigt werden. Der Weg, um hin und her zu springen, ist mit ... 5 oder 6 Labels und GOTOs!

Jetzt ist dieser Assistent linear, kein Baum. Von jedem einzelnen Dialog/jeder Seite sollten Sie höchstens zwei andere aufrufen können. Irgendwie kommt mir eine doppelt verknüpfte Liste in den Sinn. Derzeit gibt es 5 * 4 = 20 mögliche Zustandsübergänge, während nur 2*1 + 3*2 = 8 von ihnen gültig sind. Ich muss goto s nicht verwenden. Sie sind normalerweise böse, und in diesem Fall sind sie - es ist schwer, dies bereits zu behaupten ... und ich denke daran, eine weitere, sechste Seite hinzuzufügen. Der Grund, warum goto s da sind, liegt wahrscheinlich daran, dass A) Zeitdruck war, als v. 1.0 gemacht wurde, B) Es war vor 5 Jahren, also die besten Beispiele/Tutorials zu Wizards, die zu dieser Zeit verfügbar waren, waren vielleicht nicht großartig.

Jetzt fragen die meisten Seiten des Assistenten nach Benutzereingaben. Die folgenden Seiten werden gerendert, je nachdem, was der Benutzer eingegeben hat. Wenn der Benutzer auf Seite 3 sagt und entschieden hat, eine Zurück-Taste ganz auf 1 zu setzen, hat er nichts geändert und trifft zweimal auf Weiter, dann sollte sich der Zustand nicht ändern. Änderungen auf Seite x führen jedoch dazu, dass die Inhalte auf den Seiten x + 1 und darüber hinaus ungültig werden. Es gibt jedoch Ausnahmen, da einige oder alle Einstellungen auf einer Seite x von Seite x-1, x-2 usw. abhängen können, aber die Seiten x + 1, x + 2 usw. hängen nicht von diesem x ab etwas x.

Ich hoffe, die Dinge sind soweit klar. Wir versuchen, dem Benutzer zu helfen, indem wir einige Sachen für sie in Verzug setzen. Die Art und Weise, wie Dinge gelagert werden, ist auch nicht großartig. Der Dialog hat Lese-/Schreibeigenschaften, von/zu denen die Daten zu den aktuellen Steuerelementen kopiert werden. Dann gibt es in der Hauptmethode einen "Superspeicher", der Speicher für jede Seite enthält. Wenn also der Benutzer mit der Seite x fertig ist und die nächste trifft, wird der Inhalt zuerst von den Steuerelementen in den Speicher kopiert, der für die Klasse lokal ist, und dann wird dieser Inhalt in das entsprechende Element des Superspeichers gespeichert.

Arrays (von Dialogen/Speichern) und Indizes werden nicht verwendet. Es gibt separate, aber ähnliche "create & populate" -Logik für jedes goto Ziel (Label). Die Dialogobjekte werden weggeworfen, wenn die Seite nicht mehr angezeigt wird (sie werden nicht entfernt, aber jedes Mal, wenn sie angezeigt werden sollen, werden sie neu erstellt und neu bevölkert. Ich glaube nicht, dass dies notwendig ist, da nur ein Single Handle ist erforderlich, und nachdem es gezeigt und geschlossen wurde, glaube ich, dass es wieder in demselben Zustand gezeigt werden kann, ohne die Steuerelemente neu auffüllen zu müssen. Wenn das Verschwenden von Speicher das einzige Problem wäre, würde ich wahrscheinlich Dinge rutschen lassen, aber die Sache ist nicht sehr wartbar, so könnte ich auch reparieren sie alle auf

ich denke:.

  1. Shop Dialoge in einer Sammlung, wie ein Array, aber vorzugsweise DLL, weil ich kann nur Vorwärts 1 oder Rückwärts 1 oder nur eine der beiden Optionen Ich habe aufgelistet (für den ersten und letzten Dialog).
  2. Eigentlich haben meine Tabs/Pages alle eine gemeinsame abstrakte Klasse (da "next", "back", "exit" buttons und ihr Verhalten allen gemeinsam sind).
  3. Jede Registerkarte/Seite/Dialog (das gleiche für den Zweck dieser Frage, sorry für Verwirrung) wird Lese-Eigenschaften für die "Leiter" -Klasse sichtbar haben.Diese Eigenschaften werden von den Werten in den Steuerelementen (der wahren Informationsquelle) abgeleitet, manchmal werden die Eigenschaften diese Werte etwas massieren. Es liegt in der Verantwortung des "Dirigenten", diese zu ergreifen und einzulagern. Wenn der Leiter den Dialog mit einer einzigen Methode füllen möchte (nennen wir es "Seed"). Ich habe hier ein bisschen Schwierigkeiten, da die Parameter für jede Samenmethode unterschiedlich sein werden. Ich möchte in der Lage sein, sowohl das starke Tippen als auch die generischen Dinge zu nutzen. Ich vermute, dass etwas geben sollte. Ich könnte ein Wörterbuch an jede Samenmethode weitergeben, aber das fühlt sich zu pythonisch an, wie das Tippen von Enten. Ich würde es bis zur Laufzeit nicht wissen, wenn ich es vermassele. Auch das Packen und Entpacken des Wörterbuchs passt immer besser zu jeder Seite. Hier kommen Sie rein.
  4. Der globale Speicher kann ein riesiges Wörterbuch sein. Ich kann diszipliniert genug sein, um alle Schlüssel unterschiedlich zu halten, oder ihren Namen je nach Seite mit "p1_" bis "p5_" voranstellen, um sicher zu gehen. Ich bin mir sicher, dass es auch andere Systeme gibt. Auf ein riesiges Wörterbuch zu haben kann am Ende ganz praktisch sein - da ist die Reihenfolge, in der die Benutzereingaben zusammengefügt wurden, nicht von Bedeutung, solange es richtig gemacht wird. Ich kann auch eine Zustandsmaschine haben ... irgendwie. Hier verliere ich mich auch im Design. Wenn ich Dinge in einem Wörterbuch aufbewahre, müsste ich eine Menge bedingter Logik ausführen, wie zum Beispiel: Wenn ich auf Seite 2 bin, und ich eine Änderung mache, dann muss ich normalerweise (es gibt Ausnahmen) alte Vorgaben machen, wenn beliebig für Seiten 3,4,5 ungültig. Je nachdem, wie hässlich es ist, ist es vielleicht nicht viel besser als das aktuelle goto-basierte Design. Ich denke jedoch, dass ich es besser machen kann, da ich meine zustands- oder zustandsübergangsspezifische Logik mit einer Reihe von Delegierten implementieren kann, die in zwei Wörterbüchern gespeichert sind (eines für das nächste, eines für das Zurück), wobei der aktuelle Zustand das ist Schlüssel.

Wie Sie sehen können, gibt es ein paar Herausforderungen. Ich bin jedoch hoffnungsvoll, weil das Denken durch ein gutes Wizard-Design ein Rad ist, das vorher sicherlich erfunden wurde. Vielleicht können Sie eine Open-Source-C#/Mono-App empfehlen, die mit einem linearen, aber nicht trivialen Wizard ausgeliefert wird, damit ich einen Blick auf die Implementierung werfen kann. Heck, vielleicht sogar Java/Swing wird mir wahrscheinlich passen, solange der Wizard in der Natur ähnlich ist. WPF wäre eine weitere Herausforderung für mich, ich möchte nicht 2 Probleme anstelle von 1 haben.

Lassen Sie mich wissen, was Sie sich vorstellen können. Sollte ich nur die Gotos behalten, aber andere Teile so gut wie möglich aufräumen? Fühlen Sie sich frei, Fragen zu stellen. Danke,

-HG

Antwort

0

warum nicht eine Klasse erstellen, die für jede Seite Eigenschaften für jedes Eingabefeld hat und von ihnen zu verfolgen, während der Benutzer klickt sich durch den Assistenten, dass die Art und Weise Sie zurück zu springen, wäre in der Lage und zwischen mehreren Seiten und behält dennoch die Daten, die vom Benutzer unter seiner Sitzung hinzugefügt wurden. Sie könnten sogar die Eingabevalidierung in dieser Seite Modelle haben so, wenn ein Benutzer das nächste Mal klickt Sie so etwas wie

if(!page1model.IsValid) 
{ 
    List<RuleViolation> ruleViolations = page1model.GetRuleViolations(); 
} 

das ist, wenn ich einige Ihrer Probleme verstanden tun könnte.

(für die Seiten zu verfolgen könnten Sie die Seite Modelle die gleiche Schnittstelle implementieren und eine List<IPageModel> oder etwas zu schaffen und die Seite Modelle, um es hinzuzufügen)

0

I für Überbau als Parameter für jede Seite übergeben abstimmen wie es gezeigt wird. Zweiter Parameter wäre die Richtung, mit der wir auf die Seite kamen (Next oder Back). Wenn es zurück ist, zeigen Sie einfach die Daten an, die bereits existieren. Wenn es "Weiter" ist, verwenden Sie Daten von vorherigen Seiten (in der Überstruktur), um die entsprechenden Daten auf der aktuellen Seite anzuzeigen. Die Seite sollte auch die Informationen enthalten, wenn sie zum ersten Mal angezeigt wird. Wenn dies der Fall ist, sollte es Standarddaten bereitstellen, und wenn dies nicht der Fall ist, kann es vorhandene Daten, die dieser Seite zugeordnet sind, wiederverwenden (vorausgesetzt, sie widersprechen nicht den Daten von vorherigen Seiten). State ist nur eine Zahl, die nach jeder Seite inkrementiert oder dekrementiert wird.Hauptcode ist eine kleine while-Schleife, die nur eine Seite für den aktuellen Status anzeigt und einen Status aktualisiert, wenn der Benutzer mit der Seite fertig ist. Wenn Benutzer die letzte Seite mit Next verlässt, wird die Schleife beendet.

Wenn es optionale Seiten gibt, wird der Hauptcode ein wenig kompliziert, weil Sie dann eine Logik benötigen, um zu entscheiden, was die nächste Seite ist (und was alle vorherigen Seiten waren), aber alles andere ist immer noch gleich.

+0

Hm ... so muss jede "Seite" wissen, was alle anderen sind. Ich denke darüber nach, dass ich eine "Seite" aktualisieren kann, nachdem sie mit neuen wichtigen Informationen versehen wurde, und lasse die Seite alle wichtigen Informationen zurückgeben, nach denen gefragt wurde. Dann eine Art von Controller von außen ... –

+0

Die Seite ist in der besten Position, um zu "wissen", was Benutzer auf dieser Seite sehen müssen, und um zu wissen, welche Informationen dazu benötigt werden. Und die Seite benötigt alle Informationen, die sie benötigt :-). Es gibt keinen Grund, Informationen von der Seite zu verstecken, da es nicht notwendig ist, das Ganze übermäßig modular zu machen. Das heißt, es sei denn, Sie möchten "Module" in einem anderen Projekt wiederverwenden. In dieser Lösung können Sie die Seite immer noch aktualisieren, und tatsächlich sollten Sie jedes Mal aktualisieren, wenn die Seite angezeigt wird. Aber, wenn Sie eine Lösung im Sinn haben, dann gehen Sie einfach dafür. – Dialecticus

0

Erstellen Sie eine WinForm, die ein Benutzersteuerelement hostet, das eine Schnittstelle implementiert. Machen Sie alle Seiten Benutzersteuerelemente, die diese Schnittstelle implementieren, und steuern Sie den Ablauf von Ihrem Formular der obersten Ebene. Wenn Sie diesen Ansatz mögen, kann ich etwas alten Code ausgraben, um mehr Details zu geben.

+0

das sieht vielversprechend aus, und ja, ich brauche Code bevor ich mich entscheiden kann. Ich werde nicht abstimmen, auch wenn es nicht klappt. Ich ermutige andere, Hilfsversuche nicht zu bestrafen. –

1

Ich habe einige WinForms-Assistenten Code ist dies die allgemeine Architektur:

Auf dem Haupt Assistenten haben StepList Als List (Of baseWizardStep) .. Liste aller möglichen Schritte, die erstellt wird, wenn der Assistent Hauptform ist.

Auch Nächste Zurück und Abbrechen-Tasten.

Wie bei Alex oben, haben Sie eine Basisklasse (baseWizard) Hauptformular mit einem Panel, das ein Benutzersteuerelement für den aktuellen Schritt hostet. Jeder Wizard-Schritt selbst ist ein Benutzersteuerelement (geerbt von baseWizardStep, das von UserControl erbt) baseWizardStep hat einige eingebaute Ereignisse (ArriveAtStep, ValiateStep, LeaveStep usw.) Auch in einem InitliaseStep-Untersystem, das den Hauptassistenten als Parameter verwendet und speichert ein Verweis auf das Haupt-Assistentenformular in der Eigenschaft. Alle Schritte greifen auf den Hauptassistenten zu, um ihre Daten in Eigenschaften/Objekten/Datasets usw. zu speichern. (Das Speichern der Daten erfolgt normalerweise im LeaveStep-Ereignis.) Die Schritte laden Daten in ihre Steuerelemente beim ArriveAtStep-Ereignis.

Manchmal muss ich die Wizard-Schritte unter vielen Wizards teilen. In diesem Fall implementiert der mainWizard eine Schnittstelle, und der Wizard überträgt seine Haupt-Wizard-Eigenschaft an die Schnittstelle zum Zugriff/zum Speichern von Daten.

Ich habe 2 Ereignisse oberste Kontrolle durch den Assistenten fließen.

Wenn Sie keine dieser 2 Ereignisse behandeln, wird der Assistent von Schritt 1 bis Schritt 1 nacheinander durchlaufen.

Ereignis 1: Wenn Sie mit ShoudlStepOccur umgehen, erlaubt dieses Ereignis dem Entwickler zu entscheiden, ob ein Schritt in der Liste der Schritte auftritt (damit Sie Schritte überspringen können). Dies scheint die meisten Assistenten in einer leicht zu erlernenden Metapher zu bewältigen. Event gibt Ihnen den Schritt und die Diurektion (Forward, zurück)

Event 2: (erweiterte Kontrolle) THe gibt das andere Ereignis auf dem Hauptformular NavigateToStep, Ereignis gibt Ihnen den Schritt, den es gehen wollte, aber Sie kann es ändern, um zu einem völlig anderen Schritt zu navigieren. Wir verwenden dies, um mehrmals in einer Schleife herumzulaufen (z. B. gehen wir in einem Kursassistenten einige Schritte des Assistenten mehrmals für jeden Kurs ein)

Ich habe auch ein Raster, das alle Schritte auflistet mit dem aktuellen Schritt markiert und der Benutzer kann klicken, um den Assistenten zu springen. Ich verwende das StepShouldOccur-Ereignis, um zu wissen, welche Schritte in der Schrittliste jederzeit angezeigt werden können.

One gatchas: -Wenn Sie den Assistenten schließen, müssen Sie die Schritte, die aktuell nicht im Panel enthalten sind und in der StepList herumliegen, entsorgen, da sie sonst ihre Fenstergriffe nicht freigeben.

Nicht sicher, wie viel Sinn das macht, also werde ich es dort lassen.

Vielleicht eines dieser Tage werde ich diesen Wizard-Code auf Code-Projekt oder etwas, ich bin ziemlich glücklich mit, wie es für uns funktioniert.

+0

Danke, können Sie den Code teilen? –

Verwandte Themen