Разработка элемента управления для Конструктора разметок

Docsvision предлагает разработчикам возможность создавать собственные элементы управления и использовать их при проектировании разметки карточки в Конструкторе разметок.

Для разработки элементов управления, совместимых с Конструктором разметок, требуется лицензия на компоненты (WinForms) DevExpress.

Элемент управления, совместимый с Конструктором разметок — это сборка, включающая в себя три обязательных компонента:

  • Собственно элемент управления, который предоставляет графический интерфейс и необходимую бизнес-логику.

  • Класс-контейнер, который обеспечивает совместимость с Конструктором разметок, предоставляет необходимые для Конструктора разметок свойства, обеспечивает сохранение настроек элемента управления, а также передачу их значений в элемент управления.

  • Контейнер расширения, содержащий информацию об элементах управления, реализованных в сборке (в одной сборке может быть определено несколько элементов управления).

В процессе загрузки разметки (в карточке, либо в Конструкторе разметок) механизмы Конструктора разметок обращаются с зарегистрированными сборкам в поисках класса контейнера расширения. Если в сборке обнаружен класс данного типа — вызывается его метод GetControlExtensions, который предоставляет данные о типах реализованных элементов управления. Каждая запись, возвращаемая методом GetControlExtensions, обязательно содержит тип класса-контейнера, а также может содержать типы вспомогательных сущностей, которые реализуют механизмы настройки элемента управления.

После создания компонента карточки инициализируются элементы управления с загрузкой в них данных и настроек.

Далее рассматривается создание элемента управления на примере разработки простого списка, в который должны быть загружены подразделения из Справочника сотрудников.

Полный код проекта можно скачать по ссылке.

  1. Создать новый проект типа Windows Forms Class Library.

  2. В проект добавить элемент XtraUserControl, в котором будет реализован пользовательский интерфейс элемента управления.

    Вместо XtraUserControl можно использоваться стандартный UserControl.

  3. Реализовать обязательный интерфейс IPropertyControl, в котором определены ключевые функциональные возможности элемента управления: получение контекста объектов, установка и возвращение значения элемента управления, а также его базовые настройки, и др.

    public partial class RealPropertyControl : XtraUserControl, IPropertyControl
    {
     private IStaffService staffService; (1)
    
     public RealPropertyControl()
     {
      this.InitializeComponent();
     }
    
    (2)
    
     public bool Hierarchy (3)
     {
      get;
      set;
     }
    
     public void LoadData() (4)
     {
      if (ObjectContext = null) return; (5)
    
      staffService = ObjectContext.GetService<IStaffService>();
      ComboBoxItemCollection coll = staffBox.Properties.Items;
    
      coll.BeginUpdate();
      foreach (var item in staffService.GetUnits(null, true, Hierarchy))
      {
       coll.Add(new StaffBoxItem(ObjectContext.GetObjectRef(item).Id, item.Name));
      }
      coll.EndUpdate();
     }
    
     private void staffBox_SelectedIndexChanged(object sender, EventArgs e) (6)
     {
      if (staffBox.SelectedIndex = -1) ControlValue = null;
       else ControlValue = (staffBox.SelectedItem as StaffBoxItem).Id;
      ControlValueChanged(this, e);
     }
    }
    1 Сервис для работы со Справочником сотрудников.
    2 Реализация интерфейса IPropertyControl.
    3 Отображать в списке все подразделения или только подразделения верхнего уровня.
    4 Загрузка данных из Справочника сотрудников в элемент управления.
    5 Если контекст объектов не был передан, то загрузить данные не получится.
    6 Выбор значения в элементе управления.

    Приведенный выше код представляет собой часть содержимого класса RealPropertyControl. Метод LoadData, загружающий данные из Справочника сотрудников, вызывается только после присвоения значения контексту объектов (свойство ObjectContext), таким образом можно быть уверенным, что при загрузке данных будет доступное соединение с сервером Docsvision.

    Загрузка данных может быть выполнена иным образом, к примеру при обращении пользователя или после изменения видимости элемента управления. Последний способ сработает, если элемент управления расположен на вкладке, не отображаемой при открытии карточки.

    При загрузке данных учитывается значение свойства Hierarchy, которое может быть определено в настройках элемента управления в Конструкторе разметок. Данное свойство получает значение из класса-контейнера (будет реализован далее).

  4. Реализовать графический интерфейс пользователя для элемента управления. В данном примере элемент управления представляет собой простой (DevExpress) ComboBoxEdit, который может быть заменён почти без последствий на (WinForms) ComboBox.

  5. Создаем класс, реализующий контейнер для создаваемого элемента управления, который делает данный компонент доступным для Конструктора разметок.

    public class RealLayoutItem : FixedLayoutControlItem<RealPropertyControl>
    {
     private bool hierarchy;
    
     public override string ItemTypeName (1)
     {
      get
      {
       return "Собственный элемент управления";
      }
     }
    
     public override System.Drawing.Image CustomizationImage (2)
     {
      get
      {
       return SampleControl.Properties.Resources.ButtonIcon.ToBitmap();
      }
     }
    
     public override LayoutsPropertyType PropertyType (3)
     {
      get { return LayoutsPropertyType.DepartmentReference; } (4)
     }
    
     public override FieldType[] GetSupportedFieldTypes() (5)
     {
      return new FieldType[]
      {
       FieldType.RefId (6)
      };
     }
    
     public override Control Control (7)
     {
      get
      {
       return base.Control;
      }
      set
      {
       base.Control = value;
       if (value != null)
       {
        this.PropertyControl.Hierarchy = hierarchy;
       }
      }
     }
    
     [XtraSerializableProperty] (8)
     public bool Hierarchy
     {
      get
      {
       if (base.PropertyControl != null)
        return base.PropertyControl.Hierarchy;
       return hierarchy;
      }
      set
      {
       if (this.PropertyControl != null)
        this.PropertyControl.Hierarchy = value;
       hierarchy = value;
      }
     }
    }
    1 Возвращает название элемента управления, отображаемое в Конструкторе разметок.
    2 Возвращает иконку для элемента управления, отображаемую в Конструкторе разметок.
    3 Возвращает тип данные элемента управления, который используется при преобразовании для элемента управления его значения по умолчанию.
    4 В данном случае — ссылка на подразделения.
    5 Возвращает список типов полей, с которыми работает элемент управления.
    6 В данном случае — ссылочное поле.
    7 При установке элемента управления передаем настройки в него.
    8 Обеспечение передачи значения свойства в класс элемента управления.

    Класс-контейнер наследуется от типа FixedLayoutControlItem<T>, где T — тип контейнера элемента управления (был разработан ранее), и в самом простом случае должен содержать только переопределение свойства PropertyType. Свойство PropertyType должно возвращать тип данных элемента управления, что требуется для присвоения значения по умолчанию при создании новой карточки, в которой используется элемент управления.

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

    Для этого в класс добавляются свойства, аналогичные реализуемым настройкам с соответствующими типами. Свойства должны быть отмечены атрибутом XtraSerializableProperty. Для передачи значения настройки в элемент управления, переопределяется свойство Control, в котором присваивается значение свойству.

    Помимо указанных функций, в класс-контейнере можно указать название элемента управления (иначе будет использовано название класса), отображаемое в Конструкторе разметок, а также его иконку. Помимо этого переопределить метод GetSupportedFieldTypes, который предоставляет список типов полей. С этими полями может работать элемент управления.

  6. Реализуем класс-обертку, предоставляющий Конструктору разметок дополнительную информацию о дополнительных настройках элемента управления. В данном примере реализует единственное дополнительное свойство, определяющее логику загрузки подразделений из Справочника сотрудников. Класс должен наследовать от типа SpecialPropertyWrapper<T>.

    В параметре типа указывается класс-контейнер, реализованный ранее:
    public class RealWrapper : SpecialPropertyWrapper<RealLayoutItem>
    {
     [Category("Дополнительные настройки"), DisplayName("Все подразделения"), Description("Выводить все подразделения или только первый уровень")]
     [TypeConverter(typeof(BooleanTypeConverter))]
     public bool Hierarchy
     {
      get {
       return this.Item.Hierarchy;
      }
      set
      {
       this.Item.Hierarchy = value;
      }
     }
    }
    Свойство помечается атрибутами, определяющим его название и категорию, в которой оно размещается:
    • Category — категория свойств, в которой размещается собственная настройка.

    • DisplayName — название свойства, отображаемое в Конструкторе разметок.

    • Description — дополнительное описание.

      Также здесь установлен атрибут конвертера (TypeConverter), который формирует из значения свойства текстовое обозначение, отображаемое в Конструкторе разметок:

      internal sealed class BooleanTypeConverter : BooleanConverter
      {
       public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destType)
       {
        return (bool)value ? "Да" : "Нет";
       }
      
       public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
       {
        return string.Compare((string)value, "Да", StringComparison.OrdinalIgnoreCase) = 0;
       }
      }
  7. Создать обязательный класс, унаследованный от ControlExtensionInfoPackage, который возвращает список всех элементов управления, реализованных в сборке:

    public sealed class ExtensionPackage : ControlExtensionInfoPackage
    {
     public override ControlExtensionInfo[] GetControlExtensions() (1)
     {
      return new ControlExtensionInfo[]
      {
       new ControlExtensionInfo(typeof(RealLayoutItem), typeof(RealWrapper))
      };
     }
    }
    1 Переопределяем единственный метод, возвращающий список элементов управления.

    Конструктор ControlExtensionInfo принимает тип контейнера элемента управления, а также может принимать, как в приведенном коде, тип обертки для свойств и тип формы, реализующей страницу настроек.

  8. После получения готовой сборки, её необходимо зарегистрировать на всех компьютерах в ветке реестра:

    • HKEY_CURRENT_USER\Software\DocsVision\BackOffice\5.5\Client\PropertyControls — для текущего пользователя.

    • HKEY_LOCAL_MACHINE\Software\DocsVision\BackOffice\5.5\Client\PropertyControls — для всех пользователей.

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

      Ветка реестра может отличаться от приведенной при отличной разрядности операционной системы.

В случае успешной реализации и регистрации элемента управления в список элементов управления Конструктора разметок будет добавлен реализованный компонент. Чтобы компонент отобразился в конструкторе разметок, необходимо перезапустить Windows-клиент.

Созданный компонент можно использовать для создания интерфейса карточки:

Собственный элемент управления в общем списке
Рисунок 1. Собственный элемент управления в общем списке

Указанный элемент управления имеет дополнительную настройку, определяющую вариант загрузки списка подразделений из Справочника сотрудников.

Настройка доступна из свойства элемента управления:

Свойства элемента управления
Рисунок 2. Свойства элемента управления