C++ C++ C# C# ASP.NET Security ASP.NET Security ASM ASM Скачать Скачать Поиск Поиск Хостинг Хостинг  
  Программа для работы с LPT портом...
Язык: .NET — ©Alexey...
  "ASP.NET Atlas" – AJAX в исполнении Micro...
Язык: .NET — ©legigor@mail.ru...
  "Невытесняющая" Многопоточность...
Язык: C/C++ — ©...
  01.05.2010 — Update World C++: Сборник GPL QT исходников
  15.12.2007 — Весь сайт целиком можно загрузить по ссылкам из раздела Скачать
Хостинг:
Windows 2003, ASP.NET 2.0
бесплатный и от 80 руб./мес


   Отправить письмо
Кулабухов Артем, Беларусь




 CTreeCtrl запись/чтение в файл / Основы программирования с помощью библиотеки MFC / Visual C++

Сохранение и загрузка в/из файла дерева CTreeCtrl

Данный пример я построил на основе примера №1( CTreeCtrl в диалоговом окне), немного модифицировав функцию добавления узла в дерево, и добавив, собственно, код необходимый для реализации записи/чтения дерева на диск.
Для этого в форму нашего диалога добавим две кнопки "Сохранить Дерево", "Загрузить Дерево" и один чекбокс служащий флагом переключения надо ли загрузить дерево "С Нуля" или, к уже существующему дереву, добавить ветвей и листьев из файла.(Если в чекбоксе стоит фишка то будем добавлять если фишка не стоит будем загружать дерево с нуля.)


1. Теперь по поводу изменений в функции добавления элемента:
Изменения кода я сделал чтобы можно было создавать деревья сложной вложенности , такие как показанно ниже.

Вот текст кода позволяющий это делать:

void CADlg::OnButton1()
{
// TODO: Add your control notification handler code here

m_tree.ModifyStyle(LVS_TYPEMASK,TVS_LINESATROOT | TVS_HASLINES |
TVS_HASBUTTONS | TVS_EDITLABELS );

TV_INSERTSTRUCT tvstruct;
HTREEITEM pNode,pItm;

pItm = m_tree.GetSelectedItem();
tvstruct.hParent = pItm;
tvstruct.item.mask = TVIF_TEXT ;
tvstruct.item.pszText = "node";
pNode = m_tree.InsertItem(&tvstruct);
m_tree.SetFocus();

}

Смысл состоит в том, что выбранный элемент дерева является, в текущий момент, родительским и внутрь него будут добавлены элементы.
функция m_tree.SetFocus() вызывается здесь потому, что без неё, изменения в дереве становятся видны только после того как окно дерева получит фокус. А так: нажал "добавить" и сразу видишь добавилось.
А так , если честно, ведь главное идея, а как, что и куда добавлять Вы сами напишите.

2. Далее пробежимся по чекбоксу ( IDC_CHECK1 ), никуда от него не денешься.
Первое что надо сделать после его вставки на диалоговое окно это вызвать из контекстного меню ClassWizard, выбрать вкладку Member Variables и дважды щелкнув по строке с IDC_CHECK1 ввести в появившемся окне имя переменной m_check, как в этом проекте, или другое. После всего этого колдовства должно получиться нечто вроде того, чего ниже.

Вернувшись к редактору диалогового окна, и дважды щелкнув по самому чекбоксу (IDC_CHECK1) перейдем к редактированию кода,который обслуживает нажатия. Будет сгенерированна функция void CADlg::OnCheck1 , которую надо следующим макаром изменить:

void CADlg::OnCheck1()
{
// TODO: Add your control notification handler code here
if (m_check == TRUE) m_check= FALSE;
else m_check=TRUE;
}


Нельзя забыть проиннициализировать наш чекбокс, т.е. добавить в функцию CADlg::OnInitDialog() строчку типа m_check = FALSE; для этого надо дважды щелкнуть на функции OnInitDialog() класса CADlg и в конце оригинального кода дописать m_check = FALSE;


3. ТЕПЕРЬ, собственно, к файловым операциям. Для того чтобы записывать дерево в файл нам надо создать две обслуживающие, дополнительные функции:
int CADlg::GetIndentLevel(HTREEITEM hItem) для определения вложенности элемента дерева и функцию
HTREEITEM CADlg::GetNextItem(HTREEITEM hItem) для определения следующего элемента дерева. Это делается, как показанно на рисунках ниже: на классе CADlg щелкнем правой кнопкой мыши выберем Add Member Function

После чего перейдем к описанию значений функций,как показанно ниже.



После этого перейдем непосредственно к коду для этих функций.
* Следующая функция определяет уровень вложенности элемента дерева.

int CADlg::GetIndentLevel(HTREEITEM hItem)
{

int iIndent = 0;
while( (hItem= m_tree.GetParentItem(hItem))!= NULL) iIndent++;
return iIndent;
}

HTREEITEM CADlg::GetNextItem(HTREEITEM hItem)
{
HTREEITEM hti;
if( m_tree.ItemHasChildren(hItem))
return m_tree.GetChildItem(hItem);
else
{
while ( (hti=m_tree.GetNextSiblingItem(hItem))== NULL)
{
if ((hItem =m_tree.GetParentItem(hItem))==NULL) return NULL;
}
}
return hti;
}

* Следующая функция определяет следующий элемент дерева

HTREEITEM CADlg::GetNextItem(HTREEITEM hItem)
{
HTREEITEM hti;
if( m_tree.ItemHasChildren(hItem))
return m_tree.GetChildItem(hItem);
else
{
while ( (hti=m_tree.GetNextSiblingItem(hItem))== NULL)
{
if ((hItem =m_tree.GetParentItem(hItem))==NULL) return NULL;
}
}
return hti;
}

* После того как мы добавили эти функции надо добавить в программу код для кнопок "Сохранить Дерево" (IDC_BUTTON4) и "Загрузить Дерево" (IDC_BUTTON5)

* текст рограммы для "Сохранить Дерево" (IDC_BUTTON4)

void CADlg::OnButton4()
{
// TODO: Add your control notification handler code here CFile theFile;
theFile.Open("tree.sav",CFile::modeCreate| CFile::modeWrite);
CArchive ar(&theFile,CArchive::store);

HTREEITEM hti = m_tree.GetRootItem();
while(hti)
{
int indent = GetIndentLevel(hti);
while(indent--)
ar.WriteString("\t");
ar.WriteString(m_tree.GetItemText(hti)+"\r\n");
hti= GetNextItem(hti);
}

ar.Close();
theFile.Close();
}

* текст рограммы для "Загрузить Дерево" (IDC_BUTTON5)

void CADlg::OnButton5()
{
// TODO: Add your control notification handler code here
if(!m_check) m_tree.DeleteAllItems();

CString sLine;
CFile theFile;
if(!theFile.Open("tree.sav",CFile::modeRead))
{ MessageBox("File opening Error, possible it doesn't exist");return;}
;
CArchive ar(&theFile,CArchive::load);

if(!ar.ReadString(sLine)) return;
HTREEITEM hti = NULL;
int indent,baseindent = 0;
while(sLine[baseindent]=='\t')
baseindent++;
do
{
if(sLine.GetLength==0)
continue;
for(indent=0;sLine[indent]=='\t';indent++);
sLine=sLine.Right(sLine.GetLength()-indent);
indent-=baseindent;

HTREEITEM parent;
int previndent= GetIndentLevel(hti);
if (indent==previndent) parent = m_tree.GetParentItem(hti);
else if (indent>previndent) parent = hti;
else
{
int nLevelsUp = previndent - indent;
parent = m_tree.GetParentItem(hti);
while (nLevelsUp--)
parent = m_tree.GetParentItem(parent);
}
hti = m_tree.InsertItem(sLine,parent? parent: TVI_ROOT,TVI_LAST);
}
while(ar.ReadString(sLine));
ar.Close();
theFile.Close();
}

Ну вот, вроде бы, и все. Все должно заработать.

* Комментарии к текстам программ.

Дерево сохраняется в файл с именем "tree.sav" Сохранение и загрузка дерева осуществлены на основе методов работы с классом архивов CArchive. Функция чтения дерева из файла не имеет защиты от чтения НЕ ТОГО файла в качестве выхода из положения можно предложить запись в начало файла дерева какого либо маркера типа "TreeControlSaveFile" и при считывании проверять этот маркер. Но опять же если маркер совпадет а дальше будет НЕ ТО, я не проверял что получится. Это я на ВАС оставил: учитесь.
Кроме всего прочего, в документации Майкрософт желает чтобы операции чтения/записи обьектов производились через метод SERIALISE, так у меня сделать не получилось. Если у Вас получится буду рад если пришлете примеры. С Уважением Денис Козлов kozloff@dviyka.odessa.net
Скачать пример рабочего проекта можно здесь