Использование формы Windows в качестве абстрактного класса - какой шаблон использовать? - PullRequest
10 голосов
/ 20 августа 2009

Я борюсь с ситуацией, когда я снова и снова сталкиваюсь с ситуацией, но я не уверен, что мой поступок неправильный или я могу поступать иначе.

Пример:

У меня есть форма Windows, в которой есть DataGridView с некоторыми закрытыми методами для проверки сетки данных и интерпретации щелчков правой кнопкой мыши по представлению данных и т. Д. Эта форма Windows по сути является «абстрактным» классом и никогда не создается непосредственно.

Затем я наследую от этого базового класса и настраиваю его различными способами (шаблон шаблона), например. определить столбцы таблицы данных и конкретные методы форматирования, которые специфичны для этих столбцов и т. д.

Когда я использую эти классы, открытые методы базового класса формируют мой интерфейс, и я могу создать экземпляр определенного типа сетки данных, который мне нужен, и манипулировать им через общий интерфейс. Прекрасный.

Проблема:

Основная проблема заключается в том, что вы не можете фактически объявить класс Windows Form абстрактным, не вызывая конструктора Visual Studio шатким, поскольку он не может создать экземпляр этих абстрактных классов.

Некоторые решения:

В данный момент я «реализую» те методы в базовом классе, которые я хочу переопределить:

    throw new NotSupportedException();

так что, по крайней мере, если я забуду переопределить один из этих методов, которые формируют мой интерфейс. Это мне кажется довольно вонючим, и мне это не очень нравится.

Другое решение, с которым я играл, заключалось в том, чтобы полностью отказаться от наследования и определить интерфейс (например, IMyDataGrid) и реализовать его в каждом классе представления данных (своего рода шаблон стратегии). Проблема здесь заключается в том, что вы теряете преимущества повторного использования кода, которые дает вам наследование, что означает, что вам нужно создавать много разных форм, создавать на них решетку данных - эффективно копировать и вставлять один и тот же код в каждую. Bad.

Есть ли лучший способ добиться этого?

Ответы [ 3 ]

4 голосов
/ 04 марта 2010

Есть много способов сделать это в зависимости от ваших требований.

  • Поместите форму content в пользовательский элемент управления, который реализует интерфейс для выполнения пользовательской логики
  • Извлечение классов из DataGridView, который реализует интерфейс для выполнения пользовательской логики
  • Как уже упоминалось, используйте конкретный класс с virtual методами вместо abstract класса
  • ...

Вам нужно будет выбрать вариант, который наилучшим образом соответствует вашим потребностям. Не зная предметной области и специфики, в которой задают ваш вопрос, я не думаю, что мы можем дать вам 100% -ный верный ответ.

1 голос
/ 20 августа 2009

Проверьте этот метод , чтобы узнать, как создать два необходимых атрибута.

Вам нужен следующий класс дескриптора атрибута и типа (код взят из 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);
        }
    }
}

Назначьте их вашей абстрактной форме следующим образом:

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

Создайте новый класс, который будет наследоваться от вашей абстрактной формы. Этот класс будет создан Visual Studio

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

Это должно работать.

0 голосов
/ 04 марта 2010

У меня такая же проблема.
Эта страница может вам помочь, даже если это всего лишь обходной путь: Наследование формы из абстрактного класса (и заставить ее работать в Designer) .

Я не нашел лучшего решения, но, похоже, нет. Поэтому дизайнер Windows Forms заставляет вас адаптировать дизайн своего класса.

...