Выборка#

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

Выборка определяет:

  • Способ получения данных

  • Способ отображения данных пользователю

  • Бизнес логику обработки пользовательских действий

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

Пользовательский интерфейс приложения является совокупностью экземпляров отображений выборок.

Отображения#

Отображение – группирует бизнес логику выборки в зависимости от способа представления данных.

Стандартные отображения для класса:

  • Default – отображение по умолчанию
    В данном отображении определена функциональность общая для данных выборки. Остальные отображения выборки обычно наследуют это отображение.

  • List – отображение для списка объектов класса

  • Card – отображение для карточки объекта

  • и т.д.

Отношение экземпляров выборок#

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

Корнем выборок экземпляра приложения – является выборка главного меню. Все остальные выборки являются подчиненными к ней.

Выборка являющаяся корнем формы имеет доступ к данным главного меню. Экземпляры выборок внутри формы также выстраиваются в иерархии мастер-деталь, в соответствии с бизнес логикой отображения.

Доступность параметров главных выборок в подчиненных выборках#

Получение значения параметра осуществляется с помощью метода getVar() и преобразованием результата к нужному типу.

Иерархическая структура создаваемых объектов (выборок) позволяет обеспечивать доступность и передачу параметров по отношению «мастер-деталь», а также производить автоматическое обновление при изменении используемых параметров. Параметры экземпляров выборок – это, прежде всего, атрибуты (поля) их датасета. Также это могут быть дополнительно созданные параметры, или специальные параметры фреймов.

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

Обращение к параметру непосредственного мастера осуществляется прибавлением к имени параметра префикса super$. При этом, если необходимо обратиться к параметру мастер-выборки своей собственной мастер-выборки (т.е. перепрыгнуть через один экземпляр выборки), необходимо использовать префикс super$super$. В случае, если при любом из обращений к параметру он не будет найден, произойдет автоматический поиск параметра по дереву отношений «мастер-деталь» выборок вплоть до тех пор, пока мы не найдем этот параметр, или не дойдем до главной выборки приложений. Поэтому, при создании глобальных параметров на уровне главной выборки приложения, для них имеет смысл давать уникальное имя, а затем обращаться к ним из любого места приложения используя один единственный префикс super$.

Пример получения строкового параметра из мастер-выборки:

getVar("super$sCaption").asNString

Передача параметров в выборку#

При открытии выборки, в нее можно передать карту с дополнительными параметрами, используя метод params().

Пример кода:

Bs_GoodsAvi.defCard.newForm().params(Map(CardRep.IdItemSharp -> idvGds, CardRep.EditingType -> EditingType.edit)).open()

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

Открытие выборок в различных режимах#

При открытии выборки в любом из режимов:

  • newForm().open()

  • newForm().openModal()

  • newForm().openLookup()

необходимо учитывать, что родителем для открывшейся выборки будет являться ***_MainMenu, а сама открывшаяся выборка будет главной формой (selection.isMainOnForm = true). Поэтому открывать выборки, в которых установлены зависимости от параметров ("super$...") родительской выборки, без самой родительской выборки нельзя, тк никаким способом в открывающуюся выборку не получится передать подобные параметры.

Разметка выборки#

Фреймворк использует систему декларативной разметки выборок. Это позволяет ускорить разработку визуальных форм т.к. разработчику не требуется заниматься низкоуровневым программированием.

Разметка выборки задает правила отображения данных выборки и ее дочерних элементов в xml файле, с расширение .avm.xml.

Ключевые элементы разметки:

  • Отображения

  • Компоновка формы

  • Фрейм
    Задает тип представляемых данных (список, дерево и тд.)

  • Атрибуты

  • Операции

  • Фильтры

Разметка карточки. Контейнеры#

Существует два режима разметки отображения:

  1. Разметка на основе настроек атрибутов - старый режим разметки, основанный на порядковом номере и поле isLastInLine атрибутов.

  2. Разметка на основе контейнеров - новый режим, основанный на разметке с помощью контейнеров, указанных внутри тэга <card>. \

Разметка атрибутов может быть задана с помощью следующих контейнеров:

  • hBox горизонтальный контейнер: объекты внутри располагаются друг за другом горизонтально.

  • vBox - вертикальный контейнер: объекты внутри располагаются друг за другом вертикально.

  • vGroup - группа объединения: по сути тот же вертикальный контейнер, но визуально обводит содержимое рамкой и может иметь наименование.

  • vSection - сворачиваемая секция: вертикальный контейнер, который имеет наименование и функцию сворачивания, которая скрывает содержимое. Можно указать, как по умолчанию будет отображаться секция - свернутой или развернутой.

Контейнеры могут быть вложены друг в друга.

Пример разметки с помощью контейнеров:

<representation editMode="edit" name="Card" stdFilter.isAvailable="false">
        <layout>
            <simpleComposer>
                <frame filter.isVisible="false">
                    <card>
                        <layout>
                            <vBox>
                                <hBox>
                                    <vBox>
                                        <attr name="idCustomerHL"/>
                                        <attr name="dOutDate"/>
                                    </vBox>
                                    <vBox>
                                        <attr name="gidSrcDoc" visible="true"/>
                                        <attr name="nOrder"/>
                                    </vBox>
                                </hBox>
                                    <vGroup caption="Приемка">
                                        <attr name="idCustomerHL"/>
                                        <attr name="dExecuteDate"/>
                                    </vGroup>
                            </vBox>
                            <vSection caption="Примечание" collapsed="false">
                                <attr name="sComment" caption="Комментарий"/>
                            </vSection>
                        </layout>
                    </card>
                </frame>
            </simpleComposer>
        </layout>
    <attributes/>
<operations/>
</representation>

Использование шаблонов в разметке.#

Файлы разметки поддерживают язык шаблонов Thymeleaf. Шаблоны позволяют вставлять в выборку повторно используемые фрагменты. К примеру использовать общий фильтр в разных отображениях.

Для использование шаблонов языка шаблонов необходимо подключить в выборку пространство имен: xmlns:th="http://www.global-system.ru/xsd/global3-view-template-1.0"

Наследование разметки#

Для наследования разметки другой выборки необходимо:

  1. Подключить пространство имен языка шаблонов

  2. Добавить к тегу view указание о наследовании другой разметки th:extends="Some_Other.avm.xml"

Полный пример xml с наследованием:

<view xmlns="http://www.global-system.ru/xsd/global3-view-1.0"
      xmlns:th="http://www.global-system.ru/xsd/global3-view-template-1.0"
      name="RplTst_AllDbTypes" th:extends="RplTst_AllDbTypes.dvm.xml">
  <representation name="Default"/>
</view>

Алгоритм наследования выборки:

  1. При отсутствие скопировать отображения предка в наследника
    Копирование происходит в случаи если в наследники нет данного отображения

  2. Произвести наследование отображений
    Наследование производится в случаи если и в предке и в потомке есть отображение с тем же именем.

  3. Выполнить оставшиеся команды языка разметки

Алгоритм наследования отображений:

  1. При отсутствие скопировать

    • levelGroups

    • bandGroups

    • layout

    • filter

  2. Добавить отсутствующие атрибуты

  3. Добавить отсутствующие операции

Операции#

Выборка взаимодействует с программным окружением системы с помощью операций. Операция – это некая обработка того или иного события, вызванного пользователем или каким-то процессом. Операции бывают служебными, выполняя логику обработки событий, или пользовательскими, срабатывающими в результате явного действия пользователя.

Классификация операций#

  • Базовые операции
    Операции, предназначенные для работы с набором данных. Они отвечают за добавление, удаление, обновление записей, получения данных, навигацию по датасету и т.п. Эти операции, как правило, уже определены на уровне базовых выборок-предков. Некоторые из этих операций срабатывают автоматически на выполнение определенных пользовательских действий (BeforeEdit при начале редактирования, AfterEdit при завершении, CheckWorkAbility при перемещении с записи на запись)

  • Служебные операции
    Операции, выполняющие строго определенные вспомогательные действия

  • Системные операции
    Операции, создающиеся автоматически

  • Сеттеры
    Операции предназначены для изменения значений атрибутов (полей) выборки. При вводе значения в какое-то поле формы вызывается связанная с данным полем операция (сеттер), которая по умолчанию вызывает серверные методы изменения значений атрибутов класса. Сеттеры для атрибутов класса создаются автоматически.

  • Операции фильтров
    Операции, необходимые для работы с фильтрами.

  • Пользовательские операции Операции, создаваемые разработчиком самостоятельно и позволяющие осуществлять любой набор необходимых операций, не только осуществляемых над данными, но и являющихся целиком клиентскими (например, вызов другой формы, сообщения и т.п.).

Базовые операции выборки#

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

onRefresh#

Операция, загружающая данные в датасет. При повторном вызове производит обновление всего набора данных (refresh).

onRefreshItem#

Операция обновления одной (текущей) записи.

onRefreshExt#

Операция с дополнительным запросом к базе для получения полей-заголовков ссылочных объектов. Вызывается только для объектных запросов в onRefresh/onRefreshItem.
Для использования в запросе атрибутов класса необходимо передать их в блоке with. При этом обязательно для НЕ Long атрибутов нужно явно указывать тип в аннотации /*@...*/: \

  • Для Varchar атрибутов - «String», «NString» или «varchar», например - /*@NString*/

  • Для Number атрибутов - «Number», например - /*@Number*/

В противном случае атрибуты попытаются преобразоваться к bigint типу.

beforeEdit#

Операция, выполняемая перед началом редактирования записи для выполнения подготовительных действий перед началом редактирования. Автоматически выполняется в момент начала внесения изменений в объект.

afterEdit#

Операция финальной проверки ввода записи. Срабатывает автоматически при попытке перейти с отредактированной записи на другую запись выборки или при закрытии выборки.

insert#

Операция вставки новой записи в выборку.

delete#

Операция удаления записи.

checkWorkAbility#

Операция, предназначенная для осуществления проверок изменения состояния операций выборок, доступности редактирования ее атрибутов. Вызывается автоматически при переходе с записи на запись, при открытии выборки, а также после выполнения операции, для которой выставлен флаг «выполнять операцию применимости после выполнения операции».

onLoadMeta#

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

onUnloadMeta#

Операция, срабатывающая при выгрузке метаданных.

beforeOpen#

Операция, срабатывающая перед открытием выборки. Данная операция рекомендуется для создания дополнительных параметров, используемых в выборке.

afterOpen#

Операция, срабатывающая сразу после открытия выборки. Необходимо использовать для переопределения наименований столбцов выборки и т.д.

onShow#

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

onControllerCreated#

Операция, срабатывающая сразу после того, был создан фрейм отображения выборки. Эта операция аналогична OnShow, но срабатывает в том числе и при открытии интерфейсных элементов формы, которые были скрыты при открытии формы (например, закладки, которые не были первыми в перечне закладок).

Служебные операции выборки#

saveForm#

Операция сохраняет данные формы.

cancelForm#

Операция отменяет изменения на форме.

closeFormOk#

Операция закрытия формы с подтверждением выбора

closeFormCancel#

Операция закрытия формы по кнопке «Выход» или по кнопке закрытия окна «крестик»

beforeCloseForm#

Операция срабатывает перед закрытием формы.

afterCloseForm#

Операция срабатывает после закрытия формы.

onCloseFormQuery#

Операция вызывается в начале процесса закрытия формы и позволяет отменить закрытие.

Предопределенные операции выборок#

applyUniFilter#

Операция применяет настроенные условия фильтрации, выполняется перезапрос данных с наложенными условиями фильтрации.

resetUniFilter#

Операция отменяет все наложенные на выборку фильтры

clearUniFilter#

Операция отчищает все условия фильтрации.

showAuditObject#

Операция вызывает окно аудита выполняемых пользователями действий в БД, с наложенным фильтром по текущему объекту.

showAboutObject#

Операция открывает окно системной информации об объекте.

copyObject#

Операция копирования объекта

cardEdit#

Операция открытия объекта в карточке, предварительно запросив, какую использовать выборку и отображение.

allowEdit#

Разрешение/запрещение редактирования объектов в списке. По умолчанию операция доступна для редактируемых списков.

showTab#

Операция открытия/закрытия детальной части

Сеттеры изменения атрибутов#

Операции установки значений атрибутов (так называемые сеттеры) используются для того, чтобы установить определенное значение в поле записи объекта, соответствующее его типу. Имя сеттера состоит из префикса set и имени атрибута attributeName: setattributeName.

Специальные операции контролов#

onFocusedFieldChanged#

Это специальная операция, срабатывающая после перехода фокуса ввода в списке или дереве с одного столбца на другой, а также при переходе с контрола на контрол в карточке или панели фильтров.

onFocusedCellChanged#

Это специальная операция, срабатывающая после перехода фокуса ввода в списке с одной ячейки на другую.

onFrameActivated#

Специальная операция, срабатывающая при переходе фокуса ввода в грид, дерево, карточку.

Способы визуализации операций#

Существует 3 основных способа визуального отображения операций:

  • На тулбаре фрейма

  • В выпадающем (контекстном) списке (popup меню)

  • В качестве пункта главного меню (доступно только для выборок приложений главного меню)

Для настройки отображения операции каждым из способов существует собственная настройка при помощи соответствующих свойств операции.

Диалоги#

Диалоги позволяют задавать вопросы пользователю и получать от него ответы на эти вопросы.

Для доступа к методам диалогов используется специальный объект ru.bitec.app.gtk.gl.Dialogs, который доступен в Avi через переменную dialogs.

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

Стандартные диалоги#

showMessage#

Выводит на экран текст в модальном окне.

Пример использования:

dialogs.showMessage("Текст, который увидит пользователь")

showMsgDialog#

Открывает диалог указанного типа с сообщением и указанным перечнем кнопок.

Возможные типы диалогов см. в ru.bitec.app.gtk.gl.msgdlg.MsgDlg

Возможные типы кнопок см. в ru.bitec.app.gtk.gl.msgdlgbutton.MsgDlgButtong

Пример использования:

val dlgRes = dialogs.showMsgDialog(
  //тип диалога "Подтверждение"
  MsgDlg.confirmation,
  //текст диалога
  "Включить автонумерацию?",
  //список кнопок: Да, Нет
  List(MsgDlgButton.yes,MsgDlgButton.no)
)
if (dlgRes == MsgDlgButton.yes) {
  //обработка нажатия на кнопку "Да"
} else if (dlgRes == MsgDlgButton.no) {
  //обработка нажатия на кнопку "Нет"
}

showButtonsDialog#

Открывает диалог с произвольными кнопками.

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

Пример использования:

dialogs.showButtonsDialog(
  caption = "",
  text = "Удалить форму или убрать ссылку на вид отчетности?",
  colCount = 2,
  buttons = List(List("Удалить"), List("Убрать ссылку")),
  imageCollectionName = "ToolBarPrimaryHot",
  focusButtonNumber = 0L
) match {
  case 0L =>
    //обработка нажатия кнопки "Удалить"
  case 1L =>
    //обработка нажатия кнопки "Убрать ссылку"
}

showPromptDialog#

Открывает диалог с запросом ввода строки.

Пример использования:

val svDialogRes = dialogs.showPromptDialog("Заголовок окна", "Текст диалога", "Значение по умолчанию").ns

if (svDialogRes.isNotNullOrEmpty) { 
  //обработка введенного значения
}

showConfirmDialog#

Открывает диалог с типом «Подтверждение» и кнопками «Да» и «Нет»

Пример использования:

val dialogRes = dialogs.showConfirmDialog("Текст диалога")
if (dialogRes){ 
  //обработка случая, когда пользователь нажал "Да"
}

showInfoForm#

Отображает информационное сообщение с переданным текстом, без возможности его закрытия пользователем.

Скрывается при вызове метода hideInfoForm.

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

Пример использования:

dialogs.showInfoForm("Внимание, идет загрузка Госреестра типов СИ")
try {
  //продолжительное действие
  thisApi().download()
} finally {
  dialogs.hideInfoForm()
}

hideInfoForm#

Скрывает информационное сообщение, см. описание метода showInfoForm

withInfoForm#

Метод включает в себя открытие информационного окна, и его закрытие после завершение выполнения переданной функции.

Пример из описания метода showInfoForm, можно переписать используя этот метод:

dialogs.withInfoForm("Внимание, идет загрузка Госреестра типов СИ") {
  //продолжительное действие
  thisApi().download()
}

showEditPaintStyleDialog#

Вызывает диалог настройки стилей раскраски текстовых полей.

Форма в режиме выбора значения#

Одной из типичных задач является предложить выбрать пользователю какой-либо объект и затем сделать обработку его выбора. Например, чаще всего ссылочные поля открывают для выбора список объектов ссылочного класса, а после выбора пользователем значения устанавливают его в текущей форме.

Для реализации такого типа диалогов используется синтаксис открытия формы в режиме openLookup.

Пример открытия формы и обработки выбора пользователя:

//создаем новую форму
val data = Btk_ClassAvi.listForChoose().newForm()
  //указываем список полей, которые требуется получить из формы
  .results("id" :: "sCaption" :: Nil)
  //открываем форму в режиме выбора
  .openLookup()

//проверяем, что пользователь подтвердил выбор
if (data.getLookupResult eq LookupResult.ok) {
  //получаем значение первого поля из списка results
  val id = data.getData(1, 0)
  //получаем значение второго поля из списка results 
  val sCaption = data.getData(1, 1) 
}      

Выбор нескольких строк (мультиселект)#

Для того, чтобы в форме в режиме выбора пользователь мог выбрать несколько строк используется опция useMultiSelect.

Пример открытия формы в режиме мультиселекта и обработка выбранных значений:

//создаем новую форму
val data = Btk_ClassAvi.listForChoose().newForm()
  //указываем список полей, которые требуется получить из формы
  .results("id" :: "sCaption" :: Nil)
  //указываем возможность выбора нескольких строк
  .useMultiSelect
  //открываем форму в режиме выбора
  .openLookup()

//проверяем, что пользователь подтвердил выбор
if (data.getLookupResult eq LookupResult.ok) {
  //цикл по выбранным строкам
  for (i <- 1 to data.size) {
    //получаем значение первого поля из списка results для строки i
    val id = data.getData(i, 0)
    //получаем значение второго поля из списка results для строки i 
    val sCaption = data.getData(i, 1) 
  }  
}      

Фрейм#

Фреймы — это средство представления информации в выборке.

Существующие фреймы по способу представления информации можно разделить на несколько видов:

  • для отображения информации в виде карточки объекта

  • для отображения списка объектов в табличной форме

  • для отображения списка объектов в виде древовидной структуры

  • для отображения инфографики

  • специализированные (для отображения графической информации, доступа к файлам и т.д.)

Основные типы фреймов#

grid#

Фрейм для вывода списка объектов класса в табличной форме

tree#

Фрейм для вывода списка объектов класса в древовидной форме

card#

Фрейм для вывода всех атрибутов конкретного объекта (Карточка)

tab#

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

memo#

Фрейм для ввода большого количества текстовой информации (Memo)

html#

Фрейм, отображающий HTML

image#

Изображение

gantt#

Фрейм, для построения диаграмм Ганта.

olap#

Фрейм для построения сводных (OLAP) таблиц

bpmn#

Фрейм редактора диаграмм бизнес-процессов. Поддерживает диаграммы BPMN версии 2.0

gridPanel#

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

Основные типы редакторов#

button#

Редактор - Кнопка. При нажатии выполнится сеттер соответствующего атрибута.

buttonsEdit#

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

calendar#

Редактор - Календарь.

check#

Редактор - Чекбокс (Галка).

colorPick#

Редактор - Выбор цвета.

combo#

Редактор - Фиксированный выпадающий список.

currency#

Денежный редактор.

datePick#

Редактор даты.

dateTimePick#

Редактор даты и времени.

edit#

Однострочный редактор текста.

editButton#

Редактор: Редактор в строке с кнопкой.

hotKey#

Редактор ввода комбинации горячих клавиш.

icon#

Редактор - изображение. Используется для списка и дерева. Отображает изображение из коллекции изображений.

lookup#

Редактор - Выпадающий список по запросу. Источником элементов могут являться: выборка или SQL-запрос, указанные в свойствах.

memo#

Многострочный редактор. Предназначен для редактирования многострочных текстовых значений без разметки.

tagLookup#

Редактор - Выпадающий список по запросу с возможностью множественного выбора.

timePick#

Редактор времени.

editPassword#

Редактор ввода пароля.

imageCollection#

Редактор - выпадающий список изображений.

Мультиселект#

Мультиселект - возможность выбрать несколько строк в списках или деревьях.

Для включения мультиселекта в отображении укажите в теге grid или tree свойство isMultiSelectEnabled="true".

Пример разметки:

<representation name="List">
  <layout>
    <simpleComposer>
      <frame>
        <grid isMultiSelectEnabled="true"/>
      </frame>
     </simpleComposer>
  </layout>
</representation>

Пример обработки выделенных строк пользователем и получения значений атрибутов этих строк:

//цикл по выделенным строкам
for (i <- 0 until selection.selectedRecordsCount()) {
  //получение значения поля с типом NLong
  val id = NLong.fromAny(selection.selectedValueByName("id", i))
  //получение значения поля с типом NNumber
  val nOrder = NNumber.fromAny(selection.selectedValueByName("nOrder", i))
  //получение значения поля с типом NGid
  val gid = NGid(selection.selectedValueByName("gid", i).asInstanceOf[String])
  //получение значения поля с типом NString
  val sCaption = selection.selectedValueByName("sCaption", i).asInstanceOf[String].ns
  //получение значения поля с типом NDate
  val dDate = NDate(selection.selectedValueByName("dDate", i).asInstanceOf[JDate])
}

Примечание

Для формы в режиме выбора мультиселект регулируется опцией useMultiSelect при создании формы. Подробнее в главе Диалоги

Настройка стилей#

Стиль - набор свойств, включающий в себя:

  • Color - Цвет заднего фона в формате $00BBGGRR или Global константой цвета.

  • FontColor - Цвет шрифта в формате $00BBGGRR или Global константой цвета.

  • FontSize - Размер шрифта натуральным числом.

  • FontItalic - Курсив. Значение 0 - нет курсива, 1 - есть курсив.

  • FontBold - Жирность. Значение 0 - не жирный шрифт, 1 - жирный шрифт.

  • FontUnderLine - Подчеркивание. Значение 0 - нет подчёркивания шрифта, 1 - есть подчёркивание шрифта.

  • FontStrikeOut - Перечеркивание. Значение 0 - нет перечеркивания шрифта, 1 - есть перечеркивание шрифта.

Стиль задаётся строкой формата:

"param1=value1;param2=value2;param3=value3;...;paramn=valuen"

Пресеты стилей хранятся в классе Btk_Registry, их можно использовать для типовых задач.

Стили можно применять в отображении к строкам и столбцам.

Применение стиля к строке#

Чтобы применить стиль к строке, нужно в теге representation присвоить свойству rowStyleAttr имя атрибута, в котором будет храниться либо готовая строка со стилем, либо системное имя пресета.

<representation name="List" editMode="notEdit" rowStyleAttr="sStyle1">
        <attributes>
            <attr name="sCode" caption="Код" editorType="edit" order="10" isRequired="true"/>
            <attr name="sCaption" caption="Наименование" editorType="edit" order="20" isRequired="true"/>
            <attr name="sStyle1" caption="Первый стиль" editorType="edit" order="40"/>
        </attributes>
    </representation>

Применение стиля к столбцу#

Чтобы применить стиль к столбцу, нужно в теге attr создать тег style и в нём использовать либо свойство name, либо attr. В свойстве name указывается непосредственно строка стиля или системное имя пресета, в attr указывается атрибут, где хранится либо строка, либо системное имя.

<representation name="List" editMode="notEdit">
        <attributes>
            <attr name="sCode" caption="Код" editorType="edit" order="10" isRequired="true">
                <style name="Color=$00880000"/>
            </attr>

            <attr name="sCaption" caption="Наименование" editorType="edit" order="20" isRequired="true">
                <style name="BDG_URTotalRow"/>
            </attr>
            <attr name="sStyle1" caption="Первый стиль" editorType="edit" order="40">
                <style attr="sStyle1"/>
            </attr>
        </attributes>
    </representation>

Также стиль можно менять в avi используя selection

selection.attrs("Имя атрибута, на который мы хотим применить стиль").styleAttributeName = "Имя атрибута со строкой стиля или с системным именем пресета"

Создание строки стиля. StyleBuilder#

StyleBuilder - класс, позволяющий работать со стилем не как со строкой, а как с объектом, в котором хранятся параметры. Помимо этого в StyleBuilder хранятся цветовые и шрифтовые константы для удобства использования.

Внутри класса хранится изменяемый словарь, который хранит пары название параметра GS -> (значение параметра, название параметра CSS). Для его корректной работы есть вспомогательный неизменяемый словарь, хранящий пары название параметра GS -> название параметра CSS. Все манипуляции со словарём происходят через сеттеры и геттеры. Для создания результирующей строки тоже есть отдельный метод.

Изначально класс создан для стилей, которые используются в Global, но класс также поддерживает создание результирующей строки и для CSS.

Создание экземпляра StyleBuilder#

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

val builder = StyleBuilder()
builder.build() //Результирующая строка пустая 

так и взять за основу уже существующую строку стиля

val stringStyle = "color=$00FF4400;font=arial;fontsize=15".ns
val builder = StyleBuilder(stringStyle)
builder.build() //В результирующей строке будут все те же параметры c теми же значениями,
                //что и в stringStyle, но возможно параметры будут расположены в другом порядке.

Работа с цветовыми параметрами#

Для работы с цветовыми параметрами внутри StyleBuilder создан класс Color. Методы работы с цветовыми параметрами принимают на вход и возвращают экземпляры этого класса. На вход в сеттер можно подавать обычную строку типа String, она будет неявно преобразована в экземпляр Color.

При создании от строки Color пытается преобразовать цвет в HEX, если цвет невозможно преобразовать - бросается исключение. Color умеет преобразовывать строки типа:

  • $00BBGGRR

  • $BBGGRR

  • #RRGGBB

  • Наименование GS константы

Из Color можно получить строку обратно в формате HEX или GS кода:

val color = Color.Red //В Color есть константы Gs цветов
//ИЛИ
val color = Color("$000000FF")
//ИЛИ
val color = Color("$0000FF")
//ИЛИ
val color = Color("#FF0000")
//ИЛИ 
val color = Color("clRed")
//-------------------------------
color.getGsHex // Вернётся $000000FF
color.getHex // Вернётся #FF0000

Если в Color подать пустую строку - это считается как отсутствие цвета.

Примеры#

val style = StyleBuilder()
  .setBackgroundColor(Color.Blue)
  .setFontColor("#FFAAFF")
  .setFont(Font.Impact)
  .setFontUnderLine(1.nn)
  .build() // style = "color=$00FF0000;fontcolor=$00FFAAFF;fontunderline=1;fontname=Impact"

val style2 = StyleBuilder(style)
  .setBackgroundColor(Color("")) // Убрать параметр цвета заднего фона
  .setFontColor("$00FFFFFF")
  .setFont("") // Убрать параметр шрифта
  .setFontUnderLine(NNumber()) // Убрать параметр нижнего подчёркивания
  .setFontBold(1.nn)
  .buildCSS // style2 ="color: #FFFFFF;
            //          font-weight: bold;"

Жизненный цикл формы#

Жизненный цикл формы

Иконки#

Фреймворк поддерживает три варианта подключения иконок:

  1. Из ресурсов сервера (основной режим)
    Коллекции изображений берутся из каталога с ресурсами [G3_HOME]/resources/imagecollection.
    Каждый подкаталог является коллекцией, вложенные файлы - элементы коллекции, номера которых соответствуют именам файлов.

  2. Из базы данных
    Коллекции изображений загружаются из таблицы btk_component, поле clobdataxml

  3. Подключение иконки по url
    Для иконки указывается явный url

Использование иконок из java-ресурсов модуля#

Файлы изображений размещаются в ресурсных каталогах соответствующего модуля.

На скриншоте приведён пример структуры каталогов с ресурсами модуля BTK. Коллекции изображений размещены в каталоге ресурсов ../ru/bitec/app/btk/images. Каждой коллекции соответствует одноимённый каталог.

Рассмотрим структуру каталога ru/bitec/app/btk/images/toolbar.

  • В корне каталога размещаются файлы изображений с минимальным разрешением 16х16 точек.

  • В подкаталоге ru/bitec/app/btk/images/toolbar/24x24 размещаются файлы изображений с разрешением 24х24 точек. В случае необходимости могут быть добавлены каталоги изображений с большим разрешением.

  • Подкаталог ru/bitec/app/btk/images/toolbar/disabled содержит обесцвеченные копии изображений коллекции toolbar, необходимые для отображения неактивных операций на панелях управления.

Пример указания изображения для операции с использованием аннотации:

@Oper(
  caption = "Род", 
  headOperation = "references", 
  imageUri ="ru/bitec/app/gs3/images/toolbar/61.png")  
def open_Bs_Kind_RoList(): Unit = {  
  Bs_KindAvi.roList().newForm().open()  
}

При одновременном указании свойств imageIndex и indexUri, второе имеет приоритет.

Частично-загружаемые деревья#

Введение#

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

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

Настройка avm файла#

Для отключения полной загрузки дерева в avm файле в настройках отображения необходимо указать свойство

fetchAllTree="false"

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

<tree idAttr="gid" 
      idParentAttr="gidParent"
      hasChildrenAttr="bHasChild"
/>

Настройка refresh#

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

  • OPENNODEIDARRAY
    Параметр выборки, хранящий в себе список открытых нод через запятую в формате ftString

  • OPENNODEID
    Параметр выборки, передающий idAttr открываемой ноды в формате ftString

OPENNODEIDARRAY и OPENNODEID – системные параметры. Пользовательская установка значений этих параметров запрещена

Пример#

Пример запроса для Mct_StructureAvi#StructureTree, находящегося в пакете Mct_StructurePkg.getOnRefreshBs

--п.1.- открытые узлы  
with  nodes as (  
  SELECT distinct tVal.tVal as gid  
    FROM regexp_split_to_table(
          concat_ws(
             ', '
            ,:OpenNodeIdArray#
            ,:OpenNodeIdArray
          ) 
          ,', '
         ) tVal  
    where :OpenNodeId is null -- узлы нужны только при рефреше
  union  
  select :gidParent as gid  
  where :gidCurrentGid# is not null  
),  
-- фильтрация объектов для вывода в дерево  
gidFlt as (  
  select h.gid  
        ,h.gidParent  
    from (  
      -- п.2.  блок рефреша узлов "RefreshNodes"  
      select t.gid  
            ,case when ll.id is null then null else l.gidParent end as gidParent  
        from mct_structure t  
        join mct_structurelink l on l.gidstructure=t.gid   
        left join mct_structurelink ll on (
                    l.gidparent=ll.gidstructure and
                    l.idviewtype=ll.idviewtype
                  )  
        where t.idPrjVer = :flt_idPrjVer  
          and l.idviewtype=:flt_idViewType  
          and l.gidparent in (select n.gid from nodes n)  
      -- Блок OnRefresh  
      union all  
      --  п.3. 1. Раскрываем заказ – корневые записи  
      select t.gid  
            ,null as gidParent  
        from mct_structure t  
        join mct_structurelink l on l.gidstructure=t.gid  
        l.idviewtype=ll.idviewtype
    )  
      --left join mct_structure tt on (l.gidparent=tt.gid)  
    where t.idPrjVer = :flt_idPrjVer  
      and l.idviewtype=:flt_idViewType  
      and not exists (
          select 1 
            from mct_structurelink ll 
           where ll.gidstructure=l.gidparent 
             and ll.idviewtype=:flt_idViewType)  
            and :OpenNodeId is null  
      --  
      union all  
  --п.4. - 2. Раскрываем потомков – по нажатию ноды  
  select l.gidstructure as gid  
        ,l.gidparent  
    from mct_structurelink l  
   where l.idviewtype = :flt_idViewType  
     and l.gidparent = :OpenNodeId  
) h  
--п.5.  
SELECT  
       t.id  
      ,t.idClass  
      ,t.gid  
      ,tt.gidParent  
      ,(select COALESCE(max(1),0) from mct_structurelink l where
      l.gidparent=t.gid) as bHasChild  
      ,t.gidDoc  
      ,t.gidDocVer  
      ,t.gidSourceObj  
      ,t.idPrjVer  
      ,t1.sCode as idPrjVerHL  
      ,t.idEskd  
      ,t2.sCaption as idEskdHL  
      ,t.idPosType  
      ,t3.sCaption as idPosTypeHL  
      ,t.sCode  
      ,t.sCaption  
      ,t.sSysName  
  FROM Mct_Structure t  
  join gidFlt tt on t.gid=tt.gid  
  LEFT JOIN Bs_PrjVer t1 on t.idPrjVer = t1.id  
  LEFT JOIN Mct_Eskd t2 on t.idEskd = t2.id  
  LEFT JOIN Mct_PosType t3 on t.idPosType = t3.id  
  LEFT JOIN Bs_Goods t4 on t.idGds = t4.id  
  LEFT JOIN Msr_MeasureItem t5 on t.idMsr = t5.id  
  LEFT JOIN Mct_OrderSheet t6 on t.idOrderSheet = t6.id
  • п.1
    Получение таблицы значений открытых нод с учетом открытия ноды для новой созданной записи уровнем ниже

  • п.2
    Получение записей, которые входят в открытые ноды

  • п.3
    Получение корневых записей дерева

  • п.4
    Получение записей, которые входят в открытую ноду – блок, срабатывающий при первом раскрытии ноды

  • п.5
    Основной запрос для получения необходимых колонок.

Так же важно обратить внимание, что в основном запросе присутствуют изменения:

  • Добавлен атрибут bHasChild
    Признак наличие дочерних элементов.
    Текст запроса атрибута:

    (select COALESCE(max(1),0) from mct_structurelink l where
      l.gidparent=t.gid) as bHasChild
    
  • Добавлено ограничение получения записей по отфильтрованному списку

    join gidFlt tt on t.gid = tt.gid
    

Загрузка дочерних записей на открытие#

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

Примечание

Изменение параметров, участвующих в запросе, спровоцирует операцию refresh!

Для установки параметров без вызова refresh необходимо воспользоваться конструкцией

try {  
  selection.ignoreParamChange = true  
  setVar("gidParent", curGidParent)  
  setVar("gidCurrentGid#", thisRop().gid)  
}  
finally {  
  selection.ignoreParamChange = false  
}

Пример разметки выборки#

<?xml version="1.0"?>
<view xmlns="http://www.global-system.ru/xsd/global3-view-1.0"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.global-system.ru/xsd/global3-view-1.0"
      name="Bs_GdsCostDeviationType">

    <representation name="Default" doubleClickOperation.createFormMode="CardEdit"
                    doubleClickOperation.lookupMode="CloseFormOK"
                    caption="Виды отклонений в стоимости ТМЦ">
        <filter name="Bs_GdsCostDeviationTypeFilter">
            <macros name="DefFltReferenceMacro">
                <condition logicalOperator="and" id="shownotused"
                           isExpression="true"
                           expression="(:filter$flt_bShowNotUsed = 1
                                           or
                                        (coalesce(t.bnotactive,0) = 0
                                         and (t.dexpirydate is null or t.dexpirydate > current_date)
                                            )
                                          )">
                    <filterAttr name="flt_bShowNotUsed" attribute-type="Long" caption="Отображать неиспользуемые"
                                isLastInLine="false" order="10" defaultValue="0" editorType="check"/>
                </condition>
            </macros>
        </filter>
        <layout>
            <simpleComposer>
                <frame filter.isVisible="true" toolBar.isVisible="true">
                    <grid/>
                </frame>
            </simpleComposer>
        </layout>
        <attributes>
            <attr name="id" caption="Идентификатор" isVisible="false" editorType="edit" order="-1"/>
            <attr name="idClass" caption="idClass" isVisible="false" editorType="edit" order="-2"/>
            <attr name="gid" isVisible="false" editorType="edit"/>
            <attr name="sCode" caption="Код" editorType="edit" order="10" isRequired="true"/>
            <attr name="sCaption" caption="Наименование" editorType="edit" order="20" isRequired="true"/>
            <attr name="sDescription" caption="Описание" editorType="memo" order="30"/>
            <attr name="bNotActive" caption="Не используется" editorType="check"/>
            <attr name="dExpiryDate" caption="Дата окончания использования" editorType="datePick"/>
        </attributes>

        <operations>

        </operations>

    </representation>

    <representation name="List" editMode="notEdit">
    </representation>

    <representation name="Card" editMode="edit" stdFilter.isAvailable="false">
        <layout>
            <simpleComposer>
                <frame filter.isVisible="false">
                    <card/>
                </frame>
            </simpleComposer>
        </layout>
    </representation>

    <representation name="Lookup" editMode="notEdit">
    </representation>

</view>