TypeDescriptionProvider
对于那些说胡安·卡洛斯·迪亚兹(Juan Carlos Diaz)不工作并且也不喜欢条件编译的人,我有一些提示:
首先,您可能必须重新启动 Visual Studio才能使代码中的更改在表单设计器中工作(我不得不这样做,简单的重建不起作用 - 或者不是每次都起作用)。
我将针对抽象基表单的情况提出我对这个问题的解决方案。假设您有一个BaseForm
类,并且您希望任何基于它的表单都是可设计的(这将是Form1
)。胡安·TypeDescriptionProvider
卡洛斯·迪亚兹(Juan Carlos Diaz)提出的也对我不起作用。这是我通过将其与 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 表单设计器工作所必需的。然后你将第二个中产阶级分配给TypeDescriptionProvider
of BaseForm
。没有条件编译。
我还有两个问题:
- 问题 1:在设计器(或某些代码)中更改 Form1 后,它再次出现错误(尝试再次在设计器中打开它时)。
- 问题 2:在设计器中更改 Form1 的大小并且在窗体设计器中关闭并重新打开窗体时,BaseForm 的控件放置不正确。
第一个问题(您可能没有它,因为它在我的项目中在其他几个地方一直困扰着我,并且通常会产生“无法将 X 类型转换为 X 类型”异常)。我TypeDescriptionProvider
通过比较类型名称(FullName)而不是比较类型(见下文)解决了它。
第二个问题。我真的不知道为什么基本表单的控件在 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 表单设计器中对其进行编辑,然后再将其更改回来。我更喜欢这个技巧而不是条件编译,因为它不太可能忘记和发布错误的版本。