Перечисление флагов с проблемой нескольких нулевых значений (TextFormatFlags) - PullRequest
2 голосов
/ 07 декабря 2009

При попытке написать пользовательский элемент управления я столкнулся с проблемой с перечислением System.Windows.Forms.TextFormatFlags в сочетании с редактором Visual Studio (2005/2008). Причина этой проблемы, кажется, кроется в том факте, что в этом перечислении есть несколько членов, которые отображаются на нулевое значение. Выбор любого из этих элементов (GlyphOverhangPadding, Left, Default, Top) приводит к тому, что редактор устанавливает для свойства значение

this.customControl.TextFormatFlags = System.Windows.Forms.TextFormatFlags.GlyphOverhangPadding;

Код компилируется, как и ожидалось. Однако выбор любого ненулевого элемента (например, «Right») из сетки свойств редактора приводит к следующему:

this.customControl.TextFormatFlags = System.Windows.Forms.TextFormatFlags.Left, Default, Top, Right;

Очевидно, это не компилируется. Выбор нескольких ненулевых элементов (с помощью UITypeEditor, например, «Right | Bottom») приводит к следующему:

this.customControl.TextFormatFlags = ((System.Windows.Forms.TextFormatFlags)((System.Windows.Forms.TextFormatFlags.Left, Default, Top, Right | System.Windows.Forms.TextFormatFlags.Left, Default, Top, Bottom)));

Как видите, редактор добавляет три из четырех элементов с нулевым значением к любому выбранному элементу.

Если вы хотите воспроизвести эту проблему:

  • Создание нового проекта в Visual Studio 2005/2008 (приложение Windows Forms)
  • Добавить пользовательский элемент управления в проект.
  • Добавление частного поля и публичной собственности в новый класс:

    private TextFormatFlags tff = TextFormatFlags.Default;

    public TextFormatFlags TFFProperty { get {return this.tff; } set {this.tff = value; } }

  • Скомпилируйте код

  • Откройте Form1 в конструкторе и добавьте в него CustomControl1
  • Код прекрасно компилируется
  • Теперь откройте свойства CustomControl1 в редакторе PropertyGrid
  • Вы должны увидеть TFFProperty в разделе «Разное»
  • Свойство предлагает несколько значений, большинство из которых содержат запятую.
  • Выбор любого из значений через запятую (например, «Left, Default, Top, HorizontalCenter») приводит к некомпилируемому коду

То же самое происходит, если вы создаете свое собственное перечисление с атрибутом Flags и добавляете более одного члена, сопоставленного нулю (что является разновидностью перечисления неправильно сформированных флагов?) Я убедился, что это не ошибка с UITypeEditor, который я использую (та же проблема возникает без использования UITypeEditor). Я пытался обойти проблему с конвертером, но пока безуспешно. Если у кого-нибудь есть идеи, как решить эту проблему, я буду рад их услышать.

1 Ответ

3 голосов
/ 07 декабря 2009

Я проверил различные классы в пространстве имен System.ComponentModel.Design.Serialization с помощью Reflector, и я думаю, что сериализатор CodeDom немного капризный.

Перечисления обрабатываются EnumCodeDomSerializer.Serialize, цель которого - взять перечисление и превратить его в System.CodeDom.CodeExpression объект, представляющий то, что вы видите в файле конструктора.

Этот метод правильно использует CodeBinaryOperatorExpression для обработки | аспекта выражения. Однако для отдельных значений перечисления он использует Enum.ToString через EnumTypeConverter и вставляет полученную строку непосредственно в дерево выражений.

Я думаю, Enum.ToString является конечной причиной того, что вы видите:

Если несколько членов перечисления имеют одинаковое базовое значение, и вы пытаетесь получить строковое представление имени члена перечисления на основе его базового значения, ваш код не должен делать никаких предположений о том, какое имя метод возвратит.

По общему признанию, страница MSDN на Enum.ToString не говорит о запятых, но все же кажется небезопасным полагаться на вывод Enum.ToString, являющийся допустимым выражением C #.

Я не уверен, что это значит для вашего контроля:

  • Очевидно, что вы можете определить собственную замену для TextFormatFlags и сделать это без дублированных нулевых флагов
  • Возможно, вы сможете взломать его с помощью пользовательского конвертера типов, возможно, конвертирующего в InstanceDescriptor. Это дает вам немного больше контроля над тем, что появляется в сгенерированном дизайнером коде.
  • Возможно, вы можете выставить int для конструктора сериализатора, но TextFormatFlags для сетки свойств

Редактировать: Поведение списка разделенных запятыми Enum.ToString фактически задокументировано

...