Architekturfrage

Darkas

Erfahrenes Mitglied
HI!

Ich hab ein kleines Problem mit meiner Architektur: ich möchte einen Wizard für neue Projekte einbauen. Dieser besteht folglich aus mehreren Fenstern (zwingend, es geht nicht über verschiedene Panel oder ähnliches). Um das ganze aber sauber aufzubauen würde ich gerne eine Klasse bereitstellen, die den Wizard startet, und anschließend wenn er durchgelaufen ist, das ergebniss wiedergibt.
Dazu möchte ich die Fenster nur über diese Klasse erreichbar machen. Und genau da ist das problem. Ich kann die Fenster schlecht in die Klasse selbst implementieren, da ich sonst schwierigkeiten mit dem Designer hätte, und es ist auch nicht möglich, die Klassen öffentlich zugänglich zu machen, weil ich sehr pingelich bei sauberen Architekturen bin. Was mir noch einfallen würde wäre internal, aber dazu müsste ich die Klassen in ein Assembly auslagern (oder geht das vielleicht auch anders?), und das möchte ich ehrlich gesagt auch nicht, da das weitere Schwierigkeiten zur folge hätte (das würde zu lange dauern um das zu erklären). Hat jemand einen Vorschlag? Wäre wür jede Hilfe dankbar!
 
Hallo Darkas,

also wenn ich dich richtig verstanden habe, willst du das gleiche in .Net implementieren wie der Wizard in Eclipse implmementiert ist, von daher schau doch mal im Code von Eclipse nach wie die es gemacht haben. Ansonsten folgender Vorschlag:
Entwickele doch einen generischen Wizard. Das heißt dass du z.B. eine WizardPage implementierst, die notwendige Informationen erfasst und in ein Dictionary speichert. Dazu einen WizardController der die WizardPages aufnimmt und die Navigation sowie die Ausführung der WizardPage-Validierung übernimmt. Dann sollte es einen WizardPresenter geben der die den WizardController übergeben bekommt und dessen Methoden ausführt. Der WizardPresenter kann über den Controller auf das aktuelle Page zugreifen und das z.B. Panel (ControlContainer) anzeigen. Hier ist etwas Pseudo-Code:

C#:
using System;
using System.Collections;
namespace GenericWizard.Model
{
	
	/// <author>kde-se</author>
	public interface IWizardPage<TControl>
	{
		string Title
		{
			get;set;
		}
		
		string Description
		{
			get;set;
		}
		
		TControl ControlContainer
		{
			get;
		}
		
		IDictionary Validate(IDictionary theContainer);
	}
}


using System;
using System.Drawing;
using System.Windows.Forms;

namespace GenericWizard.Model.Internal
{
	/// <author>kde-se</author>
	public abstract partial class AbstractWizardPage : Form, IWizardPage<Panel>
	{
		public AbstractWizardPage()
		{
			InitializeComponent();
			panel = new Panel();
			this.Controls.Add(panel);
		}
		
		public string Title {
			get { return title; }
			set { title = value; }
		} private string title;
		
		public string Description {
			get { return description; }
			set { description = value; }
		} private string description;
		
		/// <summary>
		/// Holds the container of controls used to present in the wizard presenter.
		/// </summary>
		public System.Windows.Forms.Panel ControlContainer {
			get { return panel;	}
		} private Panel panel;
		
		/// <summary>
		/// Validate the wizard page.
		/// </summary>
		/// <remarks>The data container contains a list of errors with 'errors' as key and
		/// a list with strings as value, if the validation failed.</remarks>
		/// <param name="theContainer">Holds the input data from wizard page if validation is successful,
		/// else a list of errors.</param>
		/// <returns>The extended data container</returns>
		public System.Collections.IDictionary Validate(System.Collections.IDictionary theContainer)
		{
			//validate the wizard page
			throw new NotImplementedException();
		}
		
	}
}



using System;
using System.Collections;
using System.Collections.Generic;
using GenericWizard.Model.Internal;
namespace GenericWizard.Controller
{
	/// <author>kde-se</author>
	public interface IWizardController
	{
		void AddPage(AbstractWizardPage aPage);
		void RemovePage(AbstractWizardPage aPage);
		void NextPage();
		void PreviousPage();
		IList<string> Errors
		{
			get;
		}
		
		AbstractWizardPage CurrentPage
		{
			get;
		}
		
		
		WizardConclusion Conclude;
	}
	
	delegate IDictionary WizardConclusion(IDictionary aContainer);
}



using System;
using System.Drawing;
using System.Windows.Forms;
using GenericWizard.Controller;
namespace GenericWizard.UI.Internal
{
	/// <author>kde-se</author>
	public partial class WizardPresenter : Form, IWizardPresenter
	{
		public WizardPresenter(IWizardController theController)
		{
			
			InitializeComponent();
			
			this.controller = theController;
		}
		
		public IWizardController Controller {
			get { return controller; }
			set { controller = value; }
		} private IWizardController controller;
		
		void Button2Click(object sender, EventArgs e)
		{
			controller.NextPage();
			//this.Panel.Parent = controller.CurrentPage.ControllContainer
		}
	}
}

Wenn du das so ähnlich machst kannst du den Wizard in einer Assembly kapseln und deine WizardPages in deinem Projekt von AbstractWizardPage ableiten. Damit brauchst du eigentlich nur die Controls über den GUI-Designer zusammenzustecken und die Validierungsmethode zu implementieren. Diese erstellten WizardPages anschließend dem Controller hinzuzufügen und den an eine Instanz von WizardPresenter weiter zu geben. Müsste eigentlich so funktionieren. Sobald auf dem WizardPresenter ein Finish Button gedrückt wird soll dann das Delegate WizardConclusion ausgeführt werden.

Gruß Konstantin
 
Zuletzt bearbeitet von einem Moderator:
Das klingt schon ganz brauchbar, allerdings hätte ich es lieber so, dass alle Fensterklassen nur über meinen WizardProvider erreichbar sind. So als ob sie darin geschachtelt und private sind. Ich könnte sie zwar in ein Assembly auslagern, und als internal definieren, abe da hätte ich halt den nachteil mit dem Assembly, was auch wieder zu komplikationen führt. Gibt es da vielleicht eine Lösung?
 
also ich hab mal folgendes Probiert: ich habe den WizardProvider zu einer partial class gemacht, und um meine einzelnen Dialogfenster ebenfalls ein partial WizardProvider gemacht. Es startet auch, allerdings habe ich jetzt ein problem mit der Lokalisierung: er findet den inhalt meiner in die Form geschachtelten *.resx Datei nicht mehr. Das ganze funktioniert mit dem üblichen getResources, wie man es von der lokalisierung kennt. Hat da jemand eine Lösung?
 
1) Zahlreiche Fenster in diesem Zusammenhang sehe ich persönlich als recht schmutzig an. Alles löst sich mit entsprechenden Controls lösen, daher widerspreche ich deinem Satz aus der ersten Post.
2) Für das von dir gewünschte Verhalten würde ich eine eigene Assembly erstellen, zumal man diese ohnehin wieder in eine einzige Assembly zusammenfassen kann.
3) Ich selbst bin kein großer Fan der .NET Ressourcen Behandlung, verwende diesbezüglich daher meine eigenen Routinen, die hier auch recht wenig Stress machen, im Gegensatz zu dem von dir erwähnten Verfahren.
 
Zurück