Абстрактное наследование UserControl в дизайнере Visual Studio

abstract class CustomControl : UserControl 
{
    protected abstract int DoStuff();
}

class DetailControl : CustomControl
{
    protected override int DoStuff()
    { 
        // do stuff
        return result;
    }
}

Я сбросил элемент DetailControl в форме. Он корректно отображается во время выполнения, но дизайнер выводит сообщение об ошибке и не будет открываться, поскольку базовый пользовательский элемент управления является абстрактным.

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

class CustomControl : UserControl 
{
    protected virtual int DoStuff()
    {
        throw new InvalidOperationException("This method must be overriden.");
    }
}

class DetailControl : CustomControl
{
    protected override int DoStuff()
    { 
        // do stuff
        return result;
    }
}

У кого-нибудь есть лучшая идея о том, как решить мою проблему?

Ответ 2

Что мы хотим

Сначала определим окончательный класс и базовый абстрактный класс.

public class MyControl : AbstractControl
...
public abstract class AbstractControl : UserControl // Also works for Form
...

Теперь нам нужно поставщик описания.

public class AbstractControlDescriptionProvider<TAbstract, TBase> : TypeDescriptionProvider
{
    public AbstractControlDescriptionProvider()
        : base(TypeDescriptor.GetProvider(typeof(TAbstract)))
    {
    }

    public override Type GetReflectionType(Type objectType, object instance)
    {
        if (objectType == typeof(TAbstract))
            return typeof(TBase);

        return base.GetReflectionType(objectType, instance);
    }

    public override object CreateInstance(IServiceProvider provider, Type objectType, Type[] argTypes, object[] args)
    {
        if (objectType == typeof(TAbstract))
            objectType = typeof(TBase);

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

Наконец, мы просто применяем атрибут TypeDescriptionProvider к элементу управления Abastract.

[TypeDescriptionProvider(typeof(AbstractControlDescriptionProvider<AbstractControl, UserControl>))]
public abstract class AbstractControl : UserControl
...

И что это. Среднее управление не требуется.

И класс провайдера может быть применен к нескольким абстрактным базам, как мы хотим, в том же решении.

Ответ 3

Другим способом решения этой проблемы является использование директив предварительной обработки.

#if DEBUG
  public class UserControlAdmonEntidad : UserControl, IAdmonEntidad
#else
  public abstract class UserControlAdmonEntidad : UserControl, IAdmonEntidad
#endif
  {
    ...
    #if DEBUG
    public virtual object DoSomething()
    {
        throw new NotImplementedException("This method must be implemented!!!");
    }
    #else
    public abstract object DoSomething();
    #endif

    ...
  }

См. эту ссылку для получения дополнительной информации по этой теме: Наследование формы из абстрактного класса (и создание его в дизайнере)

То же самое решение было упомянуто и в этом разделе форума MSDN: UserControl, Inherited Control, Abstract class, (С#)

Возможно, это не более чистое решение, но оно все же самое короткое, что я нашел.

Ответ 4

Следующее - это общее решение, которое работает для меня, в основном. Он основан на статье из другого ответа. Иногда это будет работать, и я могу создать свой UserControl, а затем позже я открою файл, и он предоставит "Дизайнер должен создать экземпляр типа" MyApp.UserControlBase ", но он не может, поскольку тип объявлен как абстрактное". Я думаю, что могу исправить это, очистив, закрыв VS, повторно открыв VS и перестроить. Сейчас это похоже на поведение. Удачи.

namespace MyApp
{
    using System;
    using System.ComponentModel;

    /// <summary>
    /// Replaces a class  of <typeparamref name="T"/> with a class of
    /// <typeparamref name="TReplace"/> during design.  Useful for
    /// replacing abstract <see cref="Component"/>s with mock concrete
    /// subclasses so that designer doesn't complain about trying to instantiate
    /// abstract classes (designer does this when you try to instantiate
    /// a class that derives from the abstract <see cref="Component"/>.
    /// 
    /// To use, apply a <see cref="TypeDescriptionProviderAttribute"/> to the 
    /// class <typeparamref name="T"/>, and instantiate the attribute with
    /// <code>SwitchTypeDescriptionProvider{T, TReplace})</code>.
    /// 
    /// E.g.:
    /// <code>
    /// [TypeDescriptionProvider(typeof(ReplaceTypeDescriptionProvider{T, TReplace}))]
    /// public abstract class T
    /// {
    ///     // abstract members, etc
    /// }
    /// 
    /// public class TReplace : T
    /// {
    ///     // Implement <typeparamref name="T"/> abstract members.
    /// }
    /// </code>
    /// 
    /// </summary>
    /// <typeparam name="T">
    /// The type replaced, and the type to which the 
    /// <see cref="TypeDescriptionProviderAttribute"/> must be
    /// applied
    /// </typeparam>
    /// <typeparam name="TReplace">
    /// The type that replaces <typeparamref name="T"/>.
    /// </typeparam>
    class ReplaceTypeDescriptionProvider<T, TReplace> : TypeDescriptionProvider
    {
        public ReplaceTypeDescriptionProvider() :
            base(TypeDescriptor.GetProvider(typeof(T)))
        {
            // Nada
        }

        public override Type GetReflectionType(Type objectType, object instance)
        {
            if (objectType == typeof(T))
            {
                return typeof(TReplace);
            }
            return base.GetReflectionType(objectType, instance);
        }

        public override object CreateInstance(
            IServiceProvider provider,
            Type objectType,
            Type[] argTypes,
            object[] args)
        {

            if (objectType == typeof(T))
            {
                objectType = typeof(TReplace);
            }

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

Ответ 5

Несмотря на то, что этот вопрос стареет, я хотел бы добавить то, что нашел.

Если вы не хотите касаться своего абстрактного базового класса, вы можете сделать это:

abstract class CustomControl : UserControl 
{
    protected abstract int DoStuff();
}

class BaseDetailControl : CustomControl
{
    protected override int DoStuff()
    {
        throw new InvalidOperationException("This method must be overriden.");
    }
}

class DetailControl : BaseDetailControl
{
    protected override int DoStuff()
    { 
        // do stuff
        return result;
    }
}

Таким образом, ваша форма наследуется от не абстрактной базовой формы и отображается в дизайнере! И вы сохраняете свою абстрактную форму, но только еще один уровень в наследстве. Странно, не правда ли?

Ответ 6

Я не мог сделать работу решением "Nicole Calinoiu". Но в визуальной студии есть и другой простой способ:)

  • Создать новый проект
  • Добавить новый элемент 'userControl' и добавить одну кнопку, например
  • Добавить новый элемент 'userControl' Inhereted UserControl, затем выберите inhereted userControl.

Подробнее здесь: 'http://www.codeproject.com/Articles/20845/How-to-derive-from-a-parent-form

Ответ 7

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

например.

    class Base : UserControl
    {
        protected virtual void BlowUp()
        {
            throw new NotSupportedException("This method MUST be overriden by ALL derived classes.");
        }

    class Derived : Base
    {
        protected override void BlowUp()
        {
            // Do stuff, but don't call base implementation,
            // just like you wouldn't (can't actually) if the Base was really abstract. 
            // BTW - doesn't blow up any more  ;)
        }

Основное практическое различие между этим и реальным абстрактным базовым классом заключается в том, что вы получаете ошибки времени выполнения при вызове базовой реализации, тогда как если бы База была абстрактной, компилятор запретил бы случайные вызовы реализации базового класса. Для меня это не очень важно и позволяет мне использовать дизайнера, не беспокоясь о более сложных и трудоемких работах, предлагаемых другими...

PS - Akuma - вы должны иметь возможность редактировать свой абстрактный пользовательский интерфейс в дизайнере. У меня нет времени, чтобы проверить это прямо сейчас, но я понимаю, что дизайнеру нужно только создать экземпляр класса BASE. Пока базовый класс, который вы проектируете, конкретен, не имеет значения, что представляет собой разработанный класс.

Ответ 8

Я решил эту проблему в UWP в своем настраиваемом элементе управления.

Мой случай

public abstract class BaseModel : DependencyObject 

{
...
}

public class MainModel : BaseModel

{

public bool ShowLabel

{
    get{ return (bool)GetValue(ShowLabelProperty); }
    set{ SetValue(ShowLabelProperty, value) }
}

public static readonly DependencyProperty ShowLabelProperty =

    DependencyProperty.Register("ShowLabel",typeof(bool), typeof(MainModel), new PropertyMetadata(false));

}

Декларация

< MyCustomControl:MainModel ShowLabel=True />

Решение

Просто переопределите фиктивный стиль в общих ресурсах.

<Style TargetType="local:MainModel" />

Привет,

Сэмюэль

Ответ 9

Я был знаком с этим в UWP, и это сводило меня с ума. Я не думал об абстрактном базовом классе для UserControl. Я пошел в другом направлении. Я создал класс не-xaml Helper... HBase. Каждый вид, например VContract, имел соответствующий Помощник, называемый HContract. Здесь был указан весь код специальности для каждого вида. Переговоры, которые были бы между ViewModel VMContract и View VContract, теперь проходят через HContract. Мы можем обеспечить, как HWhatever ведет себя с IHBase. Это не совсем ответ на вопрос ОП, но показывает альтернативный подход. Все Представления теперь являются в основном оболочками. Если вы x: привязка к VContract или HContract - это решение для вас. Я выбрал способ VContract, и, в конце концов, я думаю, что это была ошибка.

Проблема UWP с исключениями в режиме разработки теперь легко фиксируется с помощью:

if (false == Windows.ApplicationModel.DesignMode.DesignModeEnabled)
{
            HContract = new HContract(this);
//   Put code here that fails in Design mode but must at run time               
}