2011-07-25 27 views
24
abstract class CustomControl : UserControl 
{ 
    protected abstract int DoStuff(); 
} 

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

我在表单中放置了一个DetailControl。它在运行时正确呈现,但设计器显示错误,并且不会打开,因为基本用户控件是抽象的。Visual Studio设计器中的抽象UserControl继承

目前,我正在考虑下面的补丁,这对我来说似乎很不对劲,因为我希望子类被强制实施该方法。

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

任何人有更好的主意,如何解决这个问题?

+1

我最近也有类似的,去了有一个单独的接口,提供我想要的方式。当我需要编辑它时,我也尝试将基类更改为UserControl,但它很混乱。 – Deanna

+0

[我如何才能让Visual Studio 2008 Windows窗体设计器渲染实现抽象基类的窗体?](http://stackoverflow.com/questions/1620847/how-can-i-get-visual- studio-2008-windows-forms-designer-to-render-a-form-that-im) – Breeze

回答

17

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

+1

它只有当我第一次打开Visual Studio时才有效。一旦我对代码进行任何更改并重建项目,它就会停止工作。不管怎样,谢谢。 – Elephant

+0

@Elephant有可能是devenv中的一个bug,后来被修复了。我现在正在使用VS2017,我可以打开设计器,关闭设计器,更改代码,重新编译项目,设计器仍然可以打开。 – Coxy

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 

    ... 
    } 

请参阅此链接了解有关此主题的更多信息:​​

同样的解决方案也正是在这一MSDN论坛线程以简短的方式提及,:UserControl, Inherited Control, Abstract class, (C#)

也许是不清晰的解决方案,但它仍然是我发现的最短的。

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); 
     } 
    } 
} 
+0

这似乎很适合编辑设计器中的抽象UI,但是当我从抽象类继承并尝试编辑子类时,我得到了关于不能实例化抽象基类的相同错误。我在做一些愚蠢的事情吗? – Akuma

+2

我有同样的问题。尝试类似构建解决方案,打开/编辑派生控件(无论是否成功),关闭派生控件,清理解决方案,关闭Visual Studio,重新打开Visual Studio和解决方案,重建解决方案。我的成功/失败是间歇性的。 –

+0

+1是的,声明类型提供者工作后清理并重新打开 –

0

我只是做了抽象基类为通过定义“抽象”的方法是虚拟的,并在他们抛出一个异常,以防任何顽皮的派生类尝试调用器的基本实现一个具体的一个。

例如

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 - 您应该可以编辑抽象UI类在设计师。我现在没有时间去检查它,但是我的理解是设计者只需要实例化BASE类。只要你正在设计的课程的基础是具体的,不管设计课程是什么。

+5

这与我最初在我的问题中发布的补丁不完全相同吗? – asmo

35

我们想要什么

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

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

就是这样。不需要中间控制。

而提供者类可以应用于同样的解决方案中尽可能多的抽象基础。

+2

这对我有效,虽然在我的情况下,我在具体的子类和UserControl之间有两个'abstract'类。对于这两个'abstract'类,我需要为'TypeDescriptionProvider'提供'UserControl'的'TBase'。为了明确清楚谁来到我身后,我将'TBase'重命名为'TFirstConcreteBase'。我曾希望两个原始的TBase声明能够链接在一起以获得正确的具体基础,但并非如此。感谢您的帮助,@Juan。 – JMD

+2

很高兴能帮到@JMD –

+2

您还需要重新编译您的项目并关闭Visual Studio以使设计器消息消失 – Alex

2

即使这个问题已经过去了几年,我想添加我发现的东西。

如果你不想碰你的抽象基类,你可以做到这一点黑客

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

这样,你的形式从非抽象基窗体继承,它的显示在设计师!你保留你的抽象形式,但只有一个级别的继承。奇怪,不是吗?

+1

中所述,这并没有真正的帮助,因为它消除了抽象基类的好处,需求您实施该方法。抛出异常只会在运行时强制执行要求。 –

+1

是的,这并不理想。但是你仍然可以保持你的抽象类在整个应用程序中按照你的计划使用。而这个仅用于IDE限制的中间助手类甚至可以放置在同一个.cs文件中,以使它更接近于没有它的情况:即在该文件中实现所有抽象方法,这是唯一的区别在于你必须做两次:*投掷*一个和真正的。有更好的选择吗? – Andrew

1

我无法为'Nicole Calinoiu'的解决方案工作。但有一个其他简单的方法,直接在Visual Studio :)

  1. 创建新项目
  2. 添加新的元素“用户控件”,添加一个按钮,例如
  3. 添加新的元素“用户控件” Inhereted用户控件然后选择干涉的userControl。

更多细节这里为'http://www.codeproject.com/Articles/20845/How-to-derive-from-a-parent-form

+0

这对我来说很有效,这是最简单的方法。 – Malick

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" /> 

问候,

塞缪尔

0

我在UWP是新来的这一点,这是我发疯。我没有想到UserControl的抽象基类。我走向另一个方向。我创建了非xaml助手类... HBase。每个视图,比如说VContract,都有一个相应的助手叫做HContract。每个视图的所有专业代码都放在那里。现在,ViewModel VMContract和View VContract之间的对话现在通过HContract。我们可以执行HWhatever如何与IHBase行为。这不是真正解决OP问题的答案,但确实表明了另一种方法。所有的视图现在基本上是炮弹。无论你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    
}