TreeNodeCollection::IndexOfKey не ищет Node
где row[0] - уникальный индекс (автоинкримент)
row[1] - родительский индекс (ссылка на row[0])
row[2] - текстовое поле
запрос из бд упорядочен по row[1] для того что бы при прохождении от начала до конца каждое последующее значение уже имело родителя
foreach (DataRow row in dt.Rows)
{
if ((int)row[5] == 0) //root
{
tnc.Add(Convert.ToString(row[0]), (string)row[2]);
}
else
{
int i = tnc.IndexOfKey(Convert.ToString(row[1]));
if (i>=0)
tnc.Nodes.Add(Convert.ToString(row[0]), (string)row[2]);
}
проблема в этой строчке: int i = tnc.IndexOfKey(Convert.ToString(row[1]));
ищет индекс только для 0-го уровня
Как сделать что бы искал по всему TreeNodeCollection?
Dictionary<int, TreeNode>:
TreeNodeCollection tnc = treeView1.Nodes;
foreach (DataRow row in dt.Rows)
{
int id = row[0];
TreeNode node = new TreeNode(Convert.ToString(row[0]), (string)row[2]);
nodes[id] = node;
if ((int)row[5] == 0) //root
{
tnc.Add(node);
}
else
{
TreeNode parent = null;
if(nodes.TryGetValue(row[1], ref parent))
parent.Nodes.Add(node);
}
(код не проверял - писал в браузере)
Но вообщето впихивать в код построения интерфейса запрос к БД - очень плохая практика.
TreeNodeCollection tnc = treeView1.Nodes;
foreach (DataRow row in dt.Rows)
{
if ((int)row[5] == 0) //root
tnc.Add(Convert.ToString(row[0]), (string)row[2]);
else
{
TreeNode []tnc1 = tnc.Find(Convert.ToString(row[1]), true);
if (tnc[0] !=null)
tnc1[0].Nodes.Add(Convert.ToString(row[0]), (string)row[2]);
}
}
[QUOTE=hardcase]
Но вообщето впихивать в код построения интерфейса запрос к БД - очень плохая практика. [/QUOTE]
Чем плохо? (TV рисуется один раз при запуске приложения, далее просто от него нужны индексы для запроса в БД)
Где надо?
Как сделать?
ЗЫ: интересует, насколько метод Find(...) будет быстрее/медленней C++ map<int, TreeNode*>
Где надо?
Как сделать?
Это скорее вопрос проектирования архитектуры приложения.
Разделение логики его работы от интерфейса пользователя.
Я бы объявил отдельные классы, которые отвечают за сами "узлы" и уже с их помощью отображал бы дерево объектов.
Разделение логики его работы от интерфейса пользователя.
Я бы объявил отдельные классы, которые отвечают за сами "узлы" и уже с их помощью отображал бы дерево объектов.
Если честно, то плохо представляю как это можно в отдельный класс загнать..
Пример бы какой посмотреть, о чем вообще речь идет
Пример бы какой посмотреть, о чем вообще речь идет
Более менее путевое обсуждение нашел здесь (запрос гуглу вида DAL DAO BLL).
Идея. Создается многослойное приложение (multi-tier app) каждый слой отвечает за четко определённые действия и работает напрямую только с предыдущим:
1) Уровень хранения Data Store, обычно реализуется в СУБД, это реляционные таблицы + [иногда] довольно простые хранимые процедуры для добавления, удаления, модификации строк.
2) Уровень доступа к данным Data Access Layer. Реализует сценарии доступа к данным, как то: использование типизированных датасетов, использование ORM (Object Relational Mapping) генераторов типа NHibernate, использование самописного ORM.
Обычно этот уровень предоставляет совего рода API для работы над данными и выполнением запросов - некоторые куски бизнесс-логики приложения из которых на следующем уровне сформируется законченный бизнесс-процесс.
Например, когда используется самописный ORM, то этот API - часто, всего лишь, обертки над вызовами хранимых процедур в СУБД + некоторое кеширование частых запросов.
На этом уровне также могут быть определены DTO (Data Transfer Object) объекты - специальные легковесные сущности, обычно представляющие конкретные строки в таблицах СУБД или коллекции строк (чем не датасет? ;) ); они могу использоваться как промежуточное звено для передачи данных на BLL уровень.
3) Уровень бизнесс-логики Bussines Logic Layer. Уровень на котором формируются бизнесс-объекты - сущности, реализующие абстракцию "реального мира". Т.е. если мы создаем базу данных DVD дисков, то бизнесс объектом станет DvdDisk, Artist и прочие подобные. Эти объекты позволяют выполнять какие-либо высокоуровневые действия (помещение диска в БД и одновременно занесение туда нового артиста) - бизнесс-логику.
4) Уровень представления User Interface. Это пользовательскй фронт-энд. Например, веб-страничка или форма настольного приложения. Интерфейс содержит лишь элементы представления бизнесс-объектов (деревья, таблички, выпадающие списки) и элементы для управления бизнесс-объектами.
Очень рекомендуется руководствоваться принципами MVC (Model View Controller) архитектуры при проектировании BLL и построении интерфейса пользователя. Критерий качества: BLL можно обернуть и в WinForms и в ASP.NET юзерский интерфейс (view), при этом не придется править ни единой строчки ни в нем, ни в нижележащих уровнях.
Ну, типа так:
{
public MyRowType DataSource;
private MyTableType m_Childeren;
public MyTableType Childeren
{
get
{
if (m_Childeren == null)
m_Childeren = new MyTableType();
return m_Childeren;
}
}
public void UpdateData(bool Save)
{
if (DataSource != null)
{
if (Save)
{
try
{
MyDataAdapter.Update(DataSource);
DataSource.AcceptChanges();
}
catch
{
}
foreach(MyTreeNode child in this.Nodes)
{
child.UptateData(true);
}
}
else // !Save
{
this.Text = DataSource[2];
MyDataAdapter.FillChilderen(Childeren, (int)DataSource[0]);
foreach(MyRowType child in Childeren)
{
item = new MyTreeNode();
item.DataSource = child;
item.UpdateData(false);
}
}
}
}
}
PS Аналогично, код писан навскидку в браузере
такая схема будет тормозная, т.к. записей м.б. весьма и весьма много (пробовал на 100 000) рисует очень долго.
долго думал над алгоритмом быстрого рисования, в результате остановился на том что написал выше, только еще его подредактировал, в соответсвии с советом hardcase
вот что получилось:
Dictionary<int, TreeNode> NodeMap = new Dictionary<int,TreeNode>();
treeView1.BeginUpdate();
foreach (DataRow row in dt.Rows)
{
if ((int)row[5] == 0) //root nodes
{
NodeMap[(int)row[0]] = tnc.Add(Convert.ToString(row[0]), (string)row[2]);
}
else //child nodes
{
TreeNode parent;
if (NodeMap.TryGetValue((int)row[1], out parent))
NodeMap[(int)row[0]] = parent.Nodes.Add(Convert.ToString(row[0]), (string)row[2]);
}
}
treeView1.EndUpdate();
всего на 100 000 записей уходит около 30 сек.
ЗЫ: VCL от борланда по такой же схеме за 2.5 мин строит
появляется дополнительный гемор с рисованием крестиков на тех нодах у которых есть подчиненые
ЗЫ: сервер делает всего 1(один) запрос и выдает упорядоченное по ParentIndex представление базовой таблицы (таким образом для каждой последующей записи уже есть Parent, для этого и загоняю в словарь все ноды), потом я проято програмно пробегаюсь по всем записям и строю дерево
Я считаю, что лучше потратить 2 часа на организацию проверки наличия дочерних узлов, чем каждый раз тратить по пол-минуты на запуск. Кроме того, в зависимости от ситуации, можно предпринять как минимум три действия, с решением этой проблемы связанных:
- запрашивать количество дочерних узлов
- отображать кнопку разворачивания всегда, а запрашивать дочерние узлы лишь при необходимости
- запрашивать дочерние записи для всез видимых узлов дерева
Организация выполнения любого из этих действий не должна, по идее, вызвать у тебя особых проблем.
- запрашивать количество дочерних узлов
- отображать кнопку разворачивания всегда, а запрашивать дочерние узлы лишь при необходимости
- запрашивать дочерние записи для всез видимых узлов дерева
Организация выполнения любого из этих действий не должна, по идее, вызвать у тебя особых проблем.
Я считаю что лучше при загрузке один раз подождать (визуализировав процесс загрузки), нежели при каждом раскрытии дерева ждать пока подгрузяться дочернии узлы, это при том что расчет идет на несколько сотен пользователей, и каждый будет напрягать сервер своим запросом, у сервера при работе приложения куча других задач будет
Для всех лучше много мелких запросов к бд, чем один большой.
И приложение стартовать будет быстрее. И на сервер меньше нагрузки.
Кроме того никто не мешает постепенно подгружать дерево - в фоне так сказать.
Такой вопрос, каким макаром потом вытащить Convert.ToString(row[0]) из три вью??
То есть получить это значение??
Такой вопрос, каким макаром потом вытащить Convert.ToString(row[0]) из три вью??
То есть получить это значение??