如何创建 Collection类型的用户控件属性可以在表单设计器中编辑吗?
今天在工作中,我偶然发现了一个让我抓狂的问题。
基本上我的目标是这样的:
我有一个 UserControl1
,其中有一个 Collection
类型的字段和一个相应的属性 Collection
。道具。就像这样:
public class UserControl1 : UserControl
{
private Collection<Class1> field = null;
// later changed to:
//private Collection<Class1> field = new Collection<Class1>();
[Category("Data")]
[DefaultValue(null)]
[Description("asdf")]
public Collection<Class1> prop
{
get { return field; }
set { field = value; }
}
}
// later added:
//[Serializable]
public class Class1
{
private bool booltest; public bool Booltest { get...set...}
private int inttest; public int Inttest { get...set...}
}
如果你已经知道我搞砸了什么:无需阅读其余部分。我将描述我到底做了什么。
现在,我将 UserControl
放入随机表单并更改 Prop
属性。将出现一个通用的“集合编辑器”,类似于列表视图控件中用于列和组的编辑器。我可以按预期输入数据。但是,当我单击“确定”时,数据就消失了。
我花了一个多小时才弄清楚我实际上必须实例化我的字段:private Collection
。很好,只是设计师进入了超级模式。级联噩梦错误消息可以简化为:“您必须将 [Serialized]
放在 Class1
之前。”完成此操作后,我实际上可以再次将我的 UserControl1
放在表单上。
但这只有效一次。编辑某些内容后打开使用 UserControl1
的表单设计器时,出现错误:
类型为“userctltest.Class1[]”的对象无法转换为类型“userctltest.Class1[]”。 Class1[]'.
好吧。错误列表显示:
警告:“userctltest.Class1[]”类型的 ResX 文件对象无法转换为“userctltest.Class1[]”类型。第 134 行,位置 5。无法解析。
设计器尝试从 resx 文件中获取属性的数据。删除 resx 文件就可以“解决”一次。
现在可以使用我的 UserControl1
再次显示该表单。 Collection 属性可编辑,并且正在保存。它确实有效。一次。每当我更改某些内容然后尝试再次打开表单设计器时,上述错误就会再次发生。我可以删除 resx 文件,但这当然也会删除我的数据。
到目前为止对我有帮助的相关资源(在大量不太有用的搜索结果中):
http://www.codeproject.com/Answers/190675/Usercontrol-with-custom-class-property#answer1
http://www.codeproject.com/KB/cs/propertyeditor.aspx
http://www.csharpfriends.com/Articles/getArticle.aspx?articleID=94
http://msdn.microsoft.com/en-us /library/system.runtime.serialization.iserializes.aspx
(我还尝试使用
{ info.AddValue("testbool", testbool); info.AddValue("testint", testint);
也没有帮助(我也尝试了属性名称而不是字段名称))
顺便说一句,我写得像一本糟糕的恐怖小说。
Today at work, I stumbled upon a problem that was driving me nuts.
Basically my goal is this:
I have a UserControl1
, with a field of the type Collection<Class1>
and a corresponding property Collection<Class1> Prop
. Like this:
public class UserControl1 : UserControl
{
private Collection<Class1> field = null;
// later changed to:
//private Collection<Class1> field = new Collection<Class1>();
[Category("Data")]
[DefaultValue(null)]
[Description("asdf")]
public Collection<Class1> prop
{
get { return field; }
set { field = value; }
}
}
// later added:
//[Serializable]
public class Class1
{
private bool booltest; public bool Booltest { get...set...}
private int inttest; public int Inttest { get...set...}
}
If you already know what I screwed up: no need to read the rest. I am going to describe what exactly I did.
Now I put the UserControl
onto a random Form and change the Prop
property. A generic "Collection Editor" appears, like the one used for the columns and groups in a listview control. I can enter data as expected. However, when I click OK, the data is gone.
It took me over hour to figure out that I actually have to instantiate my field: private Collection<MyClass> field = new Collection<MyClass>();
. Very good, only that the designer entered superspazzing mode. Cascading nightmare error message that can be reduced to: "You must put [Serializable]
before your Class1
." After doing that I could actually put my UserControl1
on the Form again.
But that only works once. When opening the designer of the Form where I use the UserControl1
after editing something, it gives me an error:
Object of type 'userctltest.Class1[]' cannot be converted to type 'userctltest.Class1[]'.
Well. The Error List says:
Warning: ResX file Object of type 'userctltest.Class1[]' cannot be converted to type 'userctltest.Class1[]'. Line 134, position 5. cannot be parsed.
The designer tries to fetch the Property's data from the resx file. Removing the resx file "solves" that exactly once.
The Form can now be displayed again, with my UserControl1
. The Collection property is editable, and it is being saved. It actually works. Once. Whenever I change something and then try to open the Form's designer again, the above error occurs again. I can delete the resx file, but that will of course also delete my data.
Relevant resources that helped me so far (among a ton of not so helpful search results):
http://www.codeproject.com/Answers/190675/Usercontrol-with-custom-class-property#answer1
http://www.codeproject.com/KB/cs/propertyeditor.aspx
http://www.csharpfriends.com/Articles/getArticle.aspx?articleID=94
http://msdn.microsoft.com/en-us/library/system.runtime.serialization.iserializable.aspx
(I also tried implementing ISerializable and overriding GetObjectData with
{ info.AddValue("testbool", testbool); info.AddValue("testint", testint); }
didn't help either (I also tried the property names instead of the field names))
Sorry for writing this like a bad horror novel btw.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您想要的是 CodeDom 序列化的设计时支持。您不需要
SerializedAttribute
或ISerialized
,它们用于二进制序列化。由于您想要序列化集合,因此您必须告诉设计者将其序列化。这是通过 DesignerSerializationVisibiliby 属性 -
Content 告诉设计者序列化属性内容而不是属性本身。属性的内容当然应该是 CodeDom 可序列化的,默认情况下是具有简单属性的简单类。
因此,如果您像这样更改
UserControl1
类:...它应该可以解决问题。哦,集合属性通常是不可写的,尽管这不是强制性的。但是序列化程序期望对集合属性进行初始化,这就是为什么您必须为该字段添加初始化的原因。
另外需要注意的是,如果您不希望您的属性在属性编辑器中以粗体标记,您可以通过特殊方法
ShouldSerializePropertyName
指定更复杂的“默认值”,该方法甚至可以是私有的。就像这样:现在你的属性只有在不为空时才会显示为粗体。但我离题了,这不是一个问题:)
What you want is a design time support with CodeDom serialization. You do not need
SerializableAttribute
orISerializable
, those are for binary serialization.Since you want to serialize the collection, you must tell the designer to serialize it as such. That is done with the DesignerSerializationVisibiliby attribute - value of
Content
tells the designer to serialize property contents rather than property itself. Contents of the property should of course be CodeDom serializable, which simple classes with simple properties are by default.So if you change your
UserControl1
class like this:... it should do the trick. Oh and collection properties are usually not writeable, although that is not mandatory. But serializer expects the collection property to be initialized, that is why you had to add initialization for the field.
Another note, if you do not want that your property is marked with bold in the property editor, you can specify a more complex "default value" through a special method
ShouldSerializePropertyName
, which can even be private. Like so:Now your property will only be bold when it is not empty. But I digress, this was not a question :)
完美的例子是这样的:
The perfect exemple is this:
只需将
Collection<>
更改为List<>
Just change
Collection<>
toList<>