Продолжение поста относительно
URI-based interface. Непосредственно использование динамического пользовательского интерфейса в приложении не обязательно требует использования ранее указанного интерфейса управления и обеспечения работы с данными и их зависимостями при хранении и передаче, но у меня использовалась и подразумевается именно эта комбинация. Но это так пояснение общей структуры приложения...
Общая идея данного интерфейса отсутствие в приложении жесткой структуры отображения элементов - все элементы описываются в XML или в другом виде - и как результат этот внешний вид может обновляться без изменения откомпилированного кода. В моем случае это использовалось для описания внешнего вида клиентской части приложения и в теории это описание можно обновлять с сервера(сделать можно, но не потребовалось в конкретном случае).
Причиной почему был выбран XML было простота реализации рекурсивного описания компонентов внутри формочек и возможность в случае чего без редактора этих форм - руками подправить ошибки. Из этого XML формировался классы(C#) для упрощения дальнейшей обработки структуры и эти классы в дальнейшем передавались компоненту который по классам формировал элементы с нужной вложенностью и структурой.
В описание любого компонента содержало:
- его тег (к нему привязывались вся остальная логика);
- текст - если нужен;
- позиция внутри контейнера;
- тип элемента(текстовое поле, заголовок, комбобокс);
- список подчиненный элементов;
- тип отображения и правила отображения - в основном можно ли пользователю, что нибудь вводить или изменять и тип проверки.
Основной причиной почему нельзя было непосредственно сериализировать сами компоненты - была возможность не привязываться к настоящим классам и возможность ограничивать сериализацию только нужными полями и свойствами. От всех графический элементов были созданы потомки обладающее общим интерфейсом и отрабатывающей простейшие проверки.
При потребности смены отображения - система проходилась по существующим блокам и скрывала лишние(так как большинство форм использовало одинаковые блоки элементов и именно их скрывали) и запрашивало визуализацию нужных элементов. Для этой логики форма делилась на блоки с именами и к этим именам привязывалась структура элементов. Как следствие формировалась видимость смены окон - без реального создания нового окна. Если нужно было что-то открыть в новом окне по интерфейсу(
URI-based interface.) передавалось сообщение расположенному выше по иерархии контроллеру, который следил за открытыми окнами и мог создать или закрыть окно, сообщение о том что нужно создать новое окно(или закрыть) с параметрами, какую инициализацию в этом окне выполнить (обычно имя формы и какое событие в ней вызвать и с какими параметрами - имя событие всегда совпадает с тегом элемента(кнопки например)).
Запуск приложения это создание контроллера окон с подгрузкой всех нужных данный и подачей сообщения о создании окна с параметрами.
При создании элемента к нему по тегу привязывается обработчик - этот обработчик должен при появлении событие(клик по кнопке например) - найти все подчиненные и выполнить над ними действия(зависимые списки например). Также при смене отображения - можно привязывать обработчики к элементам, которые должны работать по разному в зависимости от отображения(обычно если компонент может генерировать разные типы событий и их нельзя преобразовать к единому виду), и первоначальное заполнение элементов - так как в основном один элемент в зависимости от отображения может показывать разные данные. Для добавления новых обработчиков добавлялся наследники от класса основной логики и там перегружался обработчик получения события для элемента и смены отображения форм. То есть получалось дерево наследований, в каждом классе добавлялась логика обработки событий объединенных какой-то общностью - например зависимостями и типом данных.
Для того чтобы работать со всей этой структурой есть редакторы форм - реализованные основе этой же логики, но с включением подчиненного элемента отображения структуры формы в которых можно сразу посмотреть и отредактировать элемент и его свойства.
Это позволяет: для портировании и замене способа отображения - нужно только заменить генератор элементов и способ привязки к элементу события - что если не использовать специфичные для платформы свойства внутри обработчиков легко заменить отображения не изменяя логику. Также благодаря возможности повторного использования зависимостей между тегами при создании новых форм использовать старые зависимости, так как они привязаны только к тегам элементов.
Для того чтобы ограничить область работы событий, если нужна другая модель зависимостей, нужно использовать другое имя тега или создать менеджер событий и отключать события(привязывается не конечный обработчик, а вызов менеджера) в зависимости от отображения.
Начальное заполнение списков осуществляется через вызов в хранилище данных запроса о получении списка с именем совпадающим с тегом списка - для элементов, которые так подгрузить не удается через перегружаемую функцию в визуализации загрузить нужные данные.