对于有人说TypeDescriptionProvider
Juan Carlos Diaz 的by不起作用并且也不喜欢条件编译的人,我有一些建议:
首先,您可能必须重新启动Visual Studio才能使代码中的更改在表单设计器中起作用(我必须这样做,简单的重新构建不起作用-或并非每次都起作用)。
我将针对抽象基本Form的情况提出我的解决方案。假设您有一个BaseForm
类,并且希望基于它的任何形式都是可设计的(这将是Form1
)。在TypeDescriptionProvider
胡安·卡洛斯·迪亚斯所呈现并没有为我工作也。通过将它与MiddleClass解决方案(通过熔焊)结合起来,但没有#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
的BaseForm
。没有条件编译。
我还有两个问题:
- 问题1:在设计器(或某些代码)中更改Form1后,它再次给出错误(尝试在设计器中再次打开它时)。
- 问题2:当在设计器中更改Form1的大小,并且在窗体设计器中关闭并重新打开窗体时,BaseForm的控件放置不正确。
第一个问题(您可能没有,因为它在我的项目中的其他地方困扰着我,通常会产生“无法将X型转换为X型”异常)。我TypeDescriptionProvider
通过比较类型名称(FullName)而不是比较类型来解决它(请参见下文)。
第二个问题。我真的不知道为什么不能在Form1类中设计基本窗体的控件,并且在调整大小后丢失其位置,但是我已经解决了这个问题(不是一个很好的解决方案-如果您知道更好,请写信)。我只是在从BaseForm的Load事件异步调用的方法中,手动将BaseForm的按钮(应该在右下角)移动到它们的正确位置:BeginInvoke(new Action(CorrectLayout));
我的基类只有“ OK”和“ Cancel”按钮,因此情况很简单。
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表单设计器中对其进行编辑,然后再次将其更改回。与条件编译相比,我更喜欢这种技巧,因为它不太可能忘记并释放错误的版本。