Ich habe einige Tipps für Leute, die sagen, dass das TypeDescriptionProvider
von Juan Carlos Diaz nicht funktioniert und die bedingte Zusammenstellung auch nicht mag:
Zunächst müssen Sie möglicherweise Visual Studio neu starten, damit die Änderungen in Ihrem Code im Formular-Designer funktionieren (ich musste, die einfache Neuerstellung hat nicht funktioniert - oder nicht jedes Mal).
Ich werde meine Lösung dieses Problems für den Fall der abstrakten Basisform vorstellen. Angenommen, Sie haben eine BaseForm
Klasse und möchten, dass darauf basierende Formulare gestaltbar sind (dies wird der Fall sein Form1
). Das TypeDescriptionProvider
von Juan Carlos Diaz vorgestellte hat auch bei mir nicht funktioniert. So habe ich es zum Laufen gebracht, indem ich es mit der MiddleClass-Lösung (per Smelch) verbunden habe, aber ohne das#if DEBUG
bedingte Kompilieren und mit einigen Korrekturen:
[TypeDescriptionProvider(typeof(AbstractControlDescriptionProvider<BaseForm, BaseFormMiddle2>))] // BaseFormMiddle2 explained below
public abstract class BaseForm : Form
{
public BaseForm()
{
InitializeComponent();
}
public abstract void SomeAbstractMethod();
}
public class Form1 : BaseForm // Form1 is the form to be designed. As you see it's clean and you do NOTHING special here (only the the normal abstract method(s) implementation!). The developer of such form(s) doesn't have to know anything about the abstract base form problem. He just writes his form as usual.
{
public Form1()
{
InitializeComponent();
}
public override void SomeAbstractMethod()
{
// implementation of BaseForm's abstract method
}
}
Beachten Sie das Attribut in der BaseForm-Klasse. Dann müssen Sie nur noch die TypeDescriptionProvider
und zwei Mittelklassen deklarieren , aber keine Sorge, sie sind für den Entwickler von Form1 unsichtbar und irrelevant . Der erste implementiert die abstrakten Elemente (und macht die Basisklasse nicht abstrakt). Der zweite ist leer - der VS-Formular-Designer muss nur funktionieren. Dann weisen Sie die zweite Mittelschicht auf die TypeDescriptionProvider
von BaseForm
. Keine bedingte Kompilierung.
Ich hatte noch zwei Probleme:
- Problem 1: Nach dem Ändern von Form1 in Designer (oder einem Code) wurde der Fehler erneut ausgegeben (beim erneuten Öffnen in Designer).
- Problem 2: Die Steuerelemente von BaseForm wurden falsch platziert, als die Größe von Form1 im Designer geändert und das Formular geschlossen und im Formular-Designer erneut geöffnet wurde.
Das erste Problem (Sie haben es möglicherweise nicht, weil es mich in meinem Projekt an wenigen anderen Stellen verfolgt und normalerweise die Ausnahme "Typ X kann nicht in Typ X konvertiert werden" hervorruft). Ich löste es in der TypeDescriptionProvider
durch die Typnamen zu vergleichen (Fullname) anstelle der Arten zu vergleichen (siehe unten).
Das zweite Problem. Ich weiß nicht genau, warum die Steuerelemente des Basisformulars in der Form1-Klasse nicht entworfen werden können und ihre Positionen nach dem Ändern der Größe verloren gehen, aber ich habe es umgangen (keine gute Lösung - wenn Sie es besser wissen, schreiben Sie bitte). Ich verschiebe die Schaltflächen der BaseForm einfach manuell (die sich in der unteren rechten Ecke befinden sollten) an ihre korrekten Positionen in einer Methode, die asynchron vom Ladeereignis der BaseForm aufgerufen wird: BeginInvoke(new Action(CorrectLayout));
Meine Basisklasse hat nur die Schaltflächen "OK" und "Abbrechen" Fall ist einfach.
class BaseFormMiddle1 : BaseForm
{
protected BaseFormMiddle1()
{
}
public override void SomeAbstractMethod()
{
throw new NotImplementedException(); // this method will never be called in design mode anyway
}
}
class BaseFormMiddle2 : BaseFormMiddle1 // empty class, just to make the VS designer working
{
}
Und hier haben Sie die leicht modifizierte Version von TypeDescriptionProvider
:
public class AbstractControlDescriptionProvider<TAbstract, TBase> : TypeDescriptionProvider
{
public AbstractControlDescriptionProvider()
: base(TypeDescriptor.GetProvider(typeof(TAbstract)))
{
}
public override Type GetReflectionType(Type objectType, object instance)
{
if (objectType.FullName == typeof(TAbstract).FullName) // corrected condition here (original condition was incorrectly giving false in my case sometimes)
return typeof(TBase);
return base.GetReflectionType(objectType, instance);
}
public override object CreateInstance(IServiceProvider provider, Type objectType, Type[] argTypes, object[] args)
{
if (objectType.FullName == typeof(TAbstract).FullName) // corrected condition here (original condition was incorrectly giving false in my case sometimes)
objectType = typeof(TBase);
return base.CreateInstance(provider, objectType, argTypes, args);
}
}
Und das ist es!
Sie müssen den zukünftigen Entwicklern von Formularen, die auf Ihrer BaseForm basieren, nichts erklären, und sie müssen keine Tricks ausführen, um ihre Formulare zu entwerfen! Ich denke, es ist die sauberste Lösung, die es geben kann (mit Ausnahme der Neupositionierung der Steuerelemente).
Noch ein Tipp:
Wenn sich der Designer aus irgendeinem Grund immer noch weigert, für Sie zu arbeiten, können Sie jederzeit den einfachen Trick ausführen, indem Sie das public class Form1 : BaseForm
in public class Form1 : BaseFormMiddle1
(oder BaseFormMiddle2
) in der Codedatei ändern , es im VS-Formular-Designer bearbeiten und dann wieder zurück ändern. Ich bevorzuge diesen Trick gegenüber der bedingten Kompilierung, da es weniger wahrscheinlich ist, dass die falsche Version vergessen und veröffentlicht wird .