AXForum  
Вернуться   AXForum > Microsoft Dynamics AX > DAX: Программирование
DAX
Забыли пароль?
Зарегистрироваться Правила Справка Пользователи Сообщения за день Поиск Все разделы прочитаны

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 23.11.2020, 23:29   #1  
sukhanchik is offline
sukhanchik
Moderator
Аватар для sukhanchik
MCBMSS
Злыдни
Лучший по профессии 2015
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
2,881 / 2719 (96) +++++++++
Регистрация: 13.06.2004
Адрес: Москва
D365FO: Отображение в контекстном меню названия поля / метода таблицы
Добрый день всем!
Очень не хватает в D365FO (после всех версий AX) получить информацию в пользовательском режиме от контрола - к какому полю / метода какого датасорса (с указанием названия таблицы / представления).
По сравнению с предыдущими версиями AX получение информации усложняется потенциальным наличием пользовательских контролов (по типу DimensionEntryControl), которые описываются обычными классами на X++. Вдобавок немного поменялся подход к рисованию контекстных меню.

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

Итак, хочется получить вот такие вот картинки:
Нажмите на изображение для увеличения
Название: SNAG_Program-0060.png
Просмотров: 39
Размер:	38.7 Кб
ID:	12985
Название: SNAG_Program-0061.png
Просмотров: 148

Размер: 15.4 Кб
Название: SNAG_Program-0062.png
Просмотров: 148

Размер: 30.4 Кб
Нажмите на изображение для увеличения
Название: SNAG_Program-0063.png
Просмотров: 39
Размер:	61.1 Кб
ID:	12988
Название: SNAG_Program-0064.png
Просмотров: 148

Размер: 14.6 Кб

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

Образец написания подписчиков делегатов представлен на примере обработки контрола DimensionEntryControl для финансовых аналитик.
Поддерживаемые типы контролов:
  • FormCheckBoxControl
  • FormComboBoxControl
  • FormDateControl
  • FormDateTimeControl
  • FormGuidControl
  • FormInt64Control
  • FormIntControl
  • FormRadioControl
  • FormRealControl
  • FormRichTextControl
  • FormStaticTextControl
  • FormStringControl
  • FormTimeControl
  • FormWindowControl
  • FormReferenceGroupControl
  • FormSegmentedEntryControl / SegmentedEntryControl
  • DimensionEntryControl

Технически, система при открытии формы обходит все поддерживаемые контролы и добавляет в их контекстные меню - дополнительные пункты меню. Само собой - это заметно сказывается как на времени открытия формы, так и на времени отображения контекстного меню. Поэтому в параметрах пользователя предусмотрен флажок (по умолчанию выключенный), который включает данную функциональность для конкретного пользователя.
Нажмите на изображение для увеличения
Название: SNAG_Program-0065.png
Просмотров: 36
Размер:	54.7 Кб
ID:	12990

Код постарался подробно прокомментировать (умещается всё в одном классе)
X++:
// VSUH, 23.11.2020 Добавление названий источника данных (датасорс + поле / метод) в контекстное меню контрола на форме
class FormControlShowDevInfo
{
    public FormRun formRun; // Обрабатываемая форма
    const int cInfoDataSource  = -100; // Код пункта меню с названием датасорса и таблицы
    const int cInfoFieldMethod = -101; // Код пункта меню с названием поля / метода таблицы
    #Properties
    public const str cPropertyDataSource = #PropertyDataSource;
    public const str cPropertyDataFieldName  = #PropertyDataFieldName;

    // Map для хранения контекстного меню, которое может быть переопределено разработчиком на форме.
    // Если меню не переопределено разработчиком, то значение в Map пустое. Попутно выполняет роль перечня контролов, 
    // у которых переопределено контекстное меню данным классом (т.к. нельзя 2 раза вызвать метод registerOverride)
    Map   ctrlContextMenu; 

    // Map для хранения контролов, которые были созданы в контейнере пользовательского (Custom) контрола.
    // Например, в контроле финансовых аналитик (DimensionEntryControl) в момент запуска формы добавляются FormStringControl-ы
    // для вывода непосредственно значений аналитик. В данном Map хранятся эти FormStringControl-ы, с привязкой к родительскому
    // пользовательскому контролу (он хранится в value). Все дочерние контролы автоматически получают информацию об источнике
    // данных от своего родительского пользовательского контрола (в случае финансовых аналитик - DimensionEntryControl)
    Map   ctrlChildCustomControl;

    // Родительский пользовательский контрол, если производится обход подчиненных контролов (например, для финансовых аналитик - 
    // это DimensionEntryControl)
    FormContainerControl parentCustomCtrl;
        
    /// <summary>
    /// "Точка входа" в функционал. Метод вызывается после вызова super() в методе formRun.run()
    /// </summary>
    /// <param name = "_formInstance">
    /// Объект открывшейся формы
    /// </param>
    [SubscribesTo(classStr(FormRun), staticDelegateStr(FormRun, onFormRunCompleted))]
    public static void FormRun_onFormRunCompleted(FormRun _formInstance)
    {
        if (SysUserInfo::find().ShowFormControlDevInfo)
        {
            FormControlShowDevInfo::instance(_formInstance).run();
        }
    }

    /// <summary>
    /// Параметр для сохранения класса в глобальном кэше
    /// </summary>
    /// <param name = "_formRun"></param>
    /// <returns></returns>
    public static str globalCacheOwner(FormRun _formRun)
    {
        return strFmt("Form:%1", _formRun.name());
    }

    /// <summary>
    /// Параметр для сохранения класса в глобальном кэше
    /// </summary>
    /// <param name = "_formRun"></param>
    /// <returns></returns>
    public static int globalCacheKey()
    {
        return classNum(FormControlShowDevInfo);
    }

    /// <summary>
    /// Класс запускается в режиме "singleton", т.е. для каждой формы - свой единственный экземпляр. Запущенный экземпляр сохраняется в глобальном кэше
    /// </summary>
    /// <param name = "_formRun">
    /// Экземпляр формы
    /// </param>
    /// <returns></returns>
    public static FormControlShowDevInfo instance(FormRun _formRun)
    {
        FormControlShowDevInfo runClass;

        if (appl.globalCache().isSet(FormControlShowDevInfo::globalCacheOwner(_formRun), FormControlShowDevInfo::globalCacheKey()))
        {
            runClass = appl.globalCache().get(FormControlShowDevInfo::globalCacheOwner(_formRun), FormControlShowDevInfo::globalCacheKey());
        }
        else
        {
            runClass = new FormControlShowDevInfo();
            runClass.formRun = _formRun;
            appl.globalCache().set(FormControlShowDevInfo::globalCacheOwner(_formRun), FormControlShowDevInfo::globalCacheKey(), runClass);
        }
        return runClass;
    }

    private void initCtrlContextMenuMap()
    {
        ctrlContextMenu = new Map(Types::Class, Types::String);
        ctrlChildCustomControl = new Map(Types::Class, Types::Class);
    }

    /// <summary>
    /// Стартовый метод запуска обработки формы
    /// </summary>
    public void run()
    {
        this.setContextMenuOnCtrl(formRun.design());
    }

    /// <summary>
    /// Метод, определяющий тип контрола. У данного типа контрола свойство, содержащее в себе код поля называется 
    /// referenceField (в отличии от контролов, перечисленных в методе isSimpleControl())
    /// У всех контролов обязательно должен быть метод registerOverrideMethod
    /// </summary>
    /// <param name = "_ctrl">
    /// Анализируемый контрол
    /// </param>
    /// <returns>
    /// Возвращает истину, если контрол принадлежит к одному из типов, у которых есть метод referenceField
    /// </returns>
    public boolean isReferenceControl(FormControl _ctrl)
    {
        boolean ret;
        switch (classIdGet(_ctrl))
        {
            case classNum(FormReferenceGroupControl):
            case classNum(FormSegmentedEntryControl):
            case classNum(SegmentedEntryControl):
                ret = true;
                break;
        }
        return ret;
    }

    /// <summary>
    /// Метод, определяющий тип контрола. У данного типа контрола свойство, содержащее в себе код поля называется
    /// dataField (в отличии от контролов, перечисленных в методе isReferenceControl())
    /// У всех контролов обязательно должен быть метод registerOverrideMethod
    /// </summary>
    /// <param name = "_ctrl">
    /// Анализируемый контрол
    /// </param>
    /// <returns>
    /// Возвращает истину, если контрол принадлежит к одному из типов, у которых есть метод dataField
    /// </returns>
    public boolean isSimpleControl(FormControl _ctrl)
    {
        boolean ret;
        switch (classIdGet(_ctrl))
        {
            case classNum(FormCheckBoxControl):
            case classNum(FormComboBoxControl):
            case classNum(FormDateControl):
            case classNum(FormDateTimeControl):
            case classNum(FormGuidControl):
            case classNum(FormInt64Control):
            case classNum(FormIntControl):
            case classNum(FormRadioControl):
            case classNum(FormRealControl):
            case classNum(FormRichTextControl):
            case classNum(FormStaticTextControl):
            case classNum(FormStringControl):
            case classNum(FormTimeControl):
            case classNum(FormWindowControl):
                ret = true;
                break;
        }
        return ret;
    }

    /// <summary>
    /// Метод, определяющий тип контрола. Данный контрол является группирующим, т.е. у него есть дочерние контролы,
    /// которые можно перебрать, используя методы controlCount() и controlNum()
    /// </summary>
    /// <param name = "_ctrl">
    /// Анализируемый контрол
    /// </param>
    /// <returns>
    /// Возвращает истину, если контрол является группирующим
    /// </returns>
    public boolean isGroupControl(FormControl _ctrl)
    {
        boolean ret;
        switch (classIdGet(_ctrl))
        {
            case classNum(FormGroupControl):
            case classNum(FormGridControl):
            case classNum(FormTabControl):
            case classNum(FormTabPageControl):
                ret = true;
                break;
        }
        return ret;
    }

    /// <summary>
    /// Метод, определяющий тип контрола. Данный контрол является пользовательским, т.е. он базируется на обычном
    /// классе из АОТ из узла \Code\Classes. Пользовательских контролов может быть много, поэтому добавление обработки 
    /// нового пользовательского контрола осуществляется через делегаты
    /// </summary>
    /// <param name = "_ctrl">
    /// Анализируемый контрол
    /// </param>
    /// <returns>
    /// Возвращает истину, если контрол является пользовательским
    /// </returns>
    public boolean isCustomControl(FormControl _ctrl)
    {
        boolean ret;
        FormRunServiceArgs isStdCtrl = new FormRunServiceArgs();
        this.checkIsCustomControl(_ctrl, isStdCtrl);
        ret = isStdCtrl.cancelled();
        return ret;
    }

    /// <summary>
    /// Обход контролов на форме и переопределение метода getContextMenuOptions для добавления в контекстное меню пунктов
    /// </summary>
    /// <param name = "_groupControls">
    /// Группирующий контрол
    /// </param>
    private void setContextMenuOnCtrl(Object _groupControls)
    {
        Object      itemControl;
        boolean     canOverride;
        ;
        for (int i = 1; i <= _groupControls.controlCount(); i++)
        {
            itemControl = _groupControls.controlNum(i);
            // Рекурсия, если контрол группирующий
            if (this.isGroupControl(itemControl))
            {
                this.setContextMenuOnCtrl(itemControl);
            }
            canOverride = (this.getInfoDataSourceStr(itemControl) && (this.getInfoFieldStr(itemControl) || this.getInfoMethodStr(itemControl))) || parentCustomCtrl;
            // Переопределение контекстного меню выполняется только в случае, если контрол привязан к полю или методу. Либо является 
            // подчиненным контролом пользовательского контрола (например FormStringControl в контроле DimensionEntryControl)
            // Т.о. для несвязанных (Unbound) контролов контекстное меню не переопределяется
            if (canOverride)
            {
                if (!ctrlContextMenu)
                {
                    this.initCtrlContextMenuMap();
                }
                if (!ctrlContextMenu.exists(itemControl))
                {
                    ctrlContextMenu.insert(itemControl, itemControl.getContextMenuOptions());
                    if (parentCustomCtrl)
                    {
                        ctrlChildCustomControl.insert(itemControl, parentCustomCtrl);
                    }
                    itemControl.registerOverrideMethod(methodStr(FormControl, getContextMenuOptions), methodStr(FormControlShowDevInfo, getContextMenuOptions), FormControlShowDevInfo::instance(formRun));
                    
                    // Анализируются подчиненные контролы пользовательского контрола только в случае, если пользовательский контрол отнаследован от класса FormContainerControl
                    if (this.isCustomControl(itemControl) && SysDictClass::isEqualOrSuperclass(classIdGet(itemControl), classNum(FormContainerControl)))
                    {
                        parentCustomCtrl = itemControl;
                        this.setContextMenuOnCtrl(itemControl);
                        parentCustomCtrl = null;
                    }
                }
            }
        }
    }

    /// <summary>
    /// Получение ссылки на датасорс формы (FormDataSource) на основе переданного контрола. Для контролов, являющихся
    /// подчиненными контролами пользовательского контрола - возвращается ссылка на датасорс пользовательского контрола
    /// </summary>
    /// <param name = "_ctrl">
    /// Анализируемый контрол
    /// </param>
    /// <returns>
    /// Ссылка на датасорс формы. Если ссылку определить не удалось - вернется null
    /// </returns>
    private FormDataSource getDataSource(Object _ctrl)
    {
        FormDataSource formDS;
        // Для контролов из методов isSimpleControl и isReferenceControl датасорс определяется по идентификатору
        if (this.isSimpleControl(_ctrl) || this.isReferenceControl(_ctrl))
        {
            if (ctrlChildCustomControl && ctrlChildCustomControl.exists(_ctrl))
            {
                FormContainerControl parentCustomControl = ctrlChildCustomControl.lookup(_ctrl);
                return this.getDataSource(parentCustomControl);
            }
            if (_ctrl.dataSource())
            {
                for (int i = 1; i <= formRun.dataSourceCount(); i++)
                {
                    if (formRun.dataSource(i).id() == _ctrl.dataSource())
                    {
                        formDS = formRun.dataSource(i);
                        break;
                    }
                }
            }
        }
        // В пользовательских контролах заранее неизвестно, какой метод возвращает название датасорса.
        // Поэтому конкретный метод определяется в делегате getDataSourceCustomControl, а значение этого метода сохраняется в
        // экземпляре класса FormProperty под названием, которое определено в константе cPropertyDataSource
        if (this.isCustomControl(_ctrl))
        {
            FormPropertySet propertySet = new FormPropertySet();
            this.getDataSourceCustomControl(_ctrl, propertySet);
            FormProperty formProperty = propertySet.getProperty(FormControlShowDevInfo::cPropertyDataSource);
            if (formProperty)
            {
                str dsName = formProperty.parmValue();
                if (dsName)
                {
                    formDS = formRun.dataSource(dsName);
                }
            }
        }
        return formDS;
    }

    /// <summary>
    /// Формирование строки с названием датасорса и его таблицы для контрола
    /// </summary>
    /// <param name = "_ctrl">
    /// Анализируемый контрол
    /// </param>
    /// <returns>
    /// Текстовая строка в формате <Название датасорса> (<Название таблицы / view>)
    /// </returns>
    private str getInfoDataSourceStr(Object _ctrl)
    {
        FormDataSource formDS = this.getDataSource(_ctrl);
        str            infoStr;
        if (formDS)
        {
            infoStr = strFmt("%1 (%2)", formDS.name(), tableId2Name(formDS.table()));
        }
        return infoStr;
    }

    /// <summary>
    /// Формирование строки с названием поля для контрола. Для контролов, являющихся подчиненными контролами 
    /// пользовательского контрола - возвращается название поля пользовательского контрола
    /// </summary>
    /// <param name = "_ctrl">
    /// Анализируемый контрол
    /// </param>
    /// <returns>
    /// Текстовая строка с названием поля
    /// </returns>
    private str getInfoFieldStr(Object _ctrl)
    {
        FormDataSource formDS = this.getDataSource(_ctrl);
        str infoStr;
        if (formDS)
        {
            if (ctrlChildCustomControl && ctrlChildCustomControl.exists(_ctrl))
            {
                FormContainerControl parentCustomControl = ctrlChildCustomControl.lookup(_ctrl);
                return this.getInfoFieldStr(parentCustomControl);
            }
            if (this.isSimpleControl(_ctrl))
            {
                if (_ctrl.dataField())
                {
                    infoStr = fieldId2Name(formDS.table(), _ctrl.dataField());
                }
            }
            if (this.isReferenceControl(_ctrl))
            {
                if (_ctrl.referenceField())
                {
                    infoStr = fieldId2Name(formDS.table(), _ctrl.referenceField());
                }
            }
            // В пользовательских контролах заранее неизвестно, какой метод возвращает название поля.
            // Поэтому конкретный метод определяется в делегате getFieldNameCustomControl, а значение этого метода сохраняется в
            // экземпляре класса FormProperty под названием, которое определено в константе cPropertyDataFieldName
            if (this.isCustomControl(_ctrl))
            {
                FormPropertySet propertySet = new FormPropertySet();
                this.getFieldNameCustomControl(_ctrl, propertySet);
                FormProperty formProperty = propertySet.getProperty(FormControlShowDevInfo::cPropertyDataFieldName);
                if (formProperty)
                {
                    infoStr = formProperty.parmValue();
                }
            }
        }
        return infoStr;
    }

    /// <summary>
    /// Формирование строки с названием метода для контрола. Для контролов, являющихся подчиненными контролами
    /// пользовательского контрола - возвращается название метода пользовательского контрола
    /// </summary>
    /// <param name = "_ctrl">
    /// Анализируемый контрол
    /// </param>
    /// <returns>
    /// Текстовая строка с названием метода. Для расширений в этой текстовой строке будет представлено выражение,
    /// содержащее в себе название класса-расширения и название его метода (либо в формате <класс-расширение>.<метод>, 
    /// если разработчик использует Chain Of Command, либо в формате <класс-расширение>::<метод>)
    /// </returns>
    private str getInfoMethodStr(Object _ctrl)
    {
        str infoStr;
        if (this.isSimpleControl(_ctrl))
        {
            if (ctrlChildCustomControl && ctrlChildCustomControl.exists(_ctrl))
            {
                FormContainerControl parentCustomControl = ctrlChildCustomControl.lookup(_ctrl);
                return this.getInfoMethodStr(parentCustomControl);
            }
            if (_ctrl.dataMethod())
            {
                infoStr = _ctrl.dataMethod() + "()";
            }
        }
        return infoStr;
    }

    /// <summary>
    /// Переопределенный метод контекстного меню, который формирует само контекстное меню
    /// </summary>
    /// <param name = "_ctrl">
    /// Контрол, к которому формируется контекстное меню
    /// </param>
    /// <returns>
    /// Возвращается перечень пунктов меню (исключая стандартных), которые добавлены в контекстное меню. Перечень
    /// возвращается в виде строки JSON
    /// </returns>
    
    public str getContextMenuOptions(Object _ctrl)
    {
        ContextMenu menu;
        List menuOptions;
        str infoDataSourceStr, infoFieldStr, infoMethodStr;
        str            sourceMenu, ret;

        // Если разработчик уже добавлял пункты контекстного меню, то их перечень необходимо восстановить
        if (ctrlContextMenu && ctrlContextMenu.exists(_ctrl))
        {
            sourceMenu = ctrlContextMenu.lookup(_ctrl);
            if (sourceMenu)
            {
                // Восстановление ранее добавленных пунктов меню
                menu = FormJsonSerializer::deserializeObject(classNum(ContextMenu), sourceMenu);
                sourceMenu = subStr(sourceMenu, strFind(sourceMenu, '[', 1, strLen(sourceMenu)), strLen(sourceMenu));
                int endList = strFind(sourceMenu, ']', strLen(sourceMenu), -strLen(sourceMenu));
                sourceMenu = subStr(sourceMenu, 1, endList);
                if (sourceMenu)
                {
                    menuOptions = FormJsonSerializer::deserializeCollection(classNum(List), sourceMenu, Types::Class, classStr(ContextMenuOption));
                }
            }
        }
        // Если пункты меню не добавлялись разработчиком свех стандартных, то перечень пунктов меню инициализируется заново
        if (!menu || !menuOptions)
        {
            menu = new ContextMenu();
            menuOptions = new List(Types::Class);
        }

        infoDataSourceStr = this.getInfoDataSourceStr(_ctrl);
        infoFieldStr = this.getInfoFieldStr(_ctrl);
        infoMethodStr = this.getInfoMethodStr(_ctrl);

        // Если ссылка на датасорс не заполнена в контроле - то данный пункт меню не выводится. Актуально для
        // display / edit-методов, которые определены на форме
        if (infoDataSourceStr)
        {
            ContextMenuOption option = ContextMenuOption::Create(strFmt("%1: %2", "@ElectronicReporting:DataSource", infoDataSourceStr), cInfoDataSource);
            menuOptions.addEnd(option);
        }
        // Если ссылка на поле не заполнена в контроле - то данный пункт меню не выводится. Актуально для
        // display / edit-методов
        if (infoFieldStr)
        {
            ContextMenuOption option = ContextMenuOption::Create(strFmt("%1: %2", "@ElectronicReporting:Field", infoFieldStr), cInfoFieldMethod);
            menuOptions.addEnd(option);
        }
        // Если ссылка на метод не заполнена в контроле - то данный пункт меню не выводится. Актуально для
        // контролов, привязанных к полям
        if (infoMethodStr)
        {
            ContextMenuOption option = ContextMenuOption::Create(strFmt("%1: %2", "@ElectronicReporting:Method", infoMethodStr), cInfoFieldMethod);
            menuOptions.addEnd(option);
        }
        menu.ContextMenuOptions(menuOptions);
        ret = menu.Serialize();
        return ret;
    }

    /// <summary>
    /// Проверка контрола на его принадлежность к пользовательским контролам
    /// </summary>
    /// <param name = "_ctrl">
    /// Анализируемый контрол
    /// </param>
    /// <param name = "_isStdCtrl">
    /// Аргументы, через которые передается информация (Истина / Ложь). По умолчанию предполагается, что контрол
    /// не является пользовательским, поэтому в обработчике необходимо явно вызвать метод cancel(), если передаваемый
    /// контрол является пользовательским
    /// </param>
    delegate void checkIsCustomControl(Object _ctrl, FormRunServiceArgs _isStdCtrl)
    {
    }

    /// <summary>
    /// Обработчик делегата checkIsCustomControl для контрола DimensionEntryControl
    /// </summary>
    [SubscribesTo(classStr(FormControlShowDevInfo), delegateStr(FormControlShowDevInfo, checkIsCustomControl))]
    public static void checkIsDimensionEntryControl(Object _ctrl, FormRunServiceArgs _isStdCtrl)
    {
        if (!(_ctrl is DimensionEntryControl))
        {
            return;
        }
        _isStdCtrl.cancel();
    }

    /// <summary>
    /// Получение информации о названии датасорса, указанном в пользовательском контроле. Название датасорса необходимо
    /// указать в классе FormPropertySet, добавив свойство (FormProperty) cPropertyDataSource, в котором и будет передано название
    /// </summary>
    /// <param name = "_ctrl">
    /// Анализируемый контрол
    /// </param>
    /// <param name = "_propertySet">
    /// Перечень свойств, через который будет передано название датасорса
    /// </param>
    delegate void getDataSourceCustomControl(Object _ctrl, FormPropertySet _propertySet)
    {
    }

    /// <summary>
    /// Обработчик делегата getDataSourceCustomControl для контрола DimensionEntryControl
    /// </summary>
    [SubscribesTo(classStr(FormControlShowDevInfo), delegateStr(FormControlShowDevInfo, getDataSourceCustomControl))]
    public static void getDataSourceDimensionEntryControl(Object _ctrl, FormPropertySet _propertySet)
    {
        if (!(_ctrl is DimensionEntryControl))
        {
            return;
        }
        DimensionEntryControl dimCtrl = _ctrl;
        DimensionEntryControlBuild dimCtrlBuild = dimCtrl.build();
        _propertySet.addProperty(FormControlShowDevInfo::cPropertyDataSource, Types::String, dimCtrlBuild.parmDataSourceName());
    }

    /// <summary>
    /// Получение информации о названии поля, указанного в пользовательском контроле. Название поля необходимо
    /// указать в классе FormPropertySet, добавив свойство (FormProperty) cPropertyDataFieldName, в котором и будет передано название
    /// </summary>
    /// <param name = "_ctrl">
    /// Анализируемый контрол
    /// </param>
    /// <param name = "_propertySet">
    /// Перечень свойств, через который будет передано название поля
    /// </param>
    delegate void getFieldNameCustomControl(Object _ctrl, FormPropertySet _propertySet)
    {
    }

    /// <summary>
    /// Обработчик делегата getFieldNameCustomControl для контрола DimensionEntryControl
    /// </summary>
    [SubscribesTo(classStr(FormControlShowDevInfo), delegateStr(FormControlShowDevInfo, getFieldNameCustomControl))]
    public static void getFieldNameDimensionEntryControl(Object _ctrl, FormPropertySet _propertySet)
    {
        if (!(_ctrl is DimensionEntryControl))
        {
            return;
        }
        DimensionEntryControl dimCtrl = _ctrl;
        DimensionEntryControlBuild dimCtrlBuild = dimCtrl.build();
        _propertySet.addProperty(FormControlShowDevInfo::cPropertyDataFieldName, Types::String, dimCtrlBuild.parmValueSetReferenceField());
    }

}
Разработка велась на версии приложения 10.0.14 (10.0.605.10014) и платформы Update38 (7.0.5778.35626)

Прикрепляю проект в Visual Studio, axpp-файл проекта и axmodel-файл модели (файл модели удобен для быстрой установки)
Вложения
Тип файла: zip FormControlShowDevInfo_VSProject.zip (14.3 Кб, 29 просмотров)
Тип файла: axmodel FormControlDevInfo-VSUH.axmodel (8.7 Кб, 25 просмотров)
Тип файла: axpp FormControlShowDevInfo.axpp (9.1 Кб, 28 просмотров)
__________________
Возможно сделать все. Вопрос времени

Последний раз редактировалось sukhanchik; 24.11.2020 в 00:00.
За это сообщение автора поблагодарили: raz (10), trud (10), S.Kuskov (10).
 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
Как правильно отобразить значение поля таблицы Skolos DAX: Программирование 12 25.02.2016 21:30
Отображение поля для пустых строк kit22 DAX: Программирование 9 20.08.2012 14:01
вызов метода из таблицы в операторе while select V777 DAX: Программирование 7 23.04.2008 09:11
Название поля и таблицы... NetBus DAX: Программирование 1 08.07.2005 16:45
Как по имени(ID) поля таблицы установить значение поля. AKit_3 DAX: Программирование 9 24.12.2004 19:03
Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход

Рейтинг@Mail.ru
Часовой пояс GMT +3, время: 19:53.
Powered by vBulletin® v3.8.5. Перевод: zCarot
Контактная информация, Реклама.