16.02.2024, 19:22 | #1 |
Участник
|
SysLookupMultiselectCtrl отметить значения
D365 id
На форме добавлен фильтр (используется для фильтрации данных в нижерасположенном гриде) При нажатии на него выпадает multiselect lookup. Состоит он из одной колонки значений. Запрос, который передается при создании этого лукапа, он с группировками, поэтому recid там нет. Как заставить SysLookupMultiselectCtrl при переоткрывании фильтра отметить ранее выбранные значения? (из того, что вижу в SysLookupMultiselectGrid->markSelected(), кажется, что невозможно отметить без recid, но надежда умирает последней) И еще вопрос: для отлавливания lookup события создан класс-handler формы, где есть handler myFilterControl_onLookup. Когда использовала обычный systablelookup , то этот myFilterControl_onLookup вызывался . Когда заменила на SysLookupMultiselectCtrl , то почему-то это событие перестало отлавливаться. Как так? Оно же от контрола формы, а не типа выпадающего лукапа зависеть должно Спасибо Последний раз редактировалось Lankey; 16.02.2024 в 19:48. |
|
16.02.2024, 20:05 | #2 |
Участник
|
|
|
|
За это сообщение автора поблагодарили: Lankey (1). |
16.02.2024, 22:41 | #3 |
Участник
|
Гениально! Спасибо
Да, так работает, но добавляется колонка с RecId в лукап Получается не очень user friendly ( |
|
17.02.2024, 10:14 | #4 |
Участник
|
Имхо, системный SysLookupMultiselectGrid сделан с багом
Его метод markSelected позволяет отметить строки на основании любого поля, то есть не обязательно recId (он для этого использует ds.positionRecordByValue()) Но! это работает только, если контейнер с RecId выбранных строк пуст А он никогда не будет пуст, тк даже если поле RecID отсутствует в запросе (как в моем случае с группировкой) , то в методе getSelected просто забивается 0 в контейнер : selectedID +=common.RecId. То есть, создается контейнер с количеством нулей равным количеству выбранных строк .. и он поэтому никогда пустым не будет --- Если же, как workaround, явно добавить RecID в запрос через max(recID), то RecID, как и все другие поля datasource, автоматически добавляются в grid в методе buildDesign, поэтому скрыть его невозможно. |
|
17.02.2024, 17:50 | #5 |
Участник
|
Можно создать View на основе такого сгруппировано запроса с RecId. И уже по этому View строить лукап. RecId не нужно будет явно указывать в списке полей, но в выборке по View он будет. Должно сработать
|
|
17.02.2024, 21:28 | #6 |
Участник
|
Хорошая идея, спасибо
Я попробовала, но, когда указываю Max (RecId) в запросе, что использую потом во view, то view создается с константным значением(1010 в моем случае) в колонке RecId, а не результатом агрегатной функции (я посмотрела сформированный запрос на стороне sql server select....Myfield as Myfield, 1010 as RecId from MyTable group by Myfield) Пробовала поменять на Min - то же самое происходит. Попробовала без запроса, т.е напрямую во view выбрать max(RecId), но он тогда колонку переименовывает в RecId1 (и выдает ошибку "Cannot rename RecId1 to RecId . Table field name cannot be the same as system defined name") |
|
19.02.2024, 21:23 | #7 |
Участник
|
Попробовала с временной таблицей, но не понимаю, каким образом ее буфер передать в SysLookupMultiselectGrid? (Тут нет parmTmpBuffer() как на SysTableLookup )
Есть какая-то возможность это обойти? Последний раз редактировалось Lankey; 19.02.2024 в 22:36. |
|
20.02.2024, 07:32 | #8 |
Участник
|
Если говорить про техническую реализацию, то примеры есть в стандарте - класс EREnumLookupMultiSelectGrid, метод new, передача через QueryRun.
X++: container selectedFields = [tableNum(SysOperationMultiSelectTmp), fieldName2id(tableNum(SysOperationMultiSelectTmp), fieldStr(SysOperationMultiSelectTmp, Values))]; selectTableTmp = this.getMultiSelectTableForEnum(_enumId, _valuesToSkip); QueryRun localQueryRun = SysOperationHelper::getMultiSelectQueryRun(selectTableTmp); this.parmCallingControl(_targetStringControl); this.parmQuery(localQueryRun.query()); this.parmQueryRun(localQueryRun); Также можно в качестве времянки использовать TempDB, заполнять ее прямым запросом через Query::insert_recordset, будет работать быстро, но выглядит это все как какой то Overengineering, ради лукапа. Я бегло посмотрел по перекрестным ссылкам в стандарте, есть пример (да там нет группировки, может быть можно сделать примерно так же - скрытый контрол с recId - StatisticsOnInvoiceUIBuilder?). X++: private void initPostingProfilesDialogField() { DialogField postingProfilesField = this.bindInfo().getDialogField(this.dataContractObject(), methodStr(StatisticsOnInvoiceDataContract, parmPostingProfiles)); postingProfilesField.registerOverrideMethod( methodStr(FormDateControl, lookup), methodStr(StatisticsOnInvoiceUIBuilder, postingProfilesLookup), this); postingProfilesField.lookupButton(FormLookupButton::Always); SysOperationDialog reportDialogBox = this.dialog(); postingProfilesRecIdsControl = reportDialogBox.formRun().design().addControl(FormControlType::String, PostingProfilesRecIdsControlName); postingProfilesRecIdsControl.visible(false); } private void postingProfilesLookup(FormStringControl _postingProfilesControl) { Query query = new Query(); QueryBuildDataSource qbds = query.addDataSource(tableNum(VendLedger)); QueryBuildFieldList qbfl = qbds.fields(); qbfl.dynamic(false); qbfl.clearFieldList(); qbfl.addField(fieldNum(VendLedger, PostingProfile)); qbfl.addField(fieldNum(VendLedger, Name)); container selectFields = [ tableNum(VendLedger), fieldNum(VendLedger, PostingProfile) ]; SysLookupMultiSelectGrid::lookup( query, _postingProfilesControl, postingProfilesRecIdsControl, _postingProfilesControl, selectFields); }
__________________
Sergey Nefedov Последний раз редактировалось SRF; 20.02.2024 в 07:39. |
|
|
За это сообщение автора поблагодарили: Lankey (1). |
20.02.2024, 11:07 | #9 |
Участник
|
Цитата:
Сообщение от Lankey
Имхо, системный SysLookupMultiselectGrid сделан с багом
Его метод markSelected позволяет отметить строки на основании любого поля, то есть не обязательно recId (он для этого использует ds.positionRecordByValue()) Но! это работает только, если контейнер с RecId выбранных строк пуст А он никогда не будет пуст, тк даже если поле RecID отсутствует в запросе
__________________
- Может, я как-то неправильно живу?! - Отчего же? Правильно. Только зря... |
|
01.03.2024, 09:46 | #10 |
Участник
|
Цитата:
А какая раница между Lookup vs OnLookup в 365? На контроле формы (форма тут моя, в моей модели, т.е кастомная) доступен метод Lookup() и его можно переопределить в коде формы. В вышеуказанных примерах стандарта именно он используется. Я делала изначально через класс event handler и там описала OnLookup контрола. Вроде, оба, и Lookup и OnLookup, отрабатывают. То есть, нет разницы. Только писать лишний класс ради одного метода-обработника события как-то слишком. Думаю оставить подход через lookup |
|
01.03.2024, 11:21 | #11 |
Administrator
|
Для своей формы технически разницы нет. Однако с т.з. дальнейших раскопок кода всё-таки лучше как-то договориться внутри команды разработки об использовании единого подхода (мне, лично нравится Lookup)
Разница появляется тогда, когда надо сделать расширение и нет возможности сделать / использовать Lookup. Тогда использование event handler-а очень помогает. Правда тут опять-таки подводный камень в том, чтобы договориться внутри команды разработки об использовании единого подхода. Иначе можно написать много разных Event Handler-ов в разных классах, после чего они все дружно вызовутся, пользователь удивится, а разработчик, который будет искать эту ошибку - долго будет искать причину проблемы.
__________________
Возможно сделать все. Вопрос времени |
|
|
За это сообщение автора поблагодарили: Lankey (1). |
07.03.2024, 22:47 | #12 |
Участник
|
Так, все работает, но другой глупый вопрос появился:
Этот multiselect фильтрует данные на форме. То есть, в init я на DS добавляю range и в executeQuery на этот query().datasource().range добавляю условие. Вроде. стандартный подход. Все работает. Ок На этой форме грид, в нем колонка, и, соответственно, стандартная созможность добавить по ней фильтр. И это та же колонка, по которой фильтрует отдельно существующий в шапке формы multiselect. То есть, по факту, на форме две возможности фильтровать одну и ту же колонку : мой мультиселект и стандартный фильтр в шапке грида. Теперь : Если пользователь меняет стд фильтр, то они хотят , чтобы это изменение отражалось в мультиселекте. Например, убрал один из критериев, и в мультиселекте он должен исчезнуть тоже. Ну ок, я после super() в executeQuery отлавливаю range.value , что пользователь установил, и передаю в mymultiselect.text() Меня озадачивает, что пользовательские фильтры накладываются на queryRun, а не query. То есть, получается, мне надо изменить мой подход для multiselect - не создавать range в init на query().datasource(), а создавать range и меня ть только на queryRun в executeQuery. Это правильный подход? |
|
Теги |
d365 |
|
|