| Дерево картежей | - Принципы использования |
Принципы использования
Введение
[img=Деревья align=right]http://hiasm.com/xf/attach/wiki/treeview1.png[/img] К моменту написания статьи стандартный пакет содержал два визуальных компонента, позволяющих работать с деревом элементов и отличающихся принципиально разным подходом: TreeView - идентификация узлов дерева по их индексам, TreeViewTrain - инентификация узлов дерева по их уникальным ID. С точки зрения пользователя конечного приложения оба элемента ничем (почти) не отличаются друг от друга, однако с точки зрения подхода к проектированию схемы разница оказывается очень существенной. В чем она заключается и какой их элементов лучше использовать в своей схеме и будет рассказано ниже.
Выбор элемента
Элемент TreeView представляет из себя классический подход к решению задачи построения и управления древовидными (иерархическими) структурами данных. Классика в данном случае заключается в использовании методов и свойств, имеющих свои аналоги в том или ином виде в похожих элементах - ListBox, Mem, ComboBox и другие. Т.е. знакомство с одним из этих элементов позволит вам с легкостью использовать и другие из той же линейки для решения своих задач. Однако эта простота остается таковой только до тех пор, пока данных не так много и пока не требуется достаточно часто менять их, удалять и добавлять. Наибольшей проблемой на этом пути становится тот факт, что стандартный элемент TreeView использует в качестве идентификаторов узлов их абсолютные индексы, а это значит что невозможно в любой момент времени обратиться к одному конкретному узлу не найдя предварительно его абсолютный индекс в дереве по тем или иным критериям (напомню о том, что индекс может меняться, если в дерево был вставлен или удален узел).
Как раз эту основную проблему и решает новый подход к управлению данными, использованный в элементе TreeViewTrain. Т.е. в нем каждый узел обязан иметь свой уникальный ID, а так же ID своего родителя, за счет чего обеспечивается целостность и постоянство иерархии данных вне зависимости от того, какие узлы мы добавляем, удаляем или изменяем. Однако у данного подхода есть и обратная сторона. Во-первых, от разработчика требуется правильно сформировать данные для добавления в дерево и следить в последствии за уникальностью идентификаторов. Во-вторых, более сложная внутренняя структура данных потребует гораздо большей подготовки и понимания происходящего, чем классическое решение. Т.е. метод тыка в изучении данного элемента скорей всего ни к чему хорошему не приведет, а внесет еще больше путаницы и непонимания.
Как раз эту основную проблему и решает новый подход к управлению данными, использованный в элементе TreeViewTrain. Т.е. в нем каждый узел обязан иметь свой уникальный ID, а так же ID своего родителя, за счет чего обеспечивается целостность и постоянство иерархии данных вне зависимости от того, какие узлы мы добавляем, удаляем или изменяем. Однако у данного подхода есть и обратная сторона. Во-первых, от разработчика требуется правильно сформировать данные для добавления в дерево и следить в последствии за уникальностью идентификаторов. Во-вторых, более сложная внутренняя структура данных потребует гораздо большей подготовки и понимания происходящего, чем классическое решение. Т.е. метод тыка в изучении данного элемента скорей всего ни к чему хорошему не приведет, а внесет еще больше путаницы и непонимания.
Картежи
Картежи это основная единица данных, с которой работает дерево элементов. Картеж так же можно назвать МТ потоком с заранее предопределенным назначением звеньев(или их части). Если вы не знаете, что такое МТ поток, то необходимо для начала ознакомиться со статьей Оболочка->Технология MultiThread, иначе понять написанное ниже будет крайне сложно. Тут же следует коротко еще раз упомянуть о струкруре МТ - это цепочка из № подряд идущих звеньев, в которой каждое звено представляет из себя элементарные(примитивные) данные такие как числа, строки, картинки, stream и прочие. Соответственно каждому звену МТ потока можно поставить в соответствие свой индекс от 0 до №-1. Именно по этим индексам, к примеру, можно получить любое звено потока в элементе MT_Array. Тогда картеж это всего лишь заранее определенные индексы звеньев входного МТ потока, которые отвечают за ту или иную часть ветки дерева. Всего таких звена может быть 4:
Предположим, что мы определили индексы следующим образом:
Тогда для добавления первого элемента в дерево необходимо подать на вход примерно такой МТ поток:
Т.е. первым звеном(с индексом 0) в потоке должен стоять уникальный ID нового узла(следует заметить тот факт, что в качестве ID не обязательно использовать число - им может быть и любая уникальная строка), вторым звеном - его заголовок, третьим - ID родителя (поскольку узел добавляется в корень дерева, то ID = -1), и наконец четвертым - индекс иконки нового узла. Теперь, чтобы к этому узлу добавить еще один дочерний необходимо не забыть указать верный ID родителя, иначе узел будет добавлен как корневой или не будет добавлен вообще. Вот пример картежа для добавления дочернего узла к только что созданному:
Как видите у этого картежа изменился ID, поскольку в дерево нельзя добавлять два элемента с одним и тем же идентификатором (не забывайте об этом!), а так же изменился ID родителя с -1 на 1. Очевидно, что 1 в данном случае это ID элемента, который был добавлен в дерево первым.
Проверить работоспособность описанного метода можно в примере ниже:
[block]Стоит отметить тот факт, что дерево позволяет добавлять элементы с одинаковыми ID, но делать так не рекомендуется по причинам изложенным выше[/block]
Однако на этом достоинства дерева картежей не заканчиваются и существует еще одна особенность его использовании, призванная упростить ряд задач, решаемых с помощью дерева. Речь идет о тех задачах, в которых требуется каждому узлу дерева сопоставить некоторые дополнительные данные, не связанные как таковые с самим деревом. К примеру, вы составляете каталог машин, в котором корневыми элементами у вас являются их марки (Lada, Audi, BMW и т.д.), а дочерними непосредственные наименования (Lada Kalina, Lada Priora, Audi A3, Audi Q5, BMW X5, BMW Z4 и т.д.). И вы хотите каждому узлу дерева, являющемуся конкретной маркой автомобиля назначить такие параметры как: объем двигателя, количество лошадиных сил, расход топлива, тип исполнения кузова, цвет кузова и т.д. Выполнить эту задачу для классического дерева без использования обвязки из дополнительных элементов невозможно, однако в случае с TreeViewTrain она решается просто - достаточно дописать нужные параметры в конец картежа и они будут сохранены в дерево вместе со всеми остальными звеньями.
И так, попробуем задать такой формат картежей, который позволит наиболее оптимально решить поставленную задачу. Очевидно, что у нас будет два различных картежа - для марки автомобиля и для ее конкретной модели. Марка авто:
Модель авто:
Т.е. в случае модели авто картеж будет состоять из 9 звеньев, 4 из которых дерево картежей знает, а назначение 5 оставшихся ему не известно. Но несмотря на это все звенья будут сохранены в дереве и могут быть в дальнейшем использованы при работе с соответствующим его элементом. Для проверки этого обстоятельства можно использовать схему, приведенную ниже, в которой при выборе модели авто в правой части программы отображаются ее параметры, раненее сохраненные в дереве.
Последнее о чем стоит напомнить это о возможности перемешивания звеньев картежа, которые дереву неизвестны и звеньев, индексы которых указаны в его свойствах. Т.е. в случае с моделью автомобиля можно было использовать, например, и такой формат:
Однако пользоваться этой особенностью рекомендуется только в том случае, если картеж формируется не разработчиком схемы, а приходит в готовом виде откуда-то из вне (именно так происходит в схеме Маил агента MRA.sha). Во всех остальных случаях рекомендуется все предопределенные звенья размещать в начале картежа друг за другом.
CaptionIndex - Индекс заголовка элемента
ParentIDIndex - Индекс элемента, определяющего идентификатор родителя
IDIndex - Индекс идентификатора элемента
IconIndex - Индекс иконки элемента
CaptionIndex = 1
ParentIDIndex = 2
IDIndex = 0
IconIndex = 3
№0 | №1 | №2 | №3 |
1 | Первый элемент | -1 | 0 |
Т.е. первым звеном(с индексом 0) в потоке должен стоять уникальный ID нового узла(следует заметить тот факт, что в качестве ID не обязательно использовать число - им может быть и любая уникальная строка), вторым звеном - его заголовок, третьим - ID родителя (поскольку узел добавляется в корень дерева, то ID = -1), и наконец четвертым - индекс иконки нового узла. Теперь, чтобы к этому узлу добавить еще один дочерний необходимо не забыть указать верный ID родителя, иначе узел будет добавлен как корневой или не будет добавлен вообще. Вот пример картежа для добавления дочернего узла к только что созданному:
№0 | №1 | №2 | №3 |
2 | Дочерний элемент | 1 | 0 |
Как видите у этого картежа изменился ID, поскольку в дерево нельзя добавлять два элемента с одним и тем же идентификатором (не забывайте об этом!), а так же изменился ID родителя с -1 на 1. Очевидно, что 1 в данном случае это ID элемента, который был добавлен в дерево первым.
Проверить работоспособность описанного метода можно в примере ниже:
Add(MainForm,2953706,21,105)
{
Width=354
Height=203
}
Add(Button,4190499,98,105)
{
Left=30
Top=35
Width=65
Caption="First node"
Data=String(1|Первый элемент|-1|0)
link(onClick,8717425:doWork1,[(158,111)])
}
Add(TreeViewTrain,1574694,245,35)
{
Left=145
Top=20
Width=175
Height=130
Name="main"
IconsManager="icons"
CaptionIndex=1
ParentIDIndex=2
IconIndex=3
}
Add(Button,6923145,98,154)
{
Left=30
Top=65
Width=65
Caption="Child node"
Data=String(2|Дочерний элемент|1|0)
link(onClick,8717425:doWork3,[(158,160)])
}
Add(MT_String,14150441,189,105)
{
Delimeter="|"
link(onResult,3105964:doAddNode,[])
}
Add(HubEx,8717425,154,105)
{
link(onEvent,14150441:doMT,[])
}
Add(TVT_AddNode,3105964,245,105)
{
TreeView="main"
}
Add(IconsManager,3602443,301,35)
{
Name="icons"
Icons=['Icon'=[ZIP7E03000078DACD51BB12C140143D26855665CC9841A9E30FF8134A7FE1F125FC83469A68A3A06086C6234321C64C34695D7777131B89A07493BB73E7EC3977EF03C8F097CB81CF023A0690075065670815285C58D3C0DF9865595F18370A820B69D0A5F86D4CB223CC48136C89BC25B36D09166118E1733C9592A4F1D3B62697CF7EEDE405127ED14D481C82A9C9ADF5BE3F5FB6367B8130EED04BD9FC7331E3805F3F7983C58A47C4A740C66117D14622F94B323F93DB2AFFE435FF738C335D7FC9F5EBC78FF5C7E6A36C24911DA5AE6C2BA764CA5E4C99396DFED1FD3A927620BDEBB49525E53F5A0A9FBAC0BDA1FC5A645A56F9C150DE838EC33BC1133AE10F414BBEF1]]
}
Однако на этом достоинства дерева картежей не заканчиваются и существует еще одна особенность его использовании, призванная упростить ряд задач, решаемых с помощью дерева. Речь идет о тех задачах, в которых требуется каждому узлу дерева сопоставить некоторые дополнительные данные, не связанные как таковые с самим деревом. К примеру, вы составляете каталог машин, в котором корневыми элементами у вас являются их марки (Lada, Audi, BMW и т.д.), а дочерними непосредственные наименования (Lada Kalina, Lada Priora, Audi A3, Audi Q5, BMW X5, BMW Z4 и т.д.). И вы хотите каждому узлу дерева, являющемуся конкретной маркой автомобиля назначить такие параметры как: объем двигателя, количество лошадиных сил, расход топлива, тип исполнения кузова, цвет кузова и т.д. Выполнить эту задачу для классического дерева без использования обвязки из дополнительных элементов невозможно, однако в случае с TreeViewTrain она решается просто - достаточно дописать нужные параметры в конец картежа и они будут сохранены в дерево вместе со всеми остальными звеньями.
И так, попробуем задать такой формат картежей, который позволит наиболее оптимально решить поставленную задачу. Очевидно, что у нас будет два различных картежа - для марки автомобиля и для ее конкретной модели. Марка авто:
IDIndex | CaptionIndex | ParentIDIndex | IconIndex |
ID | Наименование марки | root ID | Логотип |
Модель авто:
IDIndex | CaptionIndex | ParentIDIndex | IconIndex | -- | -- | -- | -- | -- |
ID | Наименование модели | ID марки | Иконка | объем двигателя | количество лошадиных сил | расход топлива | тип кузова | цвет кузова |
Т.е. в случае модели авто картеж будет состоять из 9 звеньев, 4 из которых дерево картежей знает, а назначение 5 оставшихся ему не известно. Но несмотря на это все звенья будут сохранены в дереве и могут быть в дальнейшем использованы при работе с соответствующим его элементом. Для проверки этого обстоятельства можно использовать схему, приведенную ниже, в которой при выборе модели авто в правой части программы отображаются ее параметры, раненее сохраненные в дереве.
Add(MainForm,9944926,42,140)
{
Width=471
Height=306
Caption="TreeViewTrain"
link(onCreate,9363307:doEnum,[])
}
Add(TreeViewTrain,1574694,322,196)
{
Width=175
Height=282
Align=1
Name="main"
IconsManager="icons"
DragDrop=0
CaptionIndex=1
ParentIDIndex=2
IconIndex=3
Point(DropAccept)
Point(doExpand)
link(onClick,6992197:doSeparateMT,[])
link(DropAccept,11757345:GetData,[])
}
Add(MT_String,14150441,294,315)
{
link(onResult,3105964:doAddNode,[])
}
Add(TVT_AddNode,3105964,350,315)
{
TreeView="main"
}
Add(IconsManager,3602443,231,140)
{
Name="icons"
Icons=[]
}
Add(ArrayRW,10401924,231,189)
{
link(Array,3602443:IconArray,[])
}
Add(Icon,12754312,182,154)
{
}
Add(DoData,5987187,182,203)
{
link(onEventData,10401924:doAdd,[])
link(Data,12754312:Icon,[])
}
Add(StrList,5214053,168,259)
{
Strings=#11:1;Lada;-1;0|53:10;Kalina;1;1;1.6л;80л.с;9.5л;универсал;сер. металлик|49:11;Priora;1;1;1.6л;80л.с;9.8л;седан;сер. металлик|11:2;Audi;-1;0|46:30;Audi A3;2;1;1.6л;100л.с;9.6л;компакт;черный|59:31;Audi Q5;2;1;2.0л;155л.с;10.4л;внедорожник;мокрый асфальт|10:3;BMW;-1;0|51:40;BMW X5;3;1;3.0л;350л.с;17.5л;внедорожник;серебро|43:41;BMW Z4;3;1;2.5л;192л.с;12.1л;купе;черный|
}
Add(ArrayEnum,9589809,182,322)
{
link(onItem,14150441:doMT,[])
link(onEndEnum,1574694:doExpand,[(278,335)(278,209)])
link(Array,5214053:Array,[])
}
Add(StrList,8602002,84,84)
{
Strings=#19:int\icons\hiasm.ico|20:int\icons\spheme.ico|
}
Add(ArrayEnum,9363307,98,154)
{
link(onItem,8498031:doEvent1,[])
link(onEndEnum,9589809:doEnum,[(139,167)(139,328)])
link(Array,8602002:Array,[])
}
Add(Hub,8498031,147,154)
{
link(onEvent1,12754312:doLoad,[])
link(onEvent2,5987187:doData,[(171,167)(171,209)])
}
Add(Label,3688790,511,147)
{
Left=190
Top=12
Width=111
Height=17
Font=[MS Sans Serif,8,1,0,1]
Caption="объем двигателя "
}
Add(Label,11917310,553,175)
{
Left=215
Top=35
Width=13
Height=17
Caption="---"
}
Add(Label,6196985,511,196)
{
Left=190
Top=55
Width=171
Height=17
Font=[MS Sans Serif,8,1,0,1]
Caption="количество лошадиных сил "
}
Add(Label,14915172,553,224)
{
Left=215
Top=85
Caption="---"
}
Add(Label,13636111,510,245)
{
Left=190
Top=108
Width=143
Height=17
Font=[MS Sans Serif,8,1,0,1]
Caption="расход топлива (город)"
}
Add(Label,5471563,553,273)
{
Left=215
Top=133
Caption="---"
}
Add(Label,15935976,511,294)
{
Left=190
Top=155
Width=73
Height=17
Font=[MS Sans Serif,8,1,0,1]
Caption="тип кузова "
}
Add(Label,8798016,553,322)
{
Left=215
Top=180
Caption="---"
}
Add(Label,13125960,511,343)
{
Left=190
Top=206
Width=76
Height=17
Font=[MS Sans Serif,8,1,0,1]
Caption="цвет кузова"
}
Add(Label,13035540,553,371)
{
Left=215
Top=230
Caption="---"
}
Add(MT_MultiData,6992197,385,196)
{
From=4
Count=5
link(onData1,11917310:doText,[(496,202)(496,181)])
link(onData2,14915172:doText,[(496,209)(496,230)])
link(onData3,5471563:doText,[(483,216)(483,279)])
link(onData4,8798016:doText,[(469,223)(469,328)])
link(onData5,13035540:doText,[(461,230)(461,377)])
}
Add(EventFromData,11757345,322,147)
{
link(onEvent,12985999:doSeparateMT,[])
}
Add(MT_MultiData,12985999,364,147)
{
From=2
Count=1
link(onData1,15791721:doCompare,[])
}
Add(If_else,15791721,406,147)
{
Op2=Integer(-1)
link(onTrue,5538358:doWork2,[(446,153)(446,160)])
link(onFalse,5538358:doWork1,[(446,160)(446,153)])
}
Add(ChanelToIndex,5538358,455,147)
{
link(onIndex,11757345:doData,[(499,153)(499,141)(310,141)(310,153)])
}
IDIndex | CaptionIndex | ParentIDIndex | -- | -- | -- | -- | -- | IconIndex |
ID | Наименование модели | ID марки | объем двигателя | количество лошадиных сил | расход топлива | тип кузова | цвет кузова | Иконка |
Однако пользоваться этой особенностью рекомендуется только в том случае, если картеж формируется не разработчиком схемы, а приходит в готовом виде откуда-то из вне (именно так происходит в схеме Маил агента MRA.sha). Во всех остальных случаях рекомендуется все предопределенные звенья размещать в начале картежа друг за другом.
BB-code статьи для вставки
Всего комментариев: 0
(комментарии к статье еще не добавлены)