2009-08-20 6 views
9

Sono alle prese con una situazione che mi viene ripetuta di volta in volta, ma non sono sicuro se il modo in cui sto facendo le cose sia sbagliato o se potrei fare le cose in un modo diverso.Utilizzo di un Windows Form come classe astratta: quale pattern utilizzare?

Un esempio:

Ho una forma di Windows che ha una DataGridView con alcuni metodi privati ​​per eseguire la convalida del datagrid e interpretare clic destro del mouse sul datagridview ecc Questa forma finestre è essenzialmente una classe di "astratto" e non viene mai istanziato direttamente.

Quindi eredito da questa classe di base e la personalizzo in vari modi (modello di modello) ad es. definire le colonne del datagridview e particolari metodi di formattazione che sono specifici per quelle colonne ecc.

Quando consumo queste classi i metodi pubblici di classe base formano la mia interfaccia e posso istanziare il tipo particolare di datagridview che voglio e manipolarlo attraverso l'interfaccia comune. Bello.

Il problema:

Il problema principale è che non si può effettivamente dichiarare una classe Windows Form come astratta senza provocare la progettazione di Visual Studio per gettare un traballante in quanto non può istanziare queste classi astratte.

alcune soluzioni:

Al momento mi sto "di esecuzione" questi metodi nella classe di base che voglio essere sostituite con:

throw new NotSupportedException(); 

così almeno se mi dimentico di ignorare una delle questi metodi che formano la mia interfaccia. Questo mi sembra piuttosto maleodorante e non mi piace molto.

Un'altra soluzione con cui ho giocato era eliminare completamente l'ereditarietà e definire un'interfaccia (ad esempio IMyDataGrid) e implementarla in ogni classe datagridview (una sorta di modello di strategia). Il problema qui però è che perdi i vantaggi del riutilizzo del codice che l'ereditarietà ti dà il significato che devi creare molti moduli diversi, rilasciare una vista dati su di essi - copiare e incollare in modo efficace lo stesso codice in ognuno di essi. Cattivo.

C'è un modo migliore per cercare di raggiungere questo obiettivo?

+0

Perché non ereditare direttamente da GridView e creare un controllo per l'utilizzo su un Windows Form? – Macros

risposta

4

Ci sono molti modi per farlo a seconda delle esigenze.

  • Mettere il contenuto del modulo in un controllo utente che implementa un'interfaccia di fare la logica personalizzata
  • classi derivano da DataGridView che implementa un'interfaccia di fare la logica personalizzata
  • Come accennato, utilizzare un calcestruzzo classe con virtual metodi invece di utilizzare un abstract classe
  • ...

Dovrai scegliere un'opzione che meglio si adatta alle tue esigenze. Senza conoscere il dominio e le specifiche in cui viene posta la tua domanda, non penso che possiamo darti una risposta certa al 100%.

+0

Questi suggerimenti sono buoni! – Calanus

1

Verificare this method per sapere come creare i due attributi necessari.

È necessario il seguente attributo e la classe di tipo descrittore (codice preso dal UrbanPotato)

// Source : taken from http://www.urbanpotato.net/default.aspx/document/2001 Seem to be down 
// Allow the designer to load abstract forms 
namespace YourNamespace 
{ 

    // Place this attribute on any abstract class where you want to declare 
    // a concrete version of that class at design time. 
    [AttributeUsage(AttributeTargets.Class)] 
    public class ConcreteClassAttribute : Attribute 
    { 
     Type _concreteType; 
     public ConcreteClassAttribute(Type concreteType) 
     { 
      _concreteType = concreteType; 
     } 

     public Type ConcreteType { get { return _concreteType; } } 
    } 

    // Here is our type description provider. This is the same provider 
    // as ConcreteClassProvider except that it uses the ConcreteClassAttribute 
    // to find the concrete class. 
    public class GeneralConcreteClassProvider : TypeDescriptionProvider 
    { 
     Type _abstractType; 
     Type _concreteType; 

     public GeneralConcreteClassProvider() : base(TypeDescriptor.GetProvider(typeof(Form))) { } 

     // This method locates the abstract and concrete 
     // types we should be returning. 
     private void EnsureTypes(Type objectType) 
     { 
      if (_abstractType == null) 
      { 
       Type searchType = objectType; 
       while (_abstractType == null && searchType != null && searchType != typeof(Object)) 
       { 

        foreach (ConcreteClassAttribute cca in searchType.GetCustomAttributes(typeof(ConcreteClassAttribute), false)) 
        { 
         _abstractType = searchType; 
         _concreteType = cca.ConcreteType; 
         break; 
        } 
        searchType = searchType.BaseType; 
       } 

       if (_abstractType == null) 
       { 
        // If this happens, it means that someone added 
        // this provider to a class but did not add 
        // a ConcreteTypeAttribute 
        throw new InvalidOperationException(string.Format("No ConcreteClassAttribute was found on {0} or any of its subtypes.", objectType)); 
       } 
      } 
     } 

     // Tell anyone who reflects on us that the concrete form is the 
     // form to reflect against, not the abstract form. This way, the 
     // designer does not see an abstract class. 
     public override Type GetReflectionType(Type objectType, object instance) 
     { 
      EnsureTypes(objectType); 
      if (objectType == _abstractType) 
      { 
       return _concreteType; 
      } 
      return base.GetReflectionType(objectType, instance); 
     } 


     // If the designer tries to create an instance of AbstractForm, we override 
     // it here to create a concerete form instead. 
     public override object CreateInstance(IServiceProvider provider, Type objectType, Type[] argTypes, object[] args) 
     { 
      EnsureTypes(objectType); 
      if (objectType == _abstractType) 
      { 
       objectType = _concreteType; 
      } 

      return base.CreateInstance(provider, objectType, argTypes, args); 
     } 
    } 
} 

assegnarli alla forma astratta in questo modo:

[TypeDescriptionProvider(typeof(GeneralConcreteClassProvider))] 
[ConcreteClass(typeof(MyAbstractConcreteForm))] 
public abstract partial class MyAbstractForm : Form 
{ 
} 

creare una nuova classe che eredita da la tua forma astratta. Questa classe verrà instanciata da Visual Studio

public class MyAbstractConcreteForm: MyAbstractForm 
{ 
    public MyAbstractConcreteForm() : base() { } 
} 

Questo dovrebbe funzionare.

+1

@ Pierre-Alain Vigeant: la pagina web che hai menzionato è a proposito. – Marc

+0

Sospetto un cambiamento nel modo in cui funziona il sito web. La maggior parte dei link che puntano lì non risolve più. Vedere la domanda Microsoft Connect su questo problema http://connect.microsoft.com/VisualStudio/feedback/details/322712/forms-designer-cannot-show-form-that-inherits-from-an-abstract-form e it point al link che ho scritto. Proverò a trovare un altro riferimento. –

+1

Il metodo di cui sopra sarebbe probabilmente ok ma sheesh che molto lavoro! – Calanus