Пример использования компонента «Дерево»
Рассмотрим пример использования компонента Дерево на основе списка бизнес-процессов и папок, в которых они находятся, в двух вариантах:
Создание структуры обмена данными
Для транспортировки и отображения данных создайте структуру обмена данными WorkflowModel со свойствами:
- Uid — уникальный идентификатор, тип GUID;
- ParentUid — уникальный идентификатор родителя, тип GUID с включенной опцией Может иметь пустое значение;
- Caption — наименование, тип Строка;
- Type — тип элемента, тип Целое число.
Сохраните и опубликуйте структуру обмена данными.
Создание структуры компонента
Создайте новый компонент TreeComponentExample, и выполните следующие настройки:
-
В этот компонент добавьте новое свойство WorkflowModelList типа WorkflowModel с типом связи Список.
- На форму созданного компонента перетащите компонент Дерево. Для этого в разделе Компоненты откройте вкладку Основные.
Настройки компонента «Дерево»
Компонент Дерево состоит из двух частей:
- основной контент элемента дерева — содержит основную информацию об элементе дерева, например, заголовок;
- дополнительный контент элемента дерева — содержит дополнительную информацию об элементе дерева, например, позволяет настроить действия с ним. Отображается всегда справа от основного контента.
Перейдите к настройкам дерева, выделив его и нажав на значок шестерёнки.
В поле Источник данных укажите созданное свойство WorkflowModelList. После выбора этого свойства появляется возможность указать две настройки:
- свойство, отвечающее за ключ;
- свойство, отвечающее за ключ родителя.
Эти свойства позволяют связать родительский и дочерний элемент и получить древовидную структуру. Они могут быть следующих типов:
- GUID;
- Строка;
- Целое число (long);
- Целое число 32 бита (int);
- Целое число 16 бит (short).
1. В корневом элементе можно задать значение null , если в ключе родителя включена опция Может иметь пустое значение.
2. Корневых элементов не может быть больше одного.
Дополнительные настройки компонента «Дерево»
Задайте следующие дополнительные настройки дерева.
Показывать кнопки Свернуть всё/Развернуть всё — если эта опция включена, для пользователя в интерфейсе будут доступны кнопки, которые позволяют отображать или скрывать сразу все элементы дерева.
Выбор элементов — разрешает выбирать элементы дерева.
Включив эту опцию, задайте две появившиеся настройки:
- Список выбранных элементов — укажите свойство из контекста компонента с типом связи Список такого же типа, как источник данных.
- Метод при выборе элемента — укажите метод, который будет вызываться при выборе элемента. В этом методе задайте добавление и удаление элемента в контекстную переменную Список выбранных элементов.
Раскрыт ли узел дерева — позволяет отображать или скрывать вложенные элементы в дереве.
Действие при раскрытии узла — задайте действие, которое выполняется при попытке раскрыть или свернуть узел дерева.
Тип дерева — визуальная настройка дерева:
- По умолчанию — компактный тип отображения, при наведении на элемент дерева он выделяется рамкой, дополнительный контент также отображается при наведении на элемент.
- В виде блочных элементов — дерево растягивается на всю ширину, дополнительный контент отображается всегда и справа.
В основной контент элемента дерева добавьте компонент Кнопка и задайте его настройки. На вкладке Основные укажите:
- текст кнопки — с помощью функции GetTreeItemCaption, которая задаёт наименование кнопки (имя процесса или группы);
- иконку — с помощью функции GetTreeItemIcon, которая задаёт изображение на кнопке (процесс или группа).
В дополнительный контент элемента дерева добавьте компонент Кнопка и задайте его настройки. На вкладке Основные укажите:
- текст кнопки — с помощью функции GetAdditionalCaption, которая задаёт наименование кнопки;
- иконку — с помощью функции GetAdditionalButtonIcon, которая задаёт изображение на кнопке (добавить или удалить).
Клиентские сценарии
Если в компоненте Кнопка указаны функции, в клиентских сценариях генерируется соответствующий код. При этом, т. к. в компоненте Дерево указан источник данных, в функции внутренних компонентов аргументом передаётся элемент данных. Код сценария:
/// <summary>
/// Сценарий вычисления наименования кнопки в компоненте "Дерево"
/// </summary>
public string GetTreeItemCaption(EleWise.ELMA.DataClasses.WorkflowModel item0)
{
return item0.Caption;
}
/// <summary>
/// Сценарий вычисления иконки кнопки в компоненте "Дерево"
/// </summary>
public string GetTreeItemIcon(EleWise.ELMA.DataClasses.WorkflowModel item0)
{
switch (item0.Type)
{
case 0:
{
// Иконка "Папка"
return "folder";
}
case 1:
{
// Иконка "Процесс"
return "process";
}
default:
{
return null;
}
}
}
/// <summary>
/// Сценарий вычисления наименования дополнительной кнопки в компоненте "Дерево"
/// </summary>
public string GetAdditionalCaption(EleWise.ELMA.DataClasses.WorkflowModel item0)
{
// Кнопка не содержит наименования
return "";
}
/// <summary>
/// Сценарий вычисления иконки дополнительной кнопки в компоненте "Дерево"
/// </summary>
public string GetAdditionalButtonIcon(EleWise.ELMA.DataClasses.WorkflowModel item0)
{
switch (item0.Type)
{
case 0:
{
// Иконка "Добавить"
return "add";
}
case 1:
{
// Иконка "Корзина"
return "remove";
}
default:
{
return null;
}
}
}
Серверные сценарии
Перейдите к настройкам пользовательского компонента TreeComponentExample, нажав в правом нижнем углу на значок шестерёнки. Затем задайте сценарий при загрузке формы.
Код серверного сценария:
/// <summary>
/// Сценарий при загрузке формы
/// </summary>
public void ServerOnLoad(ViewModel form)
{
var workflowHeaders = EleWise.ELMA.Workflow.Managers.ProcessHeaderManager.Instance.FindAll();
// Уникальный идентификатор корневого узла дерева
var rootUid = Guid.Empty;
// Добавим корневой узел дерева
form.Context.WorkflowModelList.Add(
new WorkflowModel
{
Type = 0, // Тип 0 пусть означает, что это - группа
Caption = SR.T("Все процессы"),
Uid = rootUid,
ParentUid = null // Головные компоненты обязательно должны ссылаться на null
}
);
foreach (var workflowHeader in workflowHeaders)
{
// Группа процесса
var parentGroup = workflowHeader.ParentGroup;
form.Context.WorkflowModelList.Add(new WorkflowModel
{
Type = 1, // Тип 1 пусть означает, что это - процесс
Caption = workflowHeader.Name,
Uid = workflowHeader.Uid,
ParentUid = parentGroup != null ? parentGroup.Uid : rootUid
});
if (parentGroup != null)
{
AddGroupRecursive(form, rootUid, parentGroup);
}
}
}
private void AddGroupRecursive(ViewModel form, Guid rootUid, EleWise.ELMA.Workflow.Models.IProcessGroup parentGroup)
{
if (parentGroup != null)
{
var parent = parentGroup.Parent;
form.Context.WorkflowModelList.Add(new WorkflowModel
{
Type = 0, // Тип 0 пусть означает, что это - группа
Caption = parentGroup.Name,
Uid = parentGroup.Uid,
ParentUid = parent != null ? parent.Uid : rootUid
});
AddGroupRecursive(form, rootUid, parent);
}
}
Сохраните компонент, запустите в режиме эмуляции и проверьте полученный результат.
Реализация функции «Выбор элементов»
В компоненте добавьте новое свойство SelectedWorkflowModelList типа WorkflowModel с типом связи Список и выполните следующие настройки.
- Перейдите на вкладку Дополнительно нового свойства и включите опцию Только для веб-части, так как это свойство используется только в клиентских сценариях.
- Перейдите к настройкам дерева, выделив его и нажав на значок шестерёнки. Задайте следующие настройки:
- включите опцию Выбор элементов;
- в поле Список выбранных элементов выберите созданное свойство SelectedWorkflowModelList;
- в поле Метод при выборе элемента укажите клиентскую функцию OnAddElement.
Код клиентского сценария:
Функция выбора элемента
/// <summary>
/// Сценарий действия "Метод при выборе элемента"
/// </summary>
public void OnAddElement(bool selected, EleWise.ELMA.DataClasses.WorkflowModel item0)
{
if (selected)
{
// Добавляется только указанный узел
Context.SelectedWorkflowModelList.Add(item0);
}
else
{
// Удаляется только указанный узел
Context.SelectedWorkflowModelList.Remove(item0);
}
}
При выполнении этого сценария, если выбрать внутренний элемент дерева, визуально отобразится, что выбран и родительский элемент, но это не так. Если попробовать снять флажок с родительского элемента, ничего не произойдет, т. к. в коде указана логика работы только с конкретным элементом дерева, не затрагивая родительские и дочерние элементы.
Если нужно, чтобы при выборе родительского элемента выбирались дочерние или при снятии родительского элемента снимались дочерние, измените код функции:
Функция выбора элемента
/// <summary>
/// Сценарий действия "Метод при выборе элемента"
/// </summary>
public void OnAddElement(bool selected, EleWise.ELMA.DataClasses.WorkflowModel item0)
{
var selectedElements = Context.SelectedWorkflowModelList;
var tree = Context.WorkflowModelList;
if (selected)
{
OnSelectElementInternal(tree, selectedElements, item0);
}
else
{
OnDeSelectElementInternal(tree, selectedElements, item0, true);
}
}
private void OnSelectElementInternal(
ICollection<EleWise.ELMA.DataClasses.WorkflowModel> tree,
ICollection<EleWise.ELMA.DataClasses.WorkflowModel> selectedElements,
EleWise.ELMA.DataClasses.WorkflowModel element
)
{
if (!selectedElements.Contains(element))
{
// Выбираем себя
selectedElements.Add(element);
OnSelectElement(tree, selectedElements, element, true);
}
}
private void OnSelectElement(
ICollection<EleWise.ELMA.DataClasses.WorkflowModel> tree,
ICollection<EleWise.ELMA.DataClasses.WorkflowModel> selectedElements,
EleWise.ELMA.DataClasses.WorkflowModel element,
bool processChildren)
{
// Обработка родителя
var parentId = element.ParentUid;
var parent = tree.FirstOrDefault(a => a.Uid == parentId);
if (parent != null)
{
var parentChildren = tree.Where(a => a.ParentUid == parentId);
if (parentChildren.All(a => selectedElements.Contains(a)))
{
// Если все дочерние элементы выбраны - тогда мы выбираем и родителя тоже
OnSelectElementInternal(tree, selectedElements, parent);
}
}
if (processChildren)
{
// Выбираем все дочерние
var itemId = element.Uid;
var children = tree.Where(a => a.ParentUid == itemId);
foreach (var child in children)
{
OnSelectElementInternal(tree, selectedElements, child);
}
}
}
private void OnDeSelectElementInternal(
ICollection<EleWise.ELMA.DataClasses.WorkflowModel> tree,
ICollection<EleWise.ELMA.DataClasses.WorkflowModel> selectedElements,
EleWise.ELMA.DataClasses.WorkflowModel element,
bool processChildren)
{
if (selectedElements.Contains(element))
{
// Убираем из выбора себя
selectedElements.Remove(element);
OnDeSelectElement(tree, selectedElements, element, processChildren);
}
}
private void OnDeSelectElement(
ICollection<EleWise.ELMA.DataClasses.WorkflowModel> tree,
ICollection<EleWise.ELMA.DataClasses.WorkflowModel> selectedElements,
EleWise.ELMA.DataClasses.WorkflowModel element,
bool processChildren)
{
// Обработка родителя
var parentId = element.ParentUid;
var parent = tree.FirstOrDefault(a => a.Uid == parentId);
if (parent != null)
{
var parentChildren = tree.Where(a => a.ParentUid == parentId);
if (parentChildren.Any(a => !selectedElements.Contains(a)))
{
// Если не выбран хотя бы один элемент из дочерних - мы снимаем выбор с родителя
OnDeSelectElementInternal(tree, selectedElements, parent, false);
}
}
if (processChildren)
{
// Убираем все дочерние
var itemId = element.Uid;
var children = tree.Where(a => a.ParentUid == itemId);
foreach (var child in children)
{
OnDeSelectElementInternal(tree, selectedElements, child, true);
}
}
}
Сохраните компонент, запустите в режиме эмуляции и проверьте полученный результат.