Всем доброго времени суток.

Помогите, пожалуйста, уже всю голову сломала...

Каким образом можно программно "добраться" до быстрого фильтра??? (установить его и применить).

Или может быть существуют другие (более простые) решения моей задачи: при добавлении к записи раздела файлов из Библиотеки необходимо, чтобы в окне Библиотеки отображалась только конкретная запись (ID ее есть). ApplyDatasetFilter почему-то сбрасывается после отработки функции обработчика, и окно Библиотеки открывается со всеми записями.

Версия 3.2.

Заранее спасибо.

Нравится

9 комментариев

Программно установить быстрый фильтр не получится.

Фильтрация при выборе значения из справочника.

У меня не работает фильтрация... Выводятся все записи.
Вот код: (вызывается по кнопке из грида документов)

function btnAddShablonOnClick(Control) {
	var WorkspaceWindow = GetAttribute(Self, 'WorkspaceWindow');
	var DetailPages = WorkspaceWindow.ComponentsByName('pcDetails');
	var FilesPage = WorkspaceWindow.ComponentsByName('pgFilesDetail');
	var FilesDetailWindow = 
		WorkspaceWindow.ComponentsByName('wndFilesDetail').Window;
	var FileDataWindow = FilesDetailWindow.ComponentsbyName('wndFileData').Window;  
 
	var SelWin = ShowSelectWorkspaceWindow('wnd_LibraryWorkspace', FileDataWindow);
	NotVisibleDetails(SelWin.ComponentsByName('wnddata').Window.
		ComponentsbyName('pcDetails'));
	SelWin.ComponentsByName('wnddata').Window.ComponentsbyName('pcDetails').
		ItemsByName('pgFilesDetail').IsVisible = true;
 
	var DocTypeID = BaseGridArea.GridDataset.DataFields.
		ItemsByName('DocumentTypeID').Value;
	var LibID = GetDatasetFieldValueByID('ds_DocumentType', DocTypeID,
			'ShablonGroupID');
	SetAttribute(SelWin.ComponentsByName('wnddata').Window.
		ComponentsByName('wndGridData').Window, 'ParentItemID', LibID);
	var DataSet = SelWin.ComponentsByName('wnddata').Window.
		ComponentsByName('wndGridData').Window.ComponentsByName('dlData').Dataset;
 
	var IsEnabled = !IsEmptyValue(LibID);
	ApplyDatasetFilter(DataSet, 'ID', LibID, IsEnabled);
 
}

Была в отпуске, не могла об этом думать )

После применения фильтра датасет нужно переоткрывать.

Пробовала. Все равно не фильтруется.

....
DataSet.Close();
DataSet.Open();
}

Попробуйте перед применением фильтра закрывать датасет, а после - открывать. Например, так:

if (DataSet.State != dstInactive) {
    DataSet.Close();
}
ApplyDatasetFilter(DataSet, 'ID', LibID, IsEnabled);
DataSet.Open();

Вообще я правильно понимаю, что Вы сначала открываете окно (с помощью функции ShowSelectWorkspaceWindow), а потом накладываете фильтр на датасет? Попробуйте наоборот: сначала применить фильтр, а потом открывать окно.

Олег Лабьяк,
разработчик,
3-я линия Службы поддержки Terrasoft.

Закрывать - применять фильтр - открывать - это я тоже пробовала, не применяется...
А с применением фильтра до открытия окна сложность заключается в том, как взять нужный датасет и передать его... тк заполнение всех компонентов окна выполняется совсем в других местах (скриптах). Но я как раз думаю над этим. Спасибо.

А если в окно передавать уже отфильтрованный датасет? Применили фильтр, а потом

Window.ComponentsByName('dlData').Dataset = OurFilteredDataset;

Олег Лабьяк,
разработчик,
3-я линия Службы поддержки Terrasoft.

Вот так заработало. Олег, огромное спасибо за помощь.

function btnAddShablonOnClick(Control) {
	var WorkspaceWindow = GetAttribute(Self, 'WorkspaceWindow');
	var DetailPages = WorkspaceWindow.ComponentsByName('pcDetails');
	var FilesPage = WorkspaceWindow.ComponentsByName('pgFilesDetail');
	var FilesDetailWindow = 
		WorkspaceWindow.ComponentsByName('wndFilesDetail').Window;
	var FileDataWindow = FilesDetailWindow.ComponentsbyName('wndFileData').Window; 
 
	var DocTypeID = BaseGridArea.GridDataset.DataFields.
		ItemsByName('DocumentTypeID').Value;
	var LibID = GetDatasetFieldValueByID('ds_DocumentType', DocTypeID,
			'ShablonGroupID');
	var IsEnabled = !IsEmptyValue(LibID);
 
	var LibDataset = Services.GetNewItemByUSI('ds_Library');
	ApplyDatasetIDFilter(LibDataset, LibID, IsEnabled);
	var SelWin = Services.GetNewItemByUSI('wnd_SelectWorkspaceData');
	SetAttribute(SelWin, 'NotifyObject', FileDataWindow);
	SetAttribute(SelWin, 'WorkspaceUSI', 'wnd_LibraryWorkspace');
	System.BeginProcessing();
	try {
		SelWin.Prepare();
		SelWin.ComponentsByName('wnddata').Window.ComponentsByName('wndGridData').
			Window.ComponentsByName('dlData').Dataset = LibDataset;
		SelWin.Show();
		LibDataset.Open();
	} finally {
		System.EndProcessing();
	}
 
	NotVisibleDetails(SelWin.ComponentsByName('wnddata').Window.
		ComponentsbyName('pcDetails'));
	SelWin.ComponentsByName('wnddata').Window.ComponentsbyName('pcDetails').
		ItemsByName('pgFilesDetail').IsVisible = true;
 
}

(функция добавления шаблонов документов из библиотеки, в зависимости от типа документа)

Показать все комментарии

Подскажите как включать быстрый фильтр из скрипта?

Мне нужно в окне подбора товара (картинку я прикрепил) наложить фильтр по торговой марке и чтобы было видно, что этот фильтр наложен (быстрый фильтр подходит). Также необходимо, чтобы этот фильтр не убирался при перемещении по дереву групп товаров - быстрый фильтр удовлетворяет этому условию.

Только вот непонятно как мне из скрипта включить этот фильтр. На картинке я показал как это будет выглядеть, если я вручную задам этот фильтр в окне.

Нравится

14 комментариев

Теоретически:
Поле и значение

"Глова Сергей" написал:Теоретически:
Поле и значение

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

Ошибка выполнения Microsoft JScript: Объект не поддерживает это свойство или метод

Получается, что эти параметры нельзя редактировать, их можно только считывать...

Жаль :sad:

нда. не посмотрел, что они ридонли.

Андрей, а почему Вы именно "быстрый" фильтр? Возможно Вас устроит функционал, фильтра? Посмотрите как происходит фильтрация, когда Вы переходите по контекстному меню в любом реестре раздела - Ответственный, Контрагент и т.п.

Андрей, добрый день.

Быстрый фильтр из конфигурации включить нельзя. Его можно только очистить. Если Вам подходит функционал модуля фильтрации, то есть такое решение:

1) создаём функции для создания и применения фильтра в FiltersBuilder раздела:

function FilterWorkspace(WorkspaceWindow, DataFieldName, DataFieldValue) {
	WorkspaceWindow.ComponentsByName('wndGridData').Window.
		ComponentsByName('grdData').CancelQuickFilter();
	FilterByFieldValue(WorkspaceWindow, DataFieldName, DataFieldValue);	
}
 
function FilterByFieldValue(WorkspaceWindow, DataFieldName, DataFieldValue) {
	var FBControl = WorkspaceWindow.ComponentsByName('fbcFilters');
	var Dataset = FBControl.DatasetLink.Dataset;
	FBControl.FiltersBuilder.ClearFilter();
	var DataField = Dataset.DataFields.ItemsByName(DataFieldName);
	var FBItem = FBControl.FiltersBuilder.RootItems.CreateItemByDataField(
		DataField);
	FBItem.Value = DataFieldValue;
	FBItem.Operator = totEqual;
	FBControl.FiltersBuilder.RootItems.Add(FBItem);
	EnableDatasetFilters(Dataset, false);
	FBControl.ApplyFilter();
	FBControl.Refresh();
}

2) дальше в функции, где Вы хотите применить фильтр, пишем следующее:

var WorkspaceWindow = Connector.Attributes('MainWindow').
		ComponentsByName('wndWorkspace').Window;
FilterWorkspace(WorkspaceWindow, 'Name', "Ваша компания");

В данном примере я использовал раздел "Контрагенты", фильтр по названию.

Условия для корректной работы данного функционала: наличие фильтра в основном запросе раздела; функционал используется для активного на данный момент раздела.

Олег Лабьяк,
разработчик,
3-я линия Службы поддержки Terrasoft.

Спасибо за помощь.
Мне нужно было именно, чтобы кроме фильтра, который накладывается при выборе группы продуктов в дереве груп, всегда был включен фильтр по торговой марке.

Я решил не использовать быстрый фильтр, а просто переписал функцию dlOfferingOnDatasetBeforeOpen, которая срабатывает на событие OnDatasetBeforeOpen датасета с продуктами:

function dlOfferingOnDatasetBeforeOpen(Dataset, DoOpen) {
 ApplyStandardWorkspaceFilter();
 ApplyFilterToQuickOfferingSelectionGrid();
}

где ApplyFilterToQuickOfferingSelectionGrid - функция, в которой я накладываю необходимый мне фильтр, после накладывания всех фильтов FilterBuilder-а (ф-я ApplyStandardWorkspaceFilter).

// Функция накладывает фильтр на датасет окна подбора товара,
// если в окно подбора товара была передана информация о фильтре
function ApplyFilterToQuickOfferingSelectionGrid() {
 var Dataset = BaseWorkspace.GridDataset;
 var FilterFieldName = GetAttribute(Self,'FilterFieldName');
 var FilterFieldValue = GetAttribute(Self,'FilterFieldValue');
 if (IsEmptyStr(FilterFieldName) || IsEmptyStr(FilterFieldValue)) {
  return
 } else {
  ApplyDatasetFilter(Dataset, FilterFieldName,FilterFieldValue, true);
 }
}

Да, совсем забыл.

После применения фильтра нужно обновить датасет раздела:

var WorkspaceDataset = WorkspaceWindow.ComponentsByName('dlAccounts').Dataset;
RefreshDataset(WorkspaceDataset);

Олег Лабьяк,
разработчик,
3-я линия Службы поддержки Terrasoft.

"Кошкаров Андрей" написал:
Я решил не использовать быстрый фильтр, а просто переписал функцию dlOfferingOnDatasetBeforeOpen, которая срабатывает на событие OnDatasetBeforeOpen датасета с продуктами

Мне кажется, что при таком решении может возникнуть путаница, так как накладывается дополнительное условие фильтрации, но его нигде не видно. Если использовать приведённую выше функцию FilterByFieldValue, закомментировав строчку

FBControl.FiltersBuilder.ClearFilter();

Вы сможете добавить к уже существующим в модуле фильтрации условиям своё.

Олег Лабьяк,
разработчик,
3-я линия Службы поддержки Terrasoft.

"Лабьяк Олег Игоревич" написал:Мне кажется, что при таком решении может возникнуть путаница, так как накладывается дополнительное условие фильтрации, но его нигде не видно.

Да, вы правы. Поробовал использовать вашу функцию, перед этим убрал из нее 2 строчки:

FBControl.FiltersBuilder.ClearFilter();
и
EnableDatasetFilters(Dataset, false);

При запуске возникла ошибка на строке:

FBItem.Value = DataFieldValue;

Текст ошибки:

Ошибка выполнения Microsoft JScript: Недопустимое число аргументов или присвоение значения свойства

при этом
DataFieldValue = "{3F4FB1DA-2B46-4321-9F44-45EF855124CD}"

Подскажите, в чем может быть проблема?

Скажите, пожалуйста, с какими параметрами Вы вызываете функцию? Вы применяете фильтр для справочника, или для строки? Возможно, тип значения не подходит для фильтра.

Олег Лабьяк,
разработчик,
3-я линия Службы поддержки Terrasoft.

Я применяю фильтр для справочного поля VendorID из датасета ds_Offering.
Вызываю с теми же аргументами, что и вы написали:
FilterByFieldValue(Self, 'VendorID', '{3F4FB1DA-2B46-4321-9F44-45EF855124CD}');

из скрипта Workspace-окна.

Оказалось, для полей типа "Справочник" не всё так просто... Для строк работает нормально, а у фильтров для справочников несколько другие свойства.

Пока попробуйте вызывать так:

FilterByFieldValue(Self, 'VendorName', <Название>);

Если этого недостаточно, постараюсь завтра разобраться, как поступать с фильтрами для справочников.

Олег Лабьяк,
разработчик,
3-я линия Службы поддержки Terrasoft.

Для поля типа Справочник в приведенной ф-ии замените строчку

FBItem.Value = DataFieldValue;

на

FBItem.Value.Add(DataFieldValue);

Отмечу, что если Вы используете версию бинарников ниже 3.3.1.65, то фильтр создастся нормально и будет работать как задумано, но вместо отображаемого значения справочника в фильтре будет пустая строчка.

"Лабьяк Олег Игоревич" написал:Если этого недостаточно, постараюсь завтра разобраться, как поступать с фильтрами для справочников.

Спасибо, в любом случае полезная информация :)
Я пока оставлю фильтрацию обычным способом, по имени мне видиться не сильно корректно. Может быть ситуация, что занесут случайно 2 компании с одинаковыми полями. А по ID получается не видно названия торговой марки, как и написал Репко Артём (у меня версия бинарников 3.3.1.31).

Артём, спасибо за помощь. Возьму на заметку

Показать все комментарии

Здравствуйте обитатели форума! Столкнулся я с такой проблемой, во время работы с разделом задача, при попытке сделать быстрый фильтр по задачам, а именно по заголовку задачи, система начинает ругаться и выдает в лог такое сообщение : (E) Ошибка выполнения метода 'dlTasksOnDatasetBeforeOpen'. 'null' is null or not an object «Call Stack», а дебагер ругается на следующую строчку : var TeamFilter = Filter.ItemsByCode('tcTeam');, иногда фильтрация проходит удачно и без ошибок, но при снятии и установлении флагов фильтрации задач по периоду (показать за период : сегодня, текущая неделя...) или при установлении/снятии флага фильтрации по ответственному выходит такая же ошибка ... Подскажите, это только у меня такое возникает, или все же есть не доработка?

Нравится

11 комментариев

1. Какая версия сборки и на какой СУБД?
2. В разделе "Задачи" делались доработки или это базовая?

--
www.it-sfera.com.ua

Версия сборки 3.3.1, СУБД SQL 2005, раздел задач без доработок, базовый.

Если вы плюёте мне в спину, значит я впереди вас!

Только-что проверил на базовой TS XRM 3.3.1.45
не наблюдается описанная Вами проблема!

--
www.it-sfera.com.ua

Простите сборка 3.3.1.31

Если вы плюёте мне в спину, значит я впереди вас!

У меня есть только последняя 45... Возможно, в 31 и была какая-то проблема, но если конфигурация базовая - это Вам лучше на support писать! :smile:

--
www.it-sfera.com.ua

Спасибо! Обязательно так и сделаю. :smile:

Если вы плюёте мне в спину, значит я впереди вас!

если ошибка возникает хаотично то возможно нехватает этого кода

var Filter = BaseFilters.ItemsByCode('ContactIDs');
if (!Assigned(Filter)) {
Filter = BaseFilters.Items(0).ItemsByCode('ContactIDs');
}

var Parameters = Dataset.SelectQuery.Parameters;
var TeamFilter = Filter.ItemsByCode('tcTeam');

там присутствует код приведенный ниже, а переменная "Filter" уже существует, где мне сделать модификацию кода? Спасибо!
var IDs = GetContactsFilterArray();
SetAttribute(Self, 'ContactsFilterArray', IDs);
var Filter = Dataset.SelectQuery.Items(0).Filters.Items(0)
.ItemsByCode('ContactIDs');
var Parameters = Dataset.SelectQuery.Parameters;
var TeamFilter = Filter.ItemsByCode('tcTeam');
var SubSelectFilter = TeamFilter.TestExpression
.ExpressionSelectQuery.Items(0).Filters;
var OwnerIDsFilter = Filter.ItemsByCode('tcOwnerIDs');
var ContactIDsFilter = Filter.ItemsByCode('tcContactIDs');
var SubContactIDsFilter = SubSelectFilter.ItemsByCode('tcContactIDs');
ApplyIncludeFilter(OwnerIDsFilter, IDs, true, null, Parameters);
ApplyIncludeFilter(ContactIDsFilter, IDs, true, null, Parameters);
ApplyIncludeFilter(SubContactIDsFilter, IDs, true, null, Parameters);
TeamFilter.IsEnabled = true;
Filter.IsEnabled = true;

Если вы плюёте мне в спину, значит я впереди вас!

var IDs = GetContactsFilterArray();
SetAttribute(Self, 'ContactsFilterArray', IDs);
var Filter = Dataset.SelectQuery.Items(0).Filters.Items(0)
.ItemsByCode('ContactIDs');
if (!Assigned(Filter)) {
Filter = Dataset.SelectQuery.Items(0).Filters.Items(0).Items(0).ItemsByCode('ContactIDs');
}

var Parameters = Dataset.SelectQuery.Parameters;
var TeamFilter = Filter.ItemsByCode('tcTeam');
var SubSelectFilter = TeamFilter.TestExpression
.ExpressionSelectQuery.Items(0).Filters;
var OwnerIDsFilter = Filter.ItemsByCode('tcOwnerIDs');
var ContactIDsFilter = Filter.ItemsByCode('tcContactIDs');
var SubContactIDsFilter = SubSelectFilter.ItemsByCode('tcContactIDs');
ApplyIncludeFilter(OwnerIDsFilter, IDs, true, null, Parameters);
ApplyIncludeFilter(ContactIDsFilter, IDs, true, null, Parameters);
ApplyIncludeFilter(SubContactIDsFilter, IDs, true, null, Parameters);
TeamFilter.IsEnabled = true;
Filter.IsEnabled = true;

Спасибо огромное! Теперь все заработала как надо :)

Если вы плюёте мне в спину, значит я впереди вас!

Разработчики написали код... Вы его установили... А потом, кто-то подумал о значение null... Добавили заплатку... А Вам уж, звиняйте, нужно ручками дописывать :biggrin:

--
www.it-sfera.com.ua

Показать все комментарии

Для формирования первичной базы клиентов я создал новый раздел в конфигурации в "Продажах". Цель была следующая: есть большая свалка компания, которых нужно обзвонить (телефонные продажи). Вести их как Контрагентов нет смысла - слишком трудоемкий процесс создания карточки, а вероятность отказа высока. Поэтому было решено сделать раздел, в котором менеджеры смогут быстро занести минимум необходимой информации, при этом это будет единая база, можно будет контролировать процесс работы и видеть результаты в разрезе по менеджерам и датам.

Была создана новая таблица с полями:

  • Клиент
  • Статус
  • Телефон
  • Улица
  • Дом
  • Офис
  • Ответственный
  • Дата смены статуса

В поле Дата смены статуса заносилась текущая дата при первой смене статуса клиента. Таким образом по дням отслеживается работа менеджеров.

Так же были добавлены общие детали Группа и Описание. В Описание заносились результаты работы.

Для работы с этой таблицей был сделан редактируемый грид, что позволило менеджерам очень быстро заполнять эту базу. Фактически работа стала похожа на работу в Excel - менеджеры добавляли пустые строки и сразу в ячейках писали текст, либо выбирали из выпадающего списка.

Но при работе с фильтром дат возникли следующие проблемы:

  1. Очень не удобно в ручную исправлять дату, так как фокус все время скачет обратно в грид.
  2. Практически невозможно работать с календарём при выборе дат, так как если курсор стоял в гриде и сразу нажать кнопку календаря, то вываливается ошибка "Cannot change Visible on OnShow or OnHide" и предлагается отправить отчет в Террасофт.

Данная проблема относится видимо только к более старым версиям (у нас 3.1.1.6). По совету в первой записи моего блога, которая "чудесным" образом исчезла (видимо за критику) проверил ситуацию на последней версии, там этой проблемы нет - календарь корректно работает даже при редактируемом гриде. А вот скачет ли фокус - забыл проверить.

Проблему с календарем решить не удалось, а вот ситуацию со скачущим фокусом удалось сильно смягчить. Именно смягчить, но не исправить совсем. Для этого на событие OnEnter полей фильтра дат был добавлен обработчик, который в момента входа в это поле выключал свойство редактируемости грида. То есть когда менджер щелкает на поле для редактирования даты, то грид перестает быть редактируемым, и фокус перестает скакать. Можно спокойно править дату. Правда теперь на поле даты приходится щелкать два раза. После первого щелчка грид перестает быть редактируемым, но курсор не встает в поле даты.
Так же на событие OnClick самого грида был добавлен обработчик, который обратно делал свойство IsEditable равным True.

Пробовал добавлять обработчик, который менял бы свойство IsEditable на событиях OnEnter и OnExit самого грида, но тогда даже при простом перемещении по строкам срабатывают оба события и тоже самое сообщение - "Cannot change Visible on OnShow or OnHide" - начинает вываливаться в лог. Уже легче, но все равно не то.

Результат:

  • Менеджеры могут очень быстро вести базу. Есть возможность контроля по датам и менеджерам.
  • При исправлении даты в фильтре приходится щелкать на поле два раза.
  • Для того, чтобы открыть календарь для выбора даты, нужно сначала войти в поле редактирования даты. То есть в итоге нужно три клика: два раза на поле и один раз на кнопке календаря.

Планируемые доработки:

  • Возможность вешать задачи на клиентов в первичной базе.
  • Создание карточки контрагента на основе записи в первичной базе.

Нравится

Поделиться

5 комментариев

Проверил последнюю версию на скачки фокуса. При вводе даты вручную курсор перескакивает и в новой версии. Это происходит из-за того, что как только система видит нормальную дату, она тут же обновляет датасет грида и курсор прыгает назад в грид. Таким образом, если мы хотим изменить не только день, месяц или год, а минимум две цифры, то придется каждый раз щелкать на поле ввода даты.

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

Алексей, спасибо за замечания! Обязательно внесем корректировки и вышлем Вам исправления.

Алексей, добрый день. В базовой версии Terrasoft обе указанные проблемы воспроизвести не удалось.
Тестирование проводилось на версии 3.1.1.49.
Указанные Вами ошибки могут быть связаны с теми доработками, которые Вы самостоятельно вносите в конфигурацию. С Вами свяжутся специалисты службы технической поддержки для уточения деталей, необходимых для воспроизведения и последующего исправления возможных проблем.

Нет. Это точно не из-за доработок. В версии 3.1.1.49 ошибку "Cannot change Visible on OnShow or OnHide" еще не проверял. А вот фокус скачет даже в самой последней версии, которая на вашем сайте в Демо.

Можете сами попробовать, в любом разделе, любой грид делаете редактируемым. Открываете этот раздел, щелкаете на любой записи грида, а потом в фильтре дат, попробуйте любую дату сразу поменять, например, на "01.01.2000". После того как вы введете "01" обновится грид с записями и курсор выбросит из поля вода даты обратно в грид с данными. Чтобы ввести вторую цифру "01" придется снова щелкать в поле ввода даты, после этого снова выбросит в грид, потом придется еще раз щелкать и вводить "2000".

Алексей, добрый день.
Ваши замечания исправлены. Служба технической поддержки свяжется в Вами для предоставления ссылки на обновление.

Показать все комментарии

Мы часто пользуемся быстрым фильтром и привыкли к такой удобной функциональности. Но при работе в деревом такой функциональности нет.
Привожу пример, как, с моей точки зрения, возможна реализация функциональности быстрого фильтра в дереве. Это только один из возможных вариантов. Вы можете предложить большее.

Мельникова Екатерина на эту тему даже создала идею: community.terrasoft.ua/node/3032.
Главная идея - дерево может находится в двух режимах - дерево и список. При работе с деревом фильтрация невозможно. При работе со списком - мы можем осуществлять поиск нужной нам записи, и при переключении обратно в режим дерева мы позиционируемся на найденной записи.

Основа - режим переключения из дерева в список. В приведенном примере переключение между деревом и списком осуществляется по горячей клавише Ctrl-Q. Включение / отключение фильтра осуществляются по горячей клавише Ctrl-S.

Открываем окно с DataTreeGrid (в данном примере отображены все папки из директория "Program Files", Вы можете загрузить подобную структуру по кнопке "Загрузить папки из директория"):
Дерево для поиска

Допустим, в дереве Вам необходимо найти библиотеку картинок программы Skype. В поле ввода быстрого фильтра набираем текст "Skype" и нажатием Ctrl-S включаем бытрый фильтр, который будет включен при переходе в режим отображения список.
Ввод параметров быстрого фильтра

Переключение просмотра в режим "Список" нажатием Ctrl-Q, после чего можно выбрать необходимую запись:
Переключение в режим

После того, как Вы нашли искомую запись - переключаемся в режим просмотра "Дерево" (Ctrl-Q):
Переключение в режим дерево и позиционирование на найденной записи

Пример сервисов для работы быстрого фильтра в дереве Вы можете найти в присоединенном архиве TreeQuickFilter_Example.rar.

Основные моменты реализации:

Переключение режима "Дерево / Список" осуществляется подстановкой значения свойства "ParentDataFieldName" компонента DataTreeGrid поля "ParentID" если мы хотим видеть дерево или "ID", если нам нуже список (в данном случае возможно переключение без перезапроса данных в базе данных):

function SwitchTreeListViewType() {
        var ParentDataFieldName = grdData.ParentDataFieldName;
        if (ParentDataFieldName == 'ParentID') {
                grdData.ParentDataFieldName = 'ID';
                cbQuickFilter.IsEnabled = true;
        } else {
                grdData.ParentDataFieldName = 'ParentID';
                cbQuickFilter.IsEnabled = false;
        }
        UpdateQuickFilter();
}

Выбор директория для загрузки в таблицу структуры папок:

function LoadFoldersFromDirectory() {
        var FolderNameObject = System.CreateObject('TSObjectLibrary.Value');
        if ((System.SelectDirectory("Выберите папку", '', FolderNameObject))) {
                var FolderName = FolderNameObject.Value;
                var Dataset = dlData.Dataset;
                LoadDirectoryItemsFromFolder(FolderName, Dataset);
        }
}

Загрузка структуры директория осуществляется с помощью рекурсивной функции:

function LoadDirectoryItemsToDataset(Path, Dataset, ParentID) {
        try {
                var Folder = FileSearchGridArea.FSO.GetFolder(Path);
                var Folders = new Enumerator(Folder.SubFolders);
                for (Folders.moveFirst(); !Folders.atEnd(); Folders.moveNext()) {
                        var Item = Folders.item();
                        var Name = Item.Name;
                        var Path = Item.Path;
                        var ID = AddFolderToDataset(Dataset, ParentID, Name, Path);
                        LoadDirectoryItemsToDataset(Path, Dataset, ID);
                }
        } catch(e) {
                ShowErrorDialog(e.message);
        }
}

Обработка горячих клавиш осуществляется в событии OnKeyDown DataTreeGrid:

function DoKeyDown(Control, Key, Shift) {
        var CtrlDown = (Shift & 4) > 0;
        if (CtrlDown) {
                var QKeyCode = 81;
                if (Key.Value == QKeyCode) {
                        SwitchTreeListViewType();
                }
                var SKeyCode = 83;
                if (Key.Value == SKeyCode) {
                        cbQuickFilter.IsChecked = !cbQuickFilter.IsChecked;
                }
        }
}

Данный вариант работы с деревом имеет свои ограничения:

    * Он быстро работает, когда количество записей не больше нескольких тысяч.
    * Когда не используется режим подгрузки узлов дерева по требованию.

Конечно, реализовывать такое в конфигурации трудоемко, но можно. Лучшим решением было бы реализовать быстрый фильтр в ядре в компоненте DataTreeGrid.

Используйте на здоровье.

Нравится

Поделиться

5 комментариев

Автору - спасибо!
Разработчикам - быстрый фильтр в ядре для компоненты DataTreeGrid!

--
γνῶθι σεαυτόν

А в чем сложности при организации подгрузки узлов дерева по требованию? И с большими объемами бы тогда работалось нормально.

Есть несколько способов построения дерева. В данном примере я использую загрузку полного дерева на сторону клиента. Если в отфильтрованной выборке быстрого фильтра Вы нашли запись, то при следующем построении дерева мы спозиционируемся на нее в дереве и увидим, где именно в дереве находится наша запись.

При большом количестве записей данный вариант не подходит. В таком случае применяют режим загрузки по требованию. В таком режиме при раскрытии узла дерева, если узел еще не был подгружен, идет запрос в базу данных для формирования выборки всех записей, которые являются подчиненными раскрывающимся узлу и дерево формируется по мере раскрытия. Для такого варианта поиск необходимой записи останется таким же как в приведенном примере, а для отображения записи в дереве необходимо применить другой механизм - либо в выборке выбирать все записи по иерархии от найденной до самого верхнего уровня, либо по иерархии принудительно открывать все узлы от родительского до найденного узла.

В данном примере такая задача не решается. Это я и поразумевал фразой "Вы можете предложить большее".

Спасибо. Пригодилось!

Мне тоже пригодилось. Спасибо :smile:

Показать все комментарии

Добрый день! Подскажите пожалуйста как устроен быстрый фильтр. Т.к. пытаюсь создать что-то подобное в разделе "контрагенты" - в гриде, рядом с кнопками добавить..удалить создал 2 контрола:Edit(edtCode) и Button(btnSearch) - куда вводится код и после нажатия на кнопку должен отфильтроваться список контрагентов, вот скипт:

function btnSearchOnClick(Control) {
 var Dataset = BaseGridArea.GridDataset;
 Dataset.DisableEvents();
 var Code = edtCode.Value;
 ApplyDatasetFilter(Dataset, 'Code', Code, true);
 Dataset.Open();
 Dataset.EnableEvents();
 RefreshDataset(Dataset);
}

Но ничего не происходит. В чём может быть проблема? заранее спасибо!

Нравится

12 комментариев

скорее всего на BeforeOpen Вашего датасета отрабатывает функция
ApplyStandartWorkspaceFilter (название неточно)
нужно Ваш фильр накладывать после етой фн.

Судя по тому, где быстрый фильтр рисуется, он реализован в ядре.
DisableEvents() - EnableEvents() Лучше делать в try finally.
ApplyDatasetFilter, на сколько я понимаю, Dataset не трогает, тогда незачем и события отключать.
Dataset.Open() вроде бы ничего не делает, если датасет предварительно не закрыть.
Вобщем я бы это сделал так.

BaseGridArea.UseMyQuickFilter = false;
BaseGridArea.MyQuickFilterValue = '';
 
function btnSearchOnClick(Control) {
	var Dataset = BaseGridArea.GridDataset;
	var Code = edtCode.Value;
	BaseGridArea.UseMyQuickFilter = (Code != '');
	BaseGridArea.MyQuickFilterValue = Code;
	RefreshDataset(Dataset);
}
 
function dlDataOnDatasetBeforeOpen(Dataset, DoOpen) {
	edtCode.Value = BaseGridArea.MyQuickFilterValue; //Это нужно, чтобы не сбивать пользователей с толку, если случайно сотрут edtCode.Value, а btnSearch не нажмут
	ApplyDatasetFilter(Dataset, 'Code', BaseGridArea.MyQuickFilterValue,
		BaseGridArea.UseMyQuickFilter);
}

Upd: Действительно, это может не сработать, если ApplyStandardWorkspaceFilter отработает позже. Попробуйте, если не получится есть вариант UseMyQuickFilter и MyQuickFilterValue передавать через аттрибуты датасета и

ApplyDatasetFilter(Dataset, 'Code', Dataset.Attributes('MyQuickFilterValue'),
		Dataset.Attributes('UseMyQuickFilter'));

вызывать в скрипте датасета или раздела

Спасибо! А что это за методы такие: 'MyQuickFilterValue', 'UseMyQuickFilter' - я их не в датасете ни в BaseGridArea не нашёл...

Это не методы, это поля в BaseGridArea и аттрибуты в Dataset.
BaseGridArea это JS - объект и в нем вы можете создавать какие угодно поля (т.е. напишите в одном месте BaseGridArea.MyValue = 48 и в любом другом месте этого скрипта BaseGridArea.MyValue вернет 48).
Dataset.Attributes это тоже объект и в нем вы так же можете хранить свои значения. Только это объект, реализованый в ядре tscrm и в нем нельзя писать Attributes.MyValue = 48, нужно писать Attributes('MyValue') = 48.

Ни так ни так не работает :((((((((
Разве что получается если закоментировать

function dlAccountsOnDatasetBeforeOpen(Dataset) {
	var IsInSingleRowMode = GetIsInSingleRowMode();
	if (IsInSingleRowMode != true) {	
//		ApplyStandardWorkspaceFilter();		
	}
}

но она находится не в скрипте грида, а в scr_AccountsWorkspace

замените фн ApplyStandardWorkspaceFilter на свою
например такую

function ApplyMyWorkspaceFilter() {
  ApplyStandardWorkspaceFilter();
  ApplyDatasetFilter(Dataset, 'Code', BaseGridArea.MyQuickFilterValue,
                 BaseGridArea.UseMyQuickFilter);
}
 
 
function dlAccountsOnDatasetBeforeOpen(Dataset) {
        var IsInSingleRowMode = GetIsInSingleRowMode();
        if (IsInSingleRowMode != true) {       
              ApplyMyWorkspaceFilter();        
        }
}

Нуда, это то о чем я писал. Но так делать низзя. Делать нужно так

BaseGridArea.UseMyQuickFilter = false;
BaseGridArea.MyQuickFilterValue = '';
 
function btnSearchOnClick(Control) {
        var Dataset = BaseGridArea.GridDataset;
        var Code = edtCode.Value;
        BaseGridArea.UseMyQuickFilter = (Code != '');
        BaseGridArea.MyQuickFilterValue = Code;
        RefreshDataset(Dataset);
}
 
function dlDataOnDatasetBeforeOpen(Dataset, DoOpen) {
        edtCode.Value = BaseGridArea.MyQuickFilterValue; //Это нужно, чтобы не сбивать пользователей с толку, если случайно сотрут edtCode.Value, а btnSearch не нажмут
        ApplyDatasetFilter(Dataset, 'Code', BaseGridArea.MyQuickFilterValue,
                BaseGridArea.UseMyQuickFilter);
//Вот эта строка не даст отключится фильтру. Извиняюсь, что некрасиво, просто нет времени. Но работать будет
        Dataset.SelectQuery.Items(0).Filters.Items(0).ItemsByCode('Code').CanDisable = !BaseGridArea.UseMyQuickFilter;
}

"Березович Сергей" написал:замените фн ApplyStandardWorkspaceFilter на свою
например такую

Так делать не желательно. Иначе в деталях, где используется этот же грид будут нефункциональные кнопка и эдит. Их конечно можно прятать. Но по-моему мой вариант лучше. Выносить логику контролов, реализованых в одном окне, в другое как-то не солидно.

Спасибо! Вот только странно себя ведёт последняя строка , а именно:

Dataset.SelectQuery.Items(0).Filters.Items(0).ItemsByCode('Code').CanDisable 

при первом входе в раздел ругается, подходит только:

Dataset.SelectQuery.Items(0).Filters.ItemsByCode('Code').CanDisable 

, но если нажать на стандартную кнопку грида "обновить", то ругается на последнюю строку, тут ей первую подавай...

"_" написал:Так делать не желательно. Иначе в деталях, где используется этот же грид будут нефункциональные кнопка и эдит. Их конечно можно прятать. Но по-моему мой вариант лучше. Выносить логику контролов, реализованых в одном окне, в другое как-то не солидно.

почему нежелательно?
заменить фн ApplyStandardWorkspaceFilter чтобы накладывать фильтр в разделе
+ проверять если есть атрибут ParentID значить деталька
и значить накладывать фильтры в гриде детальки

"Березович Сергей" написал:почему нежелательно?

Потому что мухи должны быть отдельно, а котлеты отдельно. По возможности :) Ну и повторять одну и ту же логику в двух местах - китайский копипейст получается.

"Serega" написал:но если нажать на стандартную кнопку грида "обновить", то ругается на последнюю строку, тут ей первую подавай...

Что-то они творят с фильтрами в главном датасете раздела. Я попробовал запоминать этот фильтр в OnPrepare грида, так в OnPrepare мы имеем фильтр с одним InstanceID, а когда в ApplyDatasetFilter включаем IsEnabled, так у фильтра другой InstanceID. Получается они убивают Кенни, т.е старый фильтр и создают новый причем на уровень ниже. Будем думать.

Придумал.В scr_DB добвляем фунцкцию (делаем по подобию EnableFilter, но не ищем в подзапросах - лень, но в данном случае покатит).

function GetFilter(FiltersNode, FilterCode){
 
	if (FiltersNode.Code == FilterCode) {
		return FiltersNode;
	}
	var Result = null;
	if (FiltersNode.FilterType == ftFilters) {
		for (var i = 0; (i < FiltersNode.Count) && (!Assigned(Result)); i++) {
			Result = GetFilter(FiltersNode.Items(i), FilterCode);
		}
	}
	return Result;
}

В scr_AccountsGridArea пишем

function wnd_AccountsGridAreaOnPrepare(Window) {
	//...
	//Оставляем все как есть, просто дописываем в конец
	var Dataset = dlData.Dataset;
	Dataset.Attributes('UseMyQuickFilter') = false;
	Dataset.Attributes('MyQuickFilterValue') = '';
}
 
function dlDataOnDatasetBeforeOpen(Dataset, DoOpen) {
	edtCode.Value =  Dataset.Attributes('MyQuickFilterValue') ; //Это нужно, 
	// чтобы не сбивать пользователей с толку, если случайно сотрут 
	//edtCode.Value, а btnSearch не нажмут
	ApplyDatasetFilter(Dataset, 'MyQuickFilter', 
	Dataset.Attributes('MyQuickFilterValue'),
		Dataset.Attributes('UseMyQuickFilter'));
	var Filter = GetFilter(Dataset.SelectQuery.Items(0).Filters, 
		'MyQuickFilter');	
	Filter.CanDisable = !Dataset.Attributes('UseMyQuickFilter');	
}
 
function btnSearchOnClick(Control) {
	var Dataset = BaseGridArea.GridDataset;
	var Code = edtCode.Value;
	Dataset.Attributes('UseMyQuickFilter') = (Code != '');
	Dataset.Attributes('MyQuickFilterValue') = Code;
	RefreshDataset(Dataset);
}

В scr_AccountsWorkspace можно писать, а можно и не писать. Ядровый быстрый фильтр не сбрасывается при переходе в раздел из грида. Как по мне - лажа. Если мы хотим чтобы наш сбрасывался, то пишем так.

function dlAccountsOnDatasetBeforeOpen(Dataset) {
	var IsInSingleRowMode = GetIsInSingleRowMode();
	if (IsInSingleRowMode != true) {
		ApplyStandardWorkspaceFilter();
	} else {
		var Filter = GetFilter(Dataset.SelectQuery.Items(0).Filters, 
			'MyQuickFilter');
		Filter.CanDisable = true; 
		Filter.IsEnabled = false; 
	}
	//(IsInSingleRowMode == true) это отдельная песня. По хорошему за такое убивать надо, но GetIsInSingleRowMode может вернуть.... строку!!! :) 
	//...
	//дальше оставляем все как есть
}

Не получилось таки мух толком отделить (если делать изменения в scr_AccountsWorkspace) :) Фильтр и параметр я назвал 'MyQuickFilter'. Фильтр сдетал типа Like, в комбобоксе выбрал 'Содержит'. btnSearch нужно, наверное, сделать IsDefault = true, для удобства.

Вот, вроде сделали. Остался только маленький вопрос - зачем :)

Показать все комментарии