Как сделать свойство пользовательского элемента управления типа Collection <MyClass>редактируемым в конструкторе форм? - PullRequest
13 голосов
/ 16 сентября 2011

Сегодня на работе я наткнулся на проблему, которая сводила меня с ума.

В основном моя цель такова:

У меня есть 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. Появляется универсальный «Редактор коллекции», подобный тому, который используется для столбцов и групп в элементе управления списком. Я могу ввести данные, как ожидалось. Однако, когда я нажимаю ОК, данные исчезают.

Мне потребовалось больше часа, чтобы понять, что мне действительно нужно создать экземпляр моего поля: 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. Удаление файла 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.iserializable.aspx

(Я также пытался реализовать ISerializable и переопределить GetObjectData с помощью

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

тоже не помогло (я также пробовал имена свойств вместо имен полей))

Извините за написание этого, как плохой роман ужасов кстати.

Ответы [ 3 ]

14 голосов
/ 16 сентября 2011

То, что вы хотите, это поддержка времени разработки с сериализацией 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);
}

Теперь ваша собственность будет выделена жирным шрифтом только тогда, когда она не пуста. Но я отвлекся, это был не вопрос:)

3 голосов
/ 19 февраля 2014

Идеальный пример:

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; }
    }
0 голосов
/ 13 августа 2015

Просто измените Collection<> на List<>

...