Ich wusste, dass es einen Weg geben musste, dies zu tun (und ich fand einen Weg, dies sauber zu tun). Shengs Lösung ist genau das, was ich mir als vorübergehende Problemumgehung ausgedacht habe, aber nachdem ein Freund darauf hingewiesen hat, dass die Form
Klasse letztendlich von einer abstract
Klasse geerbt wurde , sollten wir in der Lage sein, dies zu erreichen. Wenn sie es können, können wir es schaffen.
Wir sind von diesem Code zum Problem übergegangen
Form1 : Form
Problem
public class Form1 : BaseForm
...
public abstract class BaseForm : Form
Hier kam die erste Frage ins Spiel. Wie bereits erwähnt, wies ein Freund darauf hin, dass System.Windows.Forms.Form
eine abstrakte Basisklasse implementiert wird. Wir konnten finden ...
Beweis einer besseren Lösung
Aus diesem Grund wussten wir, dass es dem Designer möglich war, eine Klasse anzuzeigen, die eine abstrakte Basisklasse implementierte. Es konnte einfach keine Designerklasse angezeigt werden, die sofort eine abstrakte Basisklasse implementierte. Dazwischen mussten maximal 5 sein, aber wir haben 1 Abstraktionsebene getestet und zunächst diese Lösung gefunden.
Erste Lösung
public class Form1 : MiddleClass
...
public class MiddleClass : BaseForm
...
public abstract class BaseForm : Form
...
Dies funktioniert tatsächlich und der Designer macht es gut, das Problem ist gelöst ... außer dass Sie eine zusätzliche Vererbungsstufe in Ihrer Produktionsanwendung haben, die nur aufgrund einer Unzulänglichkeit im Winforms-Designer erforderlich war!
Dies ist keine 100% todsichere Lösung, aber sie ist ziemlich gut. Grundsätzlich verwenden Sie #if DEBUG
, um die raffinierte Lösung zu finden.
Raffinierte Lösung
Form1.cs
#if DEBUG
public class Form1 : MiddleClass
#else
public class Form1 : BaseForm
#endif
...
MiddleClass.cs
public class MiddleClass : BaseForm
...
BaseForm.cs
public abstract class BaseForm : Form
...
Dies verwendet nur die in "Erstlösung" beschriebene Lösung, wenn sie sich im Debug-Modus befindet. Die Idee ist, dass Sie den Produktionsmodus niemals über einen Debug-Build freigeben und immer im Debug-Modus entwerfen.
Der Designer wird immer mit dem im aktuellen Modus erstellten Code ausgeführt, sodass Sie den Designer nicht im Release-Modus verwenden können. Solange Sie jedoch im Debug-Modus entwerfen und den im Release-Modus erstellten Code freigeben, können Sie loslegen.
Die einzige todsichere Lösung wäre, wenn Sie den Entwurfsmodus über eine Präprozessor-Direktive testen können.
@smelch, Es gibt eine bessere Lösung, ohne ein mittleres Steuerelement erstellen zu müssen, selbst für das Debuggen.
Was wir wollen
Definieren wir zunächst die letzte Klasse und die abstrakte Basisklasse.
Jetzt brauchen wir nur noch einen Beschreibungsanbieter .
Schließlich wenden wir einfach ein TypeDescriptionProvider-Attribut auf das Abastract-Steuerelement an.
Und das ist es. Keine mittlere Steuerung erforderlich.
Und die Provider-Klasse kann in derselben Lösung auf so viele abstrakte Basen angewendet werden, wie wir möchten.
* EDIT * In der app.config wird auch Folgendes benötigt
Danke @ user3057544 für den Vorschlag.
quelle
TypeDescriptionProvider
@ Melch, danke für die hilfreiche Antwort, da ich kürzlich auf dasselbe Problem gestoßen bin.
Im Folgenden finden Sie eine geringfügige Änderung an Ihrem Beitrag, um Kompilierungswarnungen zu vermeiden (indem Sie die Basisklasse in die
#if DEBUG
Vorprozessor-Direktive aufnehmen):quelle
Ich hatte ein ähnliches Problem, fand aber einen Weg, Dinge umzugestalten, um eine Schnittstelle anstelle einer abstrakten Basisklasse zu verwenden:
Dies gilt möglicherweise nicht für jede Situation, führt jedoch nach Möglichkeit zu einer saubereren Lösung als die bedingte Kompilierung.
quelle
UserControl
der Schnittstelle eine Eigenschaft hinzu und verwies darauf, wann immer ich direkt darauf zugreifen musste. In meinen Schnittstellenimplementierungen erweitere ich UserControl und setze dieUserControl
Eigenschaft aufthis
Ich verwende die Lösung in dieser Antwort auf eine andere Frage, die diesen Artikel verlinkt . Der Artikel empfiehlt die Verwendung einer benutzerdefinierten
TypeDescriptionProvider
und konkreten Implementierung der abstrakten Klasse. Der Designer fragt den benutzerdefinierten Anbieter, welche Typen verwendet werden sollen, und Ihr Code kann die konkrete Klasse zurückgeben, sodass der Designer zufrieden ist, während Sie die vollständige Kontrolle darüber haben, wie die abstrakte Klasse als konkrete Klasse angezeigt wird.Update: Ich habe ein dokumentiertes Codebeispiel in meine Antwort auf diese andere Frage aufgenommen. Der Code dort funktioniert, aber manchmal muss ich einen Bereinigungs- / Erstellungszyklus durchlaufen, wie in meiner Antwort angegeben, damit er funktioniert.
quelle
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 seinForm1
). DasTypeDescriptionProvider
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: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 dieTypeDescriptionProvider
vonBaseForm
. Keine bedingte Kompilierung.Ich hatte noch zwei Probleme:
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.Und hier haben Sie die leicht modifizierte Version von
TypeDescriptionProvider
: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
inpublic class Form1 : BaseFormMiddle1
(oderBaseFormMiddle2
) 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 .quelle
Ich habe einen Tipp für Juan Carlos Diaz Lösung. Es funktioniert gut für mich, war aber ein Problem damit. Wenn ich VS starte und Designer eingebe, funktioniert alles einwandfrei. Nachdem Sie die Lösung ausgeführt haben, stoppen und beenden Sie sie und versuchen Sie dann, Designer einzugeben. Die Ausnahme wird immer wieder angezeigt, bis VS neu gestartet wird. Aber ich habe die Lösung dafür gefunden - alles, was Sie tun müssen, ist unten zu Ihrer app.config hinzuzufügen
quelle
Da die abstrakte Klasse
public abstract class BaseForm: Form
einen Fehler ausgibt und die Verwendung des Designers vermeidet, habe ich virtuelle Mitglieder verwendet. Anstatt abstrakte Methoden zu deklarieren, habe ich im Grunde genommen virtuelle Methoden mit dem geringstmöglichen Text deklariert. Folgendes habe ich getan:Da
DataForm
es sich bei dem abstrakten Mitglied um eine abstrakte Klasse handeln solltedisplayFields
, "fälsche" ich dieses Verhalten mit virtuellen Mitgliedern, um die Abstraktion zu vermeiden. Der Designer beschwert sich nicht mehr und alles funktioniert gut für mich.Es ist eine besser lesbare Problemumgehung, aber da es nicht abstrakt ist, muss ich sicherstellen, dass alle untergeordneten Klassen von
DataForm
implementiert sinddisplayFields
. Seien Sie daher vorsichtig, wenn Sie diese Technik verwenden.quelle
Der Windows Forms Designer erstellt eine Instanz der Basisklasse Ihres Formulars / Steuerelements und wendet das Analyseergebnis von an
InitializeComponent
. Aus diesem Grund können Sie das vom Projektassistenten erstellte Formular entwerfen, ohne das Projekt zu erstellen. Aufgrund dieses Verhaltens können Sie auch kein Steuerelement entwerfen, das von einer abstrakten Klasse abgeleitet ist.Sie können diese abstrakten Methoden implementieren und eine Ausnahme auslösen, wenn sie nicht im Designer ausgeführt wird. Der vom Steuerelement abgeleitete Programmierer muss eine Implementierung bereitstellen, die Ihre Basisklassenimplementierung nicht aufruft. Andernfalls würde das Programm abstürzen.
quelle
Sie können das
abstract
Schlüsselwort nur bedingt kompilieren, ohne eine separate Klasse einzufügen:Dies funktioniert, sofern
BaseForm
keine abstrakten Methoden vorhanden sind (dasabstract
Schlüsselwort verhindert daher nur die Laufzeitinstanziierung der Klasse).quelle