Visual Studio设计器中的抽象UserControl继承

时间:2011-07-25 13:47:16

标签: c# winforms abstract-class user-controls

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 个答案:

答案 0 :(得分:45)

我们想要什么

首先,让我们定义最终的类和基本的抽象类。

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
...

就是这样。无需中间控制。

提供者类可以在同一解决方案中应用于我们想要的任意数量的抽象基础。

答案 1 :(得分:19)

您可以使用TypeDescriptionProviderAttribute为抽象基类提供具体的设计时实现。有关详细信息,请参阅http://wonkitect.wordpress.com/2008/06/20/using-visual-studio-whidbey-to-design-abstract-forms/

答案 2 :(得分:6)

解决此问题的另一种方法是使用预处理指令。

#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

    ...
  }

有关此主题的更多信息,请参阅此链接:Inheriting a Form from an Abstract Class (and Making it Work in the Designer)

此MSDN论坛帖子中也提到了相同的解决方案,简单明了:UserControl, Inherited Control, Abstract class, (C#)

也许不是更清洁的解决方案,但它仍然是我找到的最短的解决方案。

答案 3 :(得分:2)

以下是一个适合我的通用解决方案。它基于另一个答案的article。有时它会工作,我可以设计我的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);
        }
    }
}

答案 4 :(得分:2)

即使这个问题已经存在多年了,我还想补充一下我发现的问题。

如果您不想触摸抽象基类,可以执行此操作 hack

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

这样,您的表单继承自非抽象的基本表单,并在设计器中显示!而且你保留了抽象的形式,但继承中只有一个级别。奇怪,不是吗?

答案 5 :(得分:2)

我无法将作品解决为Nicole Calinoiu&#39;。但是直接在visual studio中有另一种简单的方法:)

  1. 创建新项目
  2. 添加新元素&#39; userControl&#39;并添加一个按钮,例如
  3. 添加新元素&#39; userControl&#39; Inhereted UserControl 然后选择固有的userControl。
  4. 此处有更多详情:&#39; http://www.codeproject.com/Articles/20845/How-to-derive-from-a-parent-form

答案 6 :(得分:0)

我只是将抽象基类定义为具体的抽象基类,将“抽象”方法定义为虚拟,并在其中抛出异常,以防任何顽皮的派生类尝试调用Base实现。

e.g。

    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实际上是抽象的,则编译器将禁止意外调用Base类实现。这对我来说不是什么大问题,让我可以使用设计师,而不必担心其他人提出的更复杂,更耗时的工作......

PS - Akuma - 您应该能够在设计器中编辑您的抽象UI类。我现在没时间检查这个,但我的理解是设计者只需要实例化BASE类。只要您正在设计的类的基础是具体的,那么设计的类是什么并不重要。

答案 7 :(得分:0)

我在自定义控件中解决了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" />

此致

塞缪尔

答案 8 :(得分:0)

我是UWP的新手,这让我疯狂。我没有想到UserControl的抽象基类。我走向了另一个方向。我创建了一个非xaml Helper类... HBase。每个视图,例如VContract,都有一个名为HContract的对应助手。每个视图的所有专业代码都存放在那里。 ViewModel VMContract和View VContract之间的对话现在会通过HContract。我们可以强制HWhateverIHBase的行为方式。它并不是OP问题的真正答案,但确实显示了另一种方法。所有的视图现在基本上都是贝壳。您是否x:绑定到VContractHContract是您的决定。我选择了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               
}