对于那些说 Juan Carlos Diaz 的 TypeDescriptionProvider 不起作用并且也不喜欢条件编译的人,我有一些提示:
首先,您可能必须重新启动 Visual Studio 以使代码中的更改在表单设计器中起作用(我不得不这样做,简单的重建不起作用 - 或者不是每次都起作用) .
我将针对抽象基表单的情况提出我对这个问题的解决方案。假设您有一个BaseForm 类,并且您希望任何基于它的表单都是可设计的(这将是Form1)。 Juan Carlos Diaz 提出的TypeDescriptionProvider 对我也不起作用。以下是我通过将其与 MiddleClass 解决方案(通过 smelch)结合起来使其工作的方法,但没有 #if DEBUG 条件编译并进行了一些更正:
[TypeDescriptionProvider(typeof(AbstractControlDescriptionProvider<BaseForm, BaseFormMiddle2>))] // BaseFormMiddle2 explained below
public abstract class BaseForm : Form
{
public BaseForm()
{
InitializeComponent();
}
public abstract void SomeAbstractMethod();
}
public class Form1 : BaseForm // Form1 is the form to be designed. As you see it's clean and you do NOTHING special here (only the the normal abstract method(s) implementation!). The developer of such form(s) doesn't have to know anything about the abstract base form problem. He just writes his form as usual.
{
public Form1()
{
InitializeComponent();
}
public override void SomeAbstractMethod()
{
// implementation of BaseForm's abstract method
}
}
注意 BaseForm 类的属性。然后你只需要声明TypeDescriptionProvider 和两个中产阶级,但别担心,他们是对Form1 的开发者是不可见的和无关的。第一个实现抽象成员(并使基类非抽象)。第二个是空的——它只是 VS 表单设计器工作所必需的。然后将 second 中产阶级分配给 TypeDescriptionProvider 的 BaseForm。 无条件编译。
我还有两个问题:
-
问题 1: 在设计器(或某些代码)中更改 Form1 后,它再次出现错误(尝试再次在设计器中打开它时)。
-
问题 2: 在设计器中更改 Form1 的大小并在窗体设计器中关闭并重新打开窗体时,BaseForm 的控件放置不正确。
第一个问题(您可能没有它,因为它在我的项目中在其他几个地方一直困扰着我,并且通常会产生“无法将类型 X 转换为类型 X”异常)。我在TypeDescriptionProvider 中通过比较类型名称(全名)而不是比较类型(见下文)解决了这个问题。
第二个问题。我真的不知道为什么基本表单的控件在 Form1 类中不可设计,并且它们的位置在调整大小后丢失,但我已经解决了(不是一个好的解决方案 - 如果你知道更好,请写)。我只是手动将 BaseForm 的按钮(应该在右下角)移动到从 BaseForm 的 Load 事件异步调用的方法中的正确位置:BeginInvoke(new Action(CorrectLayout)); 我的基类只有“确定”和“取消”按钮,所以情况很简单。
class BaseFormMiddle1 : BaseForm
{
protected BaseFormMiddle1()
{
}
public override void SomeAbstractMethod()
{
throw new NotImplementedException(); // this method will never be called in design mode anyway
}
}
class BaseFormMiddle2 : BaseFormMiddle1 // empty class, just to make the VS designer working
{
}
这里有 TypeDescriptionProvider 的略微修改版本:
public class AbstractControlDescriptionProvider<TAbstract, TBase> : TypeDescriptionProvider
{
public AbstractControlDescriptionProvider()
: base(TypeDescriptor.GetProvider(typeof(TAbstract)))
{
}
public override Type GetReflectionType(Type objectType, object instance)
{
if (objectType.FullName == typeof(TAbstract).FullName) // corrected condition here (original condition was incorrectly giving false in my case sometimes)
return typeof(TBase);
return base.GetReflectionType(objectType, instance);
}
public override object CreateInstance(IServiceProvider provider, Type objectType, Type[] argTypes, object[] args)
{
if (objectType.FullName == typeof(TAbstract).FullName) // corrected condition here (original condition was incorrectly giving false in my case sometimes)
objectType = typeof(TBase);
return base.CreateInstance(provider, objectType, argTypes, args);
}
}
就是这样!
您无需向未来基于您的 BaseForm 的表单开发人员解释任何内容,他们也无需采取任何技巧来设计他们的表单!我认为这是最干净的解决方案(除了控件的重新定位)。
另一个提示:
如果由于某种原因设计师仍然拒绝为您工作,您可以随时使用简单的技巧将代码文件中的public class Form1 : BaseForm 更改为public class Form1 : BaseFormMiddle1(或BaseFormMiddle2),在VS 表单中编辑它设计师,然后再改回来。我更喜欢这个技巧而不是条件编译,因为它不太可能忘记和发布错误的版本。