2009-08-20 9 views
9

Zmagam się z sytuacją, która pojawia się raz po raz, ale nie jestem pewien, czy sposób, w jaki robię, jest zły, czy też mogę robić rzeczy w inny sposób.Używanie formularza Windows jako klasy abstrakcyjnej - jakiego wzoru użyć?

Przykład:

Mam Windows formularz z DataGridView z niektórych metod prywatnych sprawdzania poprawności DataGrid i interpretowania prawym przyciskiem myszy na DataGridView itd. Ta forma okna jest zasadniczo „streszczenie” class i nigdy nie jest tworzony bezpośrednio.

Następnie dziedziczę z tej klasy bazowej i dostosowuję ją na różne sposoby (wzorzec szablonu), np. zdefiniować kolumny datagridview i szczególne metody formatowania, które są specyficzne dla tych kolumn itp.

Kiedy zajmuję się tymi klasami, metody publiczne klasy podstawowej tworzą mój interfejs i mogę utworzyć konkretny typ danych datagridview, które chcę i nimi manipulować. przez wspólny interfejs. Śliczny.

Problem:

Głównym problemem jest to, że nie można właściwie zadeklarować klasę Form Windows jako abstrakcyjne bez powodowania projektanta Visual Studio rzucić niepewny, ponieważ nie można utworzyć wystąpienia tych klas abstrakcyjnych.

Niektóre rozwiązania:

W tej chwili jestem „wykonawczym” te metody w klasie bazowej, że chcę być nadpisane z:

throw new NotSupportedException(); 

tak przynajmniej jeśli zapomnę, aby zastąpić jeden te metody, które tworzą mój interfejs. Wydaje mi się to jednak dość śmierdzące i nie podoba mi się to.

Innym rozwiązaniem, z którym się zetknąłem, było całkowite usunięcie dziedzictwa i zdefiniowanie interfejsu (na przykład IMyDataGrid) i wdrożenie go w każdej klasie datagridview (rodzaj wzorca strategii). Problem polega jednak na tym, że tracisz zalety ponownego użycia kodu, które dziedziczenie daje ci sens, że musisz tworzyć wiele różnych formularzy, upuszczasz na nich widok datagridview - skutecznie kopiujesz i wklejasz ten sam kod do każdego z nich. Zły.

Czy istnieje lepszy sposób na próbę osiągnięcia tego?

+0

Dlaczego nie odziedziczyć bezpośrednio z GridView i utworzyć formant do użycia w formularzu systemu Windows? – Macros

Odpowiedz

4

Istnieje wiele sposobów, aby to zrobić w zależności od wymagań.

  • Umieść zawartość forma pod kontrolą użytkownika, który implementuje interfejs do zrobienia logiki niestandardowej
  • klasy wywodzą się z DataGridView który implementuje interfejs do czynienia z niestandardową logikę
  • Jak wspomniano, stosowanie betonu klasa z virtual metod zamiast za pomocą abstract klasę
  • ...

Musisz wybrać opcję, która najlepiej odpowiada Twoim potrzebom. Bez znajomości dziedziny i specyfiki, w której zadawane jest pytanie, nie sądzę, że możemy dać ci 100% pewną odpowiedź.

+0

Te sugestie są dobre! – Calanus

1

Zapoznaj się z this method, aby dowiedzieć się, jak utworzyć dwa potrzebne atrybuty.

Trzeba następujący atrybut i klasa typ deskryptora (kod pochodzi z 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); 
     } 
    } 
} 

przypisać je do abstrakcyjnej formie tak:

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

Utwórz nową klasę, która będzie dziedziczyć z twoja abstrakcyjna forma. Ta klasa zostanie zainicjowana przez Visual Studio

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

To powinno zadziałać.

+1

@ Pierre-Alain Vigeant: Wspomniana strona internetowa znika. – Marc

+0

Podejrzewam zmianę w działaniu witryny. Większość wskazanych tam linków nie jest już rozstrzygnięta. Zobacz pytanie dotyczące tego problemu w witrynie Microsoft Connect http://connect.microsoft.com/VisualStudio/feedback/details/322712/forms-designer-cannot-show-form-that-inherits-from-an-abstract-form i punkt do linku, który napisałem. Spróbuję znaleźć inne odniesienie. –

+1

Powyższa metoda prawdopodobnie byłaby w porządku, ale co za dużo pracy! – Calanus