2009-08-20 15 views
9

Estoy luchando con una situación que vuelvo a repetir una y otra vez, pero no estoy seguro de si la forma en que estoy haciendo las cosas está mal o si podría estar haciendo las cosas de otra manera.Uso de Windows Form como clase abstracta: ¿qué patrón usar?

Un ejemplo:

Tengo un formulario Windows Forms que tiene un DataGridView con algunos métodos particulares para llevar a cabo la validación de la cuadrícula de datos e interpretación de clics de ratón derecho en el DataGridView etc. Esta forma de ventanas es esencialmente una clase de "abstracto" y nunca se crea una instancia directamente.

Luego heredo de esta clase base y la personalizo de varias maneras (patrón de plantilla) p. defina las columnas de la vista de cuadrícula de datos y los métodos particulares de formateo que son específicos de esas columnas, etc.

Cuando consumo estas clases, los métodos públicos de la clase base forman mi interfaz y puedo instanciar el tipo particular de datagridview que quiero y manipularlo a través de la interfaz común. Encantador.

El problema:

El problema principal es que la realidad no se puede declarar una clase de formularios Windows tan abstracto sin causar el diseñador de Visual Studio para lanzar una tambaleante ya que no puede crear instancias de estas clases abstractas.

algunas soluciones:

En este momento estoy "de ejecución" esos métodos en la clase base que quiero ser anulados con:

throw new NotSupportedException(); 

así que al menos si olvido para anular una de estos métodos que forman mi interfaz. Sin embargo, esto me parece mal y no me gusta.

Otra solución con la que jugué fue eliminar por completo la herencia y definir una interfaz (por ejemplo, IMyDataGrid) e implementarla en cada clase de grid de datos (tipo de patrón de estrategia). El problema aquí es que usted pierde los beneficios de la reutilización de código que le da herencia, lo que significa que tiene que crear muchas formas diferentes, colocar una vista de tabla de datos en ellas, copiar y pegar efectivamente el mismo código en cada una. Malo.

¿Hay una mejor manera de tratar de lograr esto?

+0

¿Por qué no heredar directamente de GridView y crear un control para usar en Windows Form? – Macros

Respuesta

4

Hay muchas maneras de hacerlo dependiendo de sus requisitos.

  • Poner el contenido del formulario en un control de usuario que implementa una interfaz para hacer la lógica personalizada
  • clases derivan de DataGridView que implementa una interfaz para hacer la lógica personalizada
  • Como se ha mencionado, utilice un concreto virtual clase con métodos en lugar de utilizar una clase abstract
  • ...

Tendrás que elegir la opción que mejor se adapte a tus necesidades. Sin conocer el dominio y las características específicas de su pregunta, no creo que podamos darle una respuesta 100% segura.

+0

¡Estas sugerencias son buenas! – Calanus

1

Consulte this method para saber cómo crear los dos atributos necesarios.

Es necesario el siguiente atributo y la clase descriptor de tipo (código tomado de 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); 
     } 
    } 
} 

asignarlas a sus forma de resumen así:

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

crear una nueva clase que herede de tu forma abstracta Esta clase será instanciada por Visual Studio

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

Esto debería funcionar.

+1

@ Pierre-Alain Vigeant: La página web que mencionas está por cierto. – Marc

+0

Sospecho que hay un cambio en el funcionamiento del sitio web. La mayoría de los enlaces que apuntan allí ya no se resuelven. Consulte la pregunta de Microsoft Connect sobre este problema http://connect.microsoft.com/VisualStudio/feedback/details/322712/forms-designer-cannot-show-form-that-herits-from-an-abstract-form y señale al enlace que escribí Trataré de encontrar otra referencia. –

+1

El método anterior probablemente estaría bien pero ¡qué mucho trabajo! – Calanus

Cuestiones relacionadas