Ho alcuni suggerimenti per le persone che dicono che il TypeDescriptionProvider
di Juan Carlos Diaz non funziona e non piace nemmeno la compilazione condizionale:
Prima di tutto, potrebbe essere necessario riavviare Visual Studio affinché le modifiche nel codice funzionino nel progettista del modulo (dovevo farlo, la semplice ricostruzione non funzionava o non ogni volta).
Presenterò la mia soluzione di questo problema per il caso della forma base astratta. Supponiamo che tu abbia una BaseForm
classe e desideri che qualsiasi modulo basato su di esso sia progettabile (questo sarà Form1
). Il TypeDescriptionProvider
come presentato da Juan Carlos Diaz non ha funzionato anche per me. Ecco come l'ho fatto funzionare, unendola alla soluzione MiddleClass (di smelch), ma senza la#if DEBUG
compilazione condizionale e con alcune correzioni:
[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
}
}
Notare l'attributo sulla classe BaseForm. Allora non resta che dichiarare le TypeDescriptionProvider
e due classi medie , ma non ti preoccupare, sono invisibili e irrilevanti per lo sviluppatore di Form1 . Il primo implementa i membri astratti (e rende la classe base non astratta). Il secondo è vuoto: è necessario solo affinché il progettista di moduli VS funzioni. Quindi assegni la seconda classe media al TypeDescriptionProvider
di BaseForm
. Nessuna compilazione condizionale.
Avevo altri due problemi:
- Problema 1: dopo aver modificato Form1 in designer (o un po 'di codice), l'errore restituiva di nuovo (quando si tentava di aprirlo di nuovo in designer).
- Problema 2: i controlli di BaseForm venivano posizionati in modo non corretto quando la dimensione del Form1 veniva modificata nella finestra di progettazione e il modulo veniva chiuso e riaperto nuovamente nella finestra di progettazione del modulo.
Il primo problema (potresti non averlo perché è qualcosa che mi perseguita nel mio progetto in pochi altri posti e di solito produce un'eccezione "Impossibile convertire il tipo X in tipo X"). Ho risolto nel TypeDescriptionProvider
da confrontando i nomi di tipo (FullName) invece del confronto tra i tipi (vedi sotto).
Il secondo problema. Non so davvero perché i controlli del modulo di base non sono progettabili nella classe Form1 e le loro posizioni vengono perse dopo il ridimensionamento, ma ho risolto il problema (non è una bella soluzione - se ne conosci meglio, scrivi). Ho appena spostato manualmente i pulsanti del BaseForm (che dovrebbero essere nell'angolo in basso a destra) nelle loro posizioni corrette in un metodo richiamato in modo asincrono dall'evento Load del BaseForm: la BeginInvoke(new Action(CorrectLayout));
mia classe base ha solo i pulsanti "OK" e "Annulla", quindi il il caso è semplice.
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
{
}
E qui hai la versione leggermente modificata di 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);
}
}
E questo è tutto!
Non devi spiegare nulla ai futuri sviluppatori di moduli basati sul tuo BaseForm e non devono fare alcun trucco per progettare i loro moduli! Penso che sia la soluzione più pulita che può essere (tranne per il riposizionamento dei controlli).
Un altro consiglio:
Se per qualche motivo il progettista si rifiuta ancora di lavoro per voi, si può sempre fare il semplice trucco di cambiare il public class Form1 : BaseForm
verso public class Form1 : BaseFormMiddle1
(o BaseFormMiddle2
) nel file di codice, modificarlo nella forma VS progettista e poi cambiare di nuovo. Preferisco questo trucco alla compilazione condizionale perché è meno probabile che dimentichi e rilasci la versione sbagliata .