Абстрактное наследование UserControl в конструкторе Visual Studio - PullRequest
29 голосов
/ 25 июля 2011
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;
    }
}

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

Ответы [ 9 ]

45 голосов
/ 15 июля 2013

Что мы хотим

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

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 к элементу управления Abstract.

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

И это все. Средний контроль не требуется.

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

19 голосов
/ 25 июля 2011

Вы можете использовать TypeDescriptionProviderAttribute, чтобы обеспечить конкретную реализацию во время разработки для вашего абстрактного базового класса.Подробнее см. http://wonkitect.wordpress.com/2008/06/20/using-visual-studio-whidbey-to-design-abstract-forms/.

6 голосов
/ 24 июня 2012

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

#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, (C #)

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

2 голосов
/ 21 января 2015

Я не мог заставить работать решение «Николь Калиной».Но есть и другой простой способ непосредственно в visual studio:)

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

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

2 голосов
/ 20 ноября 2014

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

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

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;
    }
}

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

2 голосов
/ 26 сентября 2012

Ниже приведено общее решение, которое в основном работает для меня. Это основано на статье из другого ответа. Иногда это будет работать, и я могу создать свой 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"/>'s 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);
        }
    }
}
0 голосов
/ 19 августа 2017

Я был новичком в UWP, и это сводило меня с ума. Я не думал об абстрактном базовом классе для UserControl. Я пошел в другом направлении. Я создал вспомогательный класс не xaml ... HBase. Каждый вид, скажем, VContract, имел соответствующего помощника по имени HContract. Весь специальный код для каждого вида был размещен там. Разговоры, которые были бы между ViewModel VMContract и View VContract, теперь проходят через HContract. Мы можем установить, как HWhatever ведет себя с IHBase. Это не совсем ответ на вопрос ОП, но он показывает альтернативный подход. Все виды теперь в основном снаряды. Независимо от того, являетесь ли вы x: Bind to 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               
}
0 голосов
/ 24 апреля 2017

Я решил эту проблему в 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" />

С уважением,

Самуил

0 голосов
/ 14 ноября 2012

Я просто превращаю абстрактный базовый класс в конкретный, определяя «абстрактные» методы как виртуальные, и добавляя в них исключение, на тот случай, если какие-нибудь непослушные производные классы попытаются вызвать реализацию 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  ;)
        }

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...