问题 如何在表单设计器中使Collection 类型的用户控件属性可编辑?


今天上班时,我偶然发现了一个让我疯狂的问题。

基本上我的目标是:

我有一个 UserControl1,具有该类型的字段 Collection<Class1> 和相应的财产 Collection<Class1> Prop。喜欢这个:

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 属性。将出现一个通用的“集合编辑器”,类似于listview控件中的列和组。我可以按预期输入数据。但是,当我单击“确定”时,数据消失了。

我花了一个多小时才发现我实际上必须实例化我的字段: private Collection<MyClass> field = new Collection<MyClass>();。非常好,只有设计师进入了超级模式。级联噩梦错误信息可以简化为:“你必须把 [Serializable] 在你之前 Class1“这样做后,我真的可以把我的 UserControl1 再次在表格上。

但那只能工作一次。打开我使用的表单的设计者时 UserControl1 编辑之后,它给了我一个错误:

Object of type 'userctltest.Class1[]' cannot be converted to type 'userctltest.Class1[]'.

好。错误列表说:

Warning: ResX file Object of type 'userctltest.Class1[]' cannot be converted to type 'userctltest.Class1[]'. Line 134, position 5. cannot be parsed.

设计者尝试从resx文件中获取Property的数据。删除resx文件“解决”一次。

表格现在可以再次显示,我的 UserControl1。 Collection属性  可编辑的,它正在被保存。它确实有效。一旦。每当我更改某些内容然后再次尝试打开Form的设计器时,会再次出现上述错误。我可以删除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.iserializable.aspx

(我也试过实现ISerializable并重写GetObjectData

{ info.AddValue("testbool", testbool); info.AddValue("testint", testint); }

也没有帮助(我也尝试了属性名称而不是字段名称))

对不起写这个像坏恐怖小说顺便说一句。


4744
2017-09-15 20:21


起源



答案:


您想要的是CodeDom序列化的设计时支持。你不需要 SerializableAttribute 要么 ISerializable,这些是用于二进制序列化。 由于您要序列化集合,因此必须告诉设计者将其序列化。这完成了 DesignerSerializationVisibiliby 属性 - 值的 Content 告诉设计者序列化属性内容而不是属性本身。该属性的内容当然应该是CodeDom可序列化的,默认情况下具有简单属性的简单类。

所以,如果你改变你的 UserControl1 像这样的类:

public class UserControl1 : UserControl
{
    private Collection<Class1> field = new Collection<Class1>();

    [Category("Data")]
    [Description("asdf")]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    public Collection<Class1> prop
    {
        get { return field; }
    }
}

......它应该做的伎俩。哦,集合属性通常是不可写的,虽然这不是强制性的。但是序列化程序期望初始化集合属性,这就是为什么必须为字段添加初始化的原因。 另请注意,如果您不希望属性在属性编辑器中标记为粗体,则可以通过特殊方法指定更复杂的“默认值” ShouldSerializePropertyName,甚至可以是私人的。像这样:

private bool ShouldSerializeprop() 
{
    return (field.Count > 0);
}

现在,您的属性只有在非空时才会变为粗体。但我离题了,这不是一个问题:)


11
2017-09-15 20:32



这似乎有效。虽然当我第一次尝试它时,设计师再次出现了(不是可序列化的等等)。经过一个小时的摆弄 最后没有实际改变任何东西(我检查了颠覆) 它完美无缺。这是有史以来最奇怪的事情:/谢谢 - dialer
如果您的用户控件与表单位于同一项目中,则必须在使用用户控件之前构建项目。 Designer会查看已编译的程序集,而不是您的代码。 - Patko


答案:


您想要的是CodeDom序列化的设计时支持。你不需要 SerializableAttribute 要么 ISerializable,这些是用于二进制序列化。 由于您要序列化集合,因此必须告诉设计者将其序列化。这完成了 DesignerSerializationVisibiliby 属性 - 值的 Content 告诉设计者序列化属性内容而不是属性本身。该属性的内容当然应该是CodeDom可序列化的,默认情况下具有简单属性的简单类。

所以,如果你改变你的 UserControl1 像这样的类:

public class UserControl1 : UserControl
{
    private Collection<Class1> field = new Collection<Class1>();

    [Category("Data")]
    [Description("asdf")]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    public Collection<Class1> prop
    {
        get { return field; }
    }
}

......它应该做的伎俩。哦,集合属性通常是不可写的,虽然这不是强制性的。但是序列化程序期望初始化集合属性,这就是为什么必须为字段添加初始化的原因。 另请注意,如果您不希望属性在属性编辑器中标记为粗体,则可以通过特殊方法指定更复杂的“默认值” ShouldSerializePropertyName,甚至可以是私人的。像这样:

private bool ShouldSerializeprop() 
{
    return (field.Count > 0);
}

现在,您的属性只有在非空时才会变为粗体。但我离题了,这不是一个问题:)


11
2017-09-15 20:32



这似乎有效。虽然当我第一次尝试它时,设计师再次出现了(不是可序列化的等等)。经过一个小时的摆弄 最后没有实际改变任何东西(我检查了颠覆) 它完美无缺。这是有史以来最奇怪的事情:/谢谢 - dialer
如果您的用户控件与表单位于同一项目中,则必须在使用用户控件之前构建项目。 Designer会查看已编译的程序集,而不是您的代码。 - Patko


完美的例子是这样的:

public partial class SCon : UserControl
    {
        public SCon()
        {
            InitializeComponent();
            if (Persoanas == null)
            {
                Persoanas = new List<Persoana>();
            }
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public List<Persoan> Persoanas { get; set; }

    }

    [Serializable]
    public class Persoan   
    {
        public int Id { get; set; }
        public String Name { get; set; }
    }

3
2018-02-19 08:41





只是改变 Collection<> 至 List<>


0
2017-08-13 13:52