Как упростить работу с TTreeView?
Подскажите пожалуйста, как упростить работу с TTreeView. Ситуация следующая...
В указанном компоненте - разделы программы. Рядом DBGrid.
Кликаем по узлу - выполняется соответствующий запрос. Грид обновляется. Теперь: одновременно с этим выполняется целый ряд процедур. Некоторые из них что-либо добавляют к запросу перед его выполнением в зависимости от состояния программы, или же совершают иные манипуляции.
Что приходится делать: в каждой процедуре оператор case, который прогоняет проверку с целью определения выделенного пользователем узла. Упрощенно:
//Узел 1, 2 ... n
end;
А теперь добавляем к дереву новый узел. И приходится каждую такую процедуру модифицировать. Очень неудобно.
Как сделать это грамотно (как можно меньше телодвижений при достижении того же результата:)
Необходимо, говоря языком формальной математики сделать отображение, которое и будет преобразовать номер выделенного узла в необходимую информацию.
Нужно пользоваться свойством TTreeView.Selected и TTreeNode.Data. В последнюю загоняешь указатель на произвольные данные, нужные для реализации логики программы - для этого и предусматривалось свойство.
Необходимо, говоря языком формальной математики сделать отображение, которое и будет преобразовать номер выделенного узла в необходимую информацию.
Можно поподробнее? Хотя бы с простеньким примером.
У меня сейчас так:
Прогоняю массив со структурой-описателем т.н. подсистемы,
чтобы понять, что выделил пользователь (поле sName)
*)
for i := 0 to Length(ocUser.arrtSubSystemNodes) - 1 do begin
if(ocUser.arrtSubSystemNodes.sTitle = TreeView_SectionTreeMain.Selected.Text) then begin
//Процедура откроет редактор для данной подсистемы
OpenViewerForSubSystem(ocUser.arrtSubSystemNodes.sName, enNewRec);
break;
end;
end;
//Далее...
procedure OpenViewerForSubSystem(sSubSystemName :String; nFormMode :TFormMode);
var
...
begin
if(sSubSystemName = {Идентификатор подсистемы})then begin
case nFormMode of //Смотрим, в каком режиме следует открыть редактор
enNewRec:
begin
//Новая запись: создаем экземпляр соответствующей
формы (редактор для каждой подсистемы свой) в
нужном режиме. Иногда для каждого из режимов - своя форма
end;
//и т.д.
end;
Вот, что я имел ввиду. Как видите все не очень эффективно:(
Подсистем (в данном случае узлов) много. В зависимости от того, где мы находимся определенное действие должно отработать определенным образом (например как в примере вызов редактора). Таких процедур предостаточно и в каждой приходится организовывать проверку. В некоторых узлах для каждого из режимов (например, "новая запись", редактирование и т.д.) своя специфическая форма - редактор.
Как все это оптимизировать до более-менее приемлимого уровня? Программа растет, и малейшее изменение (как уже упоминалось, добавление нового узла) довольно болезненно. Хотелось бы "вылечить" код, пока это не стало слишком трудоемко.
присвоить каждой подсистеме свой номер, который будет соответствовать свойству TTreeNode.Index и тогда вот этот перебор не нужен
if(ocUser.arrtSubSystemNodes.sTitle = TreeView_SectionTreeMain.Selected.Text) then begin
//Процедура откроет редактор для данной подсистемы
OpenViewerForSubSystem(ocUser.arrtSubSystemNodes.sName, enNewRec);
break;
end;
end;
а просто
Тогда функция такая:
procedure OpenViewerForSubSystem(sSubSystemIndex :Integer; nFormMode :TFormMode);
var
...
begin
case sSubSystemName of {Идентификатор подсистемы}
1: EditorOpen(nFormMode);
2: ...
....
N:...
//и т.д.
end;
И разбиваем все на мелкие функции:
begin
case nFormMode of //Смотрим, в каком режиме следует открыть редактор
enNewRec: NewRecord; // вызываем функцию новой записи
enDelRec: DelRecord; // к примеру
end;
end;
присвоить каждой подсистеме свой номер, который будет соответствовать свойству TTreeNode.Index и тогда вот этот перебор не нужен
if(ocUser.arrtSubSystemNodes.sTitle = TreeView_SectionTreeMain.Selected.Text) then begin
//Процедура откроет редактор для данной подсистемы
OpenViewerForSubSystem(ocUser.arrtSubSystemNodes.sName, enNewRec);
break;
end;
end;
а просто
Конечно, так упрощается процедура определения узла.
Какое-то время делал так. С чем столкнулся.
Индекс может иногда меняться. Пример:
Подсистема А является дочерней для B. Ее индекс там с учетом присутствующих иных узлов, скажем, третий.
Изменилось что-то, указанная подсистема А переместилась, стала дочерней для С, где уже десяток узлов. Индекс стал другим.
К примеру, применительно к моей программе, какой-то определенный отчет относился к разделу "Бухгалтерские", затем чуть поменялся порядок расчета остатков - и вот он по логике уже Менеджерский, и предполагается что использовать его станут другие люди, имеющие доступ к другой ветке...
На первых порах узлы постоянно меняли родителей:) - особенности программирования без четко описанного тех.задания.
Таким образом, идентификатор, который к тому же выступает индексом узла, использовать (в моей программе, по крайней мере) оказалось не рационально...
Здорово, спасибо!
Вот, выбрал время, порылся в инете. Нашел страничку с примером:
http://www.az-design.ru/Support/SoftWare/Delphi/D3/SB19Data.shtml
Еще не успел попробовать.
Таким образом, можно каждому узлу в данном свойстве прописать его id (цифровой, или строковый). И можно будет убрать процедуру перебора элементов.
Но это не избавит о необходимости перебора в процедуре procedure OpenViewerForSubSystem(sSubSystemIndex :Integer; nFormMode :TFormMode);
Для каждого раздела - разные редакторы.
Первое, что приходит на ум, хранить в бд имя класса формы, которую нужно открывать.
Что -то типа:
--1----TfrmJournalsEventsViewer--
Тогда можно будет вообще обходится без case. Разве что перебирать различные режимы (пожалуй, действительно удобно разбить эту большую функцию на более мелкие):
Тогда функция такая:
procedure OpenViewerForSubSystem(sSubSystemIndex :Integer; nFormMode :TFormMode);
var
...
begin
case sSubSystemName of {Идентификатор подсистемы}
1: EditorOpen(nFormMode);
2: ...
....
N:...
//и т.д.
end;
И разбиваем все на мелкие функции:
begin
case nFormMode of //Смотрим, в каком режиме следует открыть редактор
enNewRec: NewRecord; // вызываем функцию новой записи
enDelRec: DelRecord; // к примеру
end;
end;
Может еще как-то проще можно?
Инкапсулировать нужную функциональность в классы и полностью отказаться от внешних case. В ООП-программах множественное ветвление допустимо только при обработке данных, поступающих извне, в частности, оконных сообщений. Если классам требуется установить начальные значения некоторых свойств, описать тип-запись, в которой указать все параметры, а в Data пихать указатель на неё.
В общем случае императивные методики подразумевают использование указателей (явных или неявных) в тех местах, где в реляционных моделях используется ID.
В общем случае императивные методики подразумевают использование указателей (явных или неявных) в тех местах, где в реляционных моделях используется ID.
Ясно. Есть над чем поработать.
Спасибо!