Как это было просто в обычном интерфейсе: ЭлементыФормы..ТекущиеДанные[ЭлементыФормы..ТекущаяКолонка.Имя]. И как это, оказывается, нетривиально для списков в УФ. Особенно в общем случае, который я и рассматривал, работая с ТекущимиДанными как с ДаннымиФормыбезотносительно их происхождения и наполнения.
Как известно, колонки динамического списка могут являться разыменованиями колонок-полей основной таблицы/запроса, причём они могут быть добавлены как в Конфигураторе, так и пользователем либо программистом в режиме Предприятие. И вот тут 1С немного "поленились": если для "штатно" объявленных полей таблицы/запроса ещё работает ТекущиеДанные[ИмяКолонки] и для них ТекущиеДанные.Свойство(ИмяКолонки) возвращает истину, то вот уже полей, добавленных в Конфигураторе как разыменование, хоть на 1 шаг вперёд, нет возможности получить их значение. Видеть - видим, программно не получаем. Но это ещё терпимо, т.к. для таких полей есть свойство их реквизита ПутьКДанным, по которому можно, исходя из "штатно" объявленных полей, вытащить значение.
Дальше интереснее. Поля, добавленные в режиме "Предприятие" как разыменование имевшихся (и неважно, штатно-объявленных основных полей или разыменованных в конфигураторе), - эти поля вообще никак не ущучить. Для них свойство "ПутьКДанным" вообще не поддерживается, не говоря уж о ТекущиеДанные[ИмяКолонки]. Видеть - видим, общий отбор обычным образом поставить можем, а получить видимое невооружённым глазом значение ячейки программно - нет.
Да, у таких добавленных разыменованных полей есть имена. Говорящие имена. И на этом были основаны пара решений, которые на ИС появились ещё в лохматые времена. НО: у них есть один недостаток. Как известно, работая с путями к данным, которые надо преобразовать в валидные имена 1С, любая СКД в системном представлении заменяет точки на знак подчёркивания. И вот теперь у нас есть развлечение: мы знаем имя поля, но в общем виде мы не гарантированы, что разработчик не употреблял знак подчёркивания в своих целях, а значит, нам придётся отличать, который из знаков подчёркивания в имени поля, например, вида Главное_Событие_Тип_События_Код задан разработчиком, а который подсунут СКД при преобразовании пути. СКД динамического списка тут не исключение.
Заметим также, что СКД ещё и стандартные реквизиты именует латиницей (DeletionMark, а не ПометкаУдаления), притом что локализованная 1С при переборе коллекции метаданных СтандартныеРеквизиты в их свойстве "Имя" возвращает-таки русскоязычное написание.
Ничего не скажу плохого про другие варианты решений, но проблему подчёркиваний там, насколько видел, не решали. Мне пришлось решить. Выкладываю прямо тут. Некоторая странность функции ПолучитьЗначениеТекущейЯчейки объясняется желанием уконтрапупить всё в одну функцию, чтобы удобнее было таскать между модулями. При желании функцию ПолучитьЭлементыИРеквизитыДинамическогоСписка также можно засунуть внутрь, указав соответствующий РежимВызова. Словом, место для извращений есть.
// Получает массив структур, описывающих Имя,Путь,ТипЗначения (как описание типов) для реквизитов,
// входящих в реквизит списка (в первую очередь это надо для колонок, но на всякий случай там все).
// При ошибке возвращает Неопределено.
//
// Параметры:
// рФорма - сериализованная (т.е. вызванная из &НаСервере) форма, где находится реквизит-список
// рИмяРеквизитаСписка - имя реквизита формы, который рассматриваем
//
Функция ПолучитьЭлементыИРеквизитыДинамическогоСписка(рФорма,рИмяРеквизитаСписка) Экспорт
Попытка
рез=Новый Массив;
трек=Новый ТаблицаЗначений;
трек.Колонки.Добавить("Имя");
трек.Колонки.Добавить("Путь");
трек.Колонки.Добавить("ТипЗначения");
мрек=рФорма.ПолучитьРеквизиты(рИмяРеквизитаСписка);
Для каждого рек Из мрек Цикл
ЗаполнитьЗначенияСвойств(трек.Добавить(),рек);
КонецЦикла;
//
Для каждого эл Из рФорма.Элементы[рИмяРеквизитаСписка].ПодчиненныеЭлементы Цикл
стротрек=трек.Найти(эл.Имя,"Имя");
Если стротрек=Неопределено Тогда
// ищем через разыменование его пути
мстро=ОбщегоНазначения.РазложитьСтрокуВМассивПодстрок(эл.ПутьКДанным,".");
Если мстро.Количество()<2 Тогда Продолжить КонецЕсли;
// поэтапно разыменовываем
рПутьВычисления=СокрЛП(мстро[0]);
Для й=1 По мстро.Количество()-2 Цикл
рИмяПредка=СокрЛП(мстро.Получить(й));
рИмяЗвена=СокрЛП(мстро.Получить(й+1));
стротрек=трек.Найти(рИмяПредка,"Имя");
Если стротрек=Неопределено Тогда
Сообщить("Предыдущее звено "+рИмяПредка+" не найдено в таблице реквизитов!",СтатусСообщения.Важное);
Иначе
мТипов=Новый Массив; // массив возможных типов значений звена пути, разыменованного от кого-то исходного
Для каждого рТипПредка Из стротрек.ТипЗначения.Типы() Цикл
метадан=Метаданные.НайтиПоТипу(рТипПредка);
Если метадан=Неопределено Тогда // возможно, это вообще значение атомарного типа
//Сообщить("Для типа "+Представления.ПредставлениеТипа(рТипПредка)+" не удалось найти метаданные!",СтатусСообщения.Важное);
Иначе
// ищем реквизит (ничем более, полагаем пока, оно являться не может, хоть при динамсписке, хоть статично)
метарек=метадан.Реквизиты.Найти(рИмяЗвена);
Если метарек=Неопределено Тогда
// ищем среди стандартных
Для каждого стандрек Из метадан.СтандартныеРеквизиты Цикл
Если стандрек.Имя=рИмяЗвена Тогда метарек=стандрек; Прервать КонецЕсли;
КонецЦикла;
КонецЕсли;
Если метарек=Неопределено Тогда
Сообщить("Для метаобъекта "+метадан.Представление()+" не найден реквизит "+рИмяЗвена+"!",СтатусСообщения.Важное);
Иначе
рПутьВычисления=рПутьВычисления+"."+метарек.Имя;
Для каждого рТип Из метарек.Тип.Типы() Цикл
мТипов.Добавить(рТип);
КонецЦикла;
КонецЕсли; // если у метареквизитов предка есть такой
КонецЕсли; // если в метаданных нашли метапредка по одному из типов
КонецЦикла; // по типам значений предка
Если мТипов.Количество()<>0 Тогда // вносим в таблицу реквизитов текущее звено
рТекущееОписТипов=Новый ОписаниеТипов(мТипов);
рПуть=""; разд="";
Для ы=1 По й+1 Цикл
рПуть=рПуть+разд+СокрЛП(мстро[ы]); разд=".";
КонецЦикла;
Если трек.НайтиСтроки(Новый Структура("Имя,Путь",рИмяЗвена,рПуть)).Количество()=0 Тогда
стротрек=трек.Добавить();
стротрек.Имя=рИмяЗвена;
стротрек.Путь=рПуть;
стротрек.ТипЗначения=рТекущееОписТипов;
КонецЕсли;
КонецЕсли;
КонецЕсли; // если запись о предке нашли в таблице реквизитов
КонецЦикла; // по массиву кусков пути
// если всё сделали верно, то теперь такое поле в таблице реквизитов есть, вносим
стру=Новый Структура;
стру.Вставить("Имя",эл.Имя);
стру.Вставить("Путь",рПуть);
стру.Вставить("ТипЗначения",рТекущееОписТипов);
рез.Добавить(стру);
Иначе
стру=Новый Структура;
стру.Вставить("Имя",эл.Имя);
стру.Вставить("Путь",СтрЗаменить(эл.ПутьКДанным,"Список.",""));
стру.Вставить("ТипЗначения",стротрек.ТипЗначения);
рез.Добавить(стру);
КонецЕсли;
КонецЦикла;
//
Возврат рез;
Исключение
Сообщить("ПолучитьЭлементыИРеквизитыДинамическогоСписка, ошибка: "+ОписаниеОшибки(),СтатусСообщения.ОченьВажное);
Возврат Неопределено;
КонецПопытки;
КонецФункции
&НаСервере
// Получение из динам.списка на форме того значения, что находится на пересечении текущей строки и текущей колонки.
// При ошибке возвращает Неопределено.
//
// Входные параметры:
// рПараметры - структура, обязательно содержащая поля ТекущиеДанные (из списка) и ИмяПоля (строковое имя тек.колонки)
// также можно передать массив структур "ЭлементыИРеквизиты", получаемый ПолучитьЭлементыИРеквизитыДинамическогоСписка();
// поле "РежимВызова" можно не указывать, или указывать пустой строкой, или равным "Получение". Прочие варианты - служебные!
//
// Функция содержит внутри себя, по сути, 1 функцию и 1 процедуру, для удобства копипаста объединены, различаются Режимом.
// ВАЖНО: Функция работает, исходя из условия, что элемент динам.списка и реквизит динам.списка имеют одинаковые имена!!!
//
Функция ПолучитьЗначениеТекущейЯчейки(рПараметры)
рРежим=?(рПараметры.Свойство("РежимВызова"),СокрЛП(рПараметры.РежимВызова),"");
Если ПустаяСтрока(рРежим) Тогда рРежим="Получение" КонецЕсли;
Если рРежим="Получение" Тогда
текдан=рПараметры.ТекущиеДанные;
рИмяПоля=рПараметры.ИмяПоля; // имя текущей колонки
ЭиР=?(рПараметры.Свойство("ЭлементыИРеквизиты"),рПараметры.ЭлементыИРеквизиты,Неопределено);
//
Если ЭиР=Неопределено Тогда
ЭиР=ПолучитьЭлементыИРеквизитыДинамическогоСписка(ЭтаФорма,рПараметры.ИмяЭлементаИРеквизитаСписка);
рПараметры.Вставить("ЭлементыИРеквизиты",ЭиР);
КонецЕсли;
//
// здесь же, если надо, преобразование англоязычных служебных полей в русскую нотацию (вместо _Code делаем _Код)
Если текдан.Свойство(рИмяПоля) Тогда // штатно объявленное поле 1-го уровня
рЗначение=текдан[рИмяПоля];
рПараметры.Вставить("ПутьКДанным",рИмяПоля);
Иначе // разыменованное как-либо поле
// ищем среди сведений об элементах и реквизитах
рНужное=Неопределено;
Для каждого эл Из ЭиР Цикл
Если Лев(рИмяПоля,СтрДлина(СокрЛП(эл.Имя)))=СокрЛП(эл.Имя) Тогда
рНужное=эл; Прервать;
КонецЕсли;
КонецЦикла;
Если рНужное<>Неопределено Тогда // скорее всего, разыменованное как-либо
Если СокрЛП(рНужное.Имя)=рИмяПоля Тогда // точное совпадение - значит, разыменование было в конфигураторе
Выполнить("рЗначение=текдан."+СокрЛП(рНужное.Путь));
рПараметры.Вставить("ПутьКДанным",рНужное.Путь);
Иначе // скорее всего, разыменованное от разыменованного, в т.ч. добавленное динамически
// первый шаг надо делать от имеющихся полей текдан, и они возможны любые, поэтому привлекаем ЭиР,
// а далее возможны лишь их реквизиты, т.е. простые разыменования, и там вся задача лишь в преобразовании
// имеющегося сейчас пути (только знаки подчёркивания) в путь правильного формата, через точку (где надо).
//
рПараметрыДалее=Новый Структура;
рПараметрыДалее.Вставить("РежимВызова","Разыменование");
рПараметрыДалее.Вставить("ИмяЭлементаИРеквизитаСписка",рПараметры.ИмяЭлементаИРеквизитаСписка);
рПараметрыДалее.Вставить("ИмяПуть",рИмяПоля);
рПараметрыДалее.Вставить("ОсталосьРазобрать",рИмяПоля);
рПараметрыДалее.Вставить("ИмяИсходногоПоля",СокрЛП(рНужное.Имя));
рПараметрыДалее.Вставить("ОписТиповИсходногоПоля",рНужное.ТипЗначения);
ПолучитьЗначениеТекущейЯчейки(рПараметрыДалее);
рИмяПуть=рПараметрыДалее.ИмяПуть;
//
рПараметрыДалее=Новый Структура("ТекущиеДанные,ИмяПоля,ЭлементыИРеквизиты",текдан,СокрЛП(рНужное.Имя),ЭиР);
рЗначениеИсхПоля=ПолучитьЗначениеТекущейЯчейки(рПараметрыДалее);
рПутьБезИсходного=СтрЗаменить(рИмяПуть,СокрЛП(рНужное.Имя)+".","");
Выполнить("рЗначение=рЗначениеИсхПоля."+СокрЛП(рПутьБезИсходного));
рПараметры.Вставить("ПутьКДанным",рИмяПуть);
КонецЕсли;
Иначе
рЗначение=Неопределено; // такого имени не нашли по началу строки ни одного из имён элементов (некая неучтённая ситуация)
рПараметры.Вставить("ПутьКДанным","");
КонецЕсли;
КонецЕсли;
//
Возврат рЗначение;
ИначеЕсли рРежим="Разыменование" Тогда
рИмяПуть=рПараметры.ИмяПуть;
рОсталосьРазобрать=рПараметры.ОсталосьРазобрать;
рИмяИсходногоПоля=рПараметры.ИмяИсходногоПоля;
рОписТиповИсходногоПоля=рПараметры.ОписТиповИсходногоПоля;
//
рИмяПуть=СтрЗаменить(рИмяПуть,рИмяИсходногоПоля+"_",рИмяИсходногоПоля+".");
рПараметры.Вставить("ИмяПуть",рИмяПуть); // это должно меняться внутри процедуры
//
Если Найти(рОсталосьРазобрать,рИмяИсходногоПоля+"_")<>0 Тогда
рОсталосьРазобрать=СтрЗаменить(рОсталосьРазобрать,рИмяИсходногоПоля+"_","");
Иначе
Если рОсталосьРазобрать=рИмяИсходногоПоля Тогда Возврат Неопределено КонецЕсли; // уже всё
КонецЕсли;
Если ПустаяСтрока(рОсталосьРазобрать) Тогда Возврат Неопределено КонецЕсли; // уже всё
//
// и теперь от этого исходного ищем уже просто в реквизитах, перебирая все возможные типы исходного поля
рИмяНужногоРеквизита="";
мТиповИтого=Новый Массив;
Для каждого рТип Из рОписТиповИсходногоПоля.Типы() Цикл
#Если Клиент Тогда
ОбработкаПрерыванияПользователя();
#КонецЕсли
рНужныйРеквизит=Неопределено;
метадан=Метаданные.НайтиПоТипу(рТип);
Если метадан=Неопределено Тогда Продолжить КонецЕсли;
Для каждого метарек Из метадан.Реквизиты Цикл
Если Лев(рОсталосьРазобрать,СтрДлина(метарек.Имя))=метарек.Имя Тогда рНужныйРеквизит=метарек; Прервать КонецЕсли;
КонецЦикла;
Если рНужныйРеквизит=Неопределено Тогда // ищем среди стандартных
Для каждого метарек Из метадан.СтандартныеРеквизиты Цикл
Если Лев(рОсталосьРазобрать,СтрДлина(метарек.Имя))=метарек.Имя Тогда рНужныйРеквизит=метарек; Прервать КонецЕсли;
КонецЦикла;
КонецЕсли;
// у объектов с разными типами и метатипами могут быть одноимённые реквизиты с разными типами значений.
// поэтому имя-то общее, а вот типы надо накопить, чтобы разыменовать все возможные варианты значений
Если рНужныйРеквизит<>Неопределено Тогда
рИмяНужногоРеквизита=рНужныйРеквизит.Имя;
Для каждого рТипРеквизита Из рНужныйРеквизит.Тип.Типы() Цикл
мТиповИтого.Добавить(рТипРеквизита);
КонецЦикла;
КонецЕсли;
КонецЦикла;
// если есть какие-то типы и имя для реквизита, можем рассмотреть следующий шаг
Если мТиповИтого.Количество()<>0 и не ПустаяСтрока(рИмяНужногоРеквизита) Тогда
рПараметрыДалее=Новый Структура;
рПараметрыДалее.Вставить("РежимВызова","Разыменование");
рПараметрыДалее.Вставить("ИмяПуть",рИмяПуть);
рПараметрыДалее.Вставить("ОсталосьРазобрать",рОсталосьРазобрать);
рПараметрыДалее.Вставить("ИмяИсходногоПоля",рИмяНужногоРеквизита);
рПараметрыДалее.Вставить("ОписТиповИсходногоПоля",Новый ОписаниеТипов(мТиповИтого));
ПолучитьЗначениеТекущейЯчейки(рПараметрыДалее);
рПараметры.Вставить("ИмяПуть",рПараметрыДалее.ИмяПуть); // это должно меняться внутри процедуры
КонецЕсли;
КонецЕсли;
КонецФункции
Что ещё из интересного успел пронаблюдать:
1. В настройке формы поля с подчёркиванием и без штатных представлений ооочень забавно выглядят... Посмотрите сами ))
2. При изменении данных объявленные в конфигураторе обновляются, а добавленные динамически в 1С - нет. Приходится переоткрывать форму. Или это у меня релиз такой (19.83), или лыжи не совсем едут.
3. Несмотря на то, что стандартные реквизиты в служебных именах СКД списка даёт латиницей, в диалоге настройки списка установленные исходя из англоязычных термов отборы представлены по-русски.
4. Если разыменованное поле имеет тип Булево, но путь разыменования прерван пустым значением (т.е. до конечного значения просто нельзя дойти), показ псевдозначения поля в этой колонке всё равно будет, равный "нет", но при отборе по "Нет" он эту строку в удовлетворяющие отбору не включит. Спецэффект, вызывающий недоумение пользователей.
И "до кучи" предлагаю процедуру установки "быстрого отбора" по значению текущей колонки. Да, есть отборы, вызываемые через "Настроить список", да, есть поиск с множественным отображением, но привычка пользователя - страшная штука, да и нажатий меньше.
&НаКлиенте
// Установка отбора по значению текущей колонки в текущей строке
// Параметры:
// рИмяСписка - имя элемента формы списка, оно же имя реквизита списка
//
// ВАЖНО: Функция работает, исходя из условия, что элемент динам.списка и реквизит динам.списка имеют одинаковые имена!!!
//
Процедура УстановитьОтборПоЗначениюТекущейКолонки(рИмяСписка)
Попытка
текдан=Элементы[СокрЛП(рИмяСписка)].ТекущиеДанные;
Если текдан=Неопределено Тогда Возврат КонецЕсли; // вообще ничего не ставим и не сообщаем
теккол=Элементы[СокрЛП(рИмяСписка)].ТекущийЭлемент;
рПараметры=Новый Структура("ТекущиеДанные,ИмяПоля",текдан,теккол.Имя);
рПараметры.Вставить("ИмяЭлементаИРеквизитаСписка",СокрЛП(рИмяСписка));
рЗначение=ПолучитьЗначениеТекущейЯчейки(рПараметры);
Если рЗначение=Неопределено Тогда
Предупреждение("Отбор по текущей колонке невозможен, нельзя получить значение отбора!"); Возврат
КонецЕсли;
мстро=ОбщегоНазначения.РазложитьСтрокуВМассивПодстрок(рПараметры.ПутьКДанным,".");
рИсходноеПоле=СокрЛП(мстро.Получить(0));
ЭиР=рПараметры.ЭлементыИРеквизиты;
рНачалоПути="";
Для каждого эл Из ЭиР Цикл
Если СокрЛП(эл.Имя)=рИсходноеПоле Тогда
рНачалоПути=СокрЛП(эл.Путь); Прервать;
КонецЕсли;
КонецЦикла;
Если ПустаяСтрока(рНачалоПути) Тогда
Предупреждение("Отбор по текущей колонке невозможен, нельзя определить путь отбора!"); Возврат
КонецЕсли;
рИтоговыйПуть=СтрЗаменить(рПараметры.ПутьКДанным,рИсходноеПоле+".",рНачалоПути+".");
рПоле=Новый ПолеКомпоновкиДанных(рИтоговыйПуть);
//
// У отбора динам.списка есть интересный глюк: он не может фигурировать в процедурах/функциях как
// аргумент, если есть шансы, что он там изменится (т.е. не объявлен в параметрах через Знач), поэтому
// в ПолучитьОтборСКД он определяется именно так; в результате элотб как элемент отбора хранит все
// нужные данные, но НЕ наследуется от Список.Отбор и поэтому не реагирует на изменение свойств;
// в частности, ему бесполезно ставить Используется=Истина; поэтому удаляем и заново создаём его.
// поскольку это, как правило, отбор первого уровня (т.е. группы не используются), то чуть схалявим
// и обойдёмся без рекурсии. При прямом поиске перебором элемент "наследуется".
элотб=Неопределено;
Для каждого эл Из ЭтаФорма[СокрЛП(рИмяСписка)].Отбор.Элементы Цикл
Если эл.ЛевоеЗначение=рПоле Тогда элотб=эл; Прервать КонецЕсли;
КонецЦикла;
Если элотб<>Неопределено Тогда
ЭтаФорма[СокрЛП(рИмяСписка)].Отбор.Элементы.Удалить(элотб);
КонецЕсли;
элотб=ЭтаФорма[СокрЛП(рИмяСписка)].Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
элотб.ЛевоеЗначение=рПоле;
элотб.ВидСравнения=ВидСравненияКомпоновкиДанных.Равно;
элотб.ПравоеЗначение=рЗначение;
элотб.Использование=Истина;
Исключение
Сообщить("УстановитьОтборПоЗначениюТекущейКолонки, ошибка: "+ОписаниеОшибки(),СтатусСообщения.ОченьВажное);
КонецПопытки;
КонецПроцедуры
&НаКлиенте
// Собственно отработка команды
Процедура ОтборПоТекущейКолонке(Команда)
УстановитьОтборПоЗначениюТекущейКолонки("Список");
КонецПроцедуры