Показать сообщение отдельно
Старый 20.07.2009, 16:03   #1  
gl00mie is offline
gl00mie
Участник
MCBMSS
Most Valuable Professional
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,684 / 5788 (200) ++++++++++
Регистрация: 28.11.2005
Адрес: Москва
Записей в блоге: 3
Итератор с поддержкой методов обратного вызова для обработки контролов на форме
Для изменения свойств контролов на форме есть два подхода: либо править свойства в дизайне формы, либо делать это откуда-то из кода. Первый способ вроде бы наиболее простой, но он, порой, связан со значительными трудозатратами, кроме того, таким образом нельзя править свойства контролов, входящих в группы со свойством AutoDataGroup == Yes. При этом отказываться от этого чудесного свойства тоже не хотелось бы, поскольку оно позволяет за счет простого изменения группы полей на таблице менять перечень и порядок полей в группах на всех связанных формах.
С другой стороны, чтобы менять свойства контролов из кода, нужно обращаться к ним по одному, при этом контролы обычно должны иметь свойство AutoDeclaration == Yes, а для этого у группы, в которую входят контролы, необходимо обязательно выставить AutoDataGroup == No. Как вариант, можно в коде реализовать цикл обхода всех дочерних контролов той же группы, смотреть, к какому типу относится тот или иной дочерний контрол (потому что у наследников FormControl ряд свойств отличается, а в самом FormControl кое-какие свойства просто не реализованы, поэтому необходмо приведение типов дочерних контролов), тогда для группы можно оставить свойство AutoDataGroup == Yes. Но каждый раз организовывать такой цикл очень нудно: на одной и той же форме контролы в разных группах может понадобиться обрабатывать совершенно по-разному. Например, есть группы, в которые выведены коды пользователей либо сотрудников, а также display-методы для отображения их имен из таблицы сотрудников (EmplTable), плюс дата и время создания/модификации записи. Вот как это "штатно" выглядит:

Видно, что для кодов пользователей и сотрудников (userId/emplId) значения свойств displayLength у расширенных типов отличаются, а для имен и значений времени тут совершенно излишни метки. Хотелось бы, чтобы эти группы выглядели примерно так:

Так вот (та-да!) теперь этого можно легко добиться за счет использования итератора и методов обратного вызова, которые собственно будут устанавливать свойства контролов. В коде на форме это выглядит примерно так:
X++:
Set         setOfIds;
;
setOfIds = DEV_FormHelpers::addStringCtrlId2Set();    // на контролах какого типа должен дергаться метод обратного вызова
DEV_iterateThroughFormControls( GrpAuthorNames,         null, staticmethodstr(DEV_FormHelpers, hideLabel),      setOfIds, true, classnum(DEV_FormHelpers) );
DEV_iterateThroughFormControls( GrpAuthorIds,           null, staticmethodstr(DEV_FormHelpers, setColumnWidth), setOfIds, true, classnum(DEV_FormHelpers) );
setOfIds = DEV_FormHelpers::addTimeCtrlId2Set();
DEV_iterateThroughFormControls( GrpCreatedModifiedTime, null, staticmethodstr(DEV_FormHelpers, hideLabel),      setOfIds, true, classnum(DEV_FormHelpers) );
Здесь DEV_iterateThroughFormControls() - собственно метод-итератор, GrpAuthorNames и GrpAuthorIds - соотв. группы контролов со свойством AutoDeclaration == Yes, а DEV_FormHelpers - вспомогательный класс, чьи статические методы используются для выполнения действий над контролами. В принципе, ничто не мешает передавать в метод-итератор вместо null указатель на экземпляр какого-либо класса и название экземплярного метода этого класса, вместо того чтобы использовать статические методы DEV_FormHelpers.
Может возникнуть резонный вопрос, почему все-таки не изменить дизайн формы вручную раз и навсегда. Что ж, мне лично зачастую приходится создавать схожие по внешнему виду формы, отличающиеся некоторыми параметрами поведения и обрабатываемыми данными. Разумеется, лепить кучу форм-клонов неохота - куда интересней сделать одну форму и семейсво классов, управляющих ее поведением. Так вот, для изменения внешнего вида формы в зависимости от класса, который ей управляет, и понадобился такой механизм.
Метод-итератор для рекурсивного перебора контролов на форме использует интерфейс DEV_IFormContainerControl, представляющий общие методы контролов-контейнеров (FormButtonGroupControl, FormGridControl, FormGroupControl, FormMenuButtonControl, FormTabControl, FormTabPageControl и - с одной оговоркой - FormDesign). Также в проект включены и другие вспомогательные интерфейсы - DEV_IFormButtonControl (общие методы и свойства FormButtonControl, FormCommandButtonControl и FormFunctionButtonControl) и DEV_IFormGridContainedControl (общие методы и свойства контролов, которые могут появиться на гриде при использовании табличных групп полей). Единственное неудобство использования этих интерфейсов - это необходимость при присваивании "интерфейсным" переменным ссылок на наследники FormControl использовать промежуточное приведение к Object. Впрочем, избавление от необходимости приведения того же FormControl к нужному типу его наследника, чтобы дернуть какой-нибудь метод, не определенный на FormControl, и унификация работы со схожими типами контролов, на мой взгляд, с лихвой компенсирует это неудобство.

PS. Все это проверялось на AX3
Вложения
Тип файла: rar DEV_FormControlIterator.rar (4.7 Кб, 249 просмотров)
За это сообщение автора поблагодарили: mazzy (2), sukhanchik (6), Logger (15), Ar (1), konopello (3), Jorj (1), Stainless (1), Kabardian (6).