Создание приложения Windows Forms редактирования файла XML формата.




Примечание. В VS2010 не полностью работают подсказки полей в C++ под.NET. Для того, чтобы получать полный список полей (плюс ещё множество полезных опций) можно попробовать установить надстройку над VS2010, которая называется Visual Assist X. Найти её можно на сайте разработчика: https://www.wholetomato.com/

Эта же надстройка имеется и для версии VS2008.

А. Последовательность действий по созданию проекта.

Выбрать Новый проект

Выбрать Visual C++

Вверху выбрать.NET Framework 3.5, если у вас VS2008 или.NET Framework 4, если VS2010

Выбрать Приложение Windows Forms

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

В редакторе формы Form1 (кликните дважды на файл Form1.h в проекте)

- в тулбаре или меню выбрать «Панель Элементов»

- там выбрать Button и поместить 2 кнопки на форму

- в разделе «Данные» выбрать элемент DataGridView и добавить его на форму

В. Работа с кнопками.

Для отображения иконки вместе с текстом на кнопке вот так:

в свойствах кнопки выберите BackgroundImage и для него укажите файл иконки (тогда фон будет прозрачным) желаемого размера (например, 16 на 16). Далее свойство BackgroundImageLayout установите в None.

Двойной клик на элементах (кнопки или таблице) автоматически сгенерирует call back метод Click (нажатие кнопки мыши на элементе) и переместит курсор на его начало. Обратите внимание, что для любого элемента, который присутствует в классе формы под именем X, можно указать множество call back методов, которые будут автоматически вызываться при определенных действиях. Список событий можно просмотреть в полях элемента (знак молнии). Для этого эти методы должны иметь соответствующий прототип и имя по типу: X_Action. Например:

System::Void button1_Click(System::Object^ sender,System::EventArgs^ e)

System::Void button1_DoubleClick(System::Object^ sender,System::EventArgs^ e)

System::Void button1_MouseMove(System::Object^ sender,System::EventArgs^ e)

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

void InitializeComponent(void);

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

#pragma region Windows Form Designer generated code

и заканчивающуюся строкой

#pragma endregion

В частности в качестве обработчика клика на кнопку там прописан следующий код:

this->buttonLoadXML->Click += gcnew System::EventHandler(this, &Form1::button1_Click);

Как видно имя функции-обработчика указано в качестве параметра при вызове регистратора обработчика события. Вы можете указать здесь имя другой функции. Как можно заметить используются делегаты (аналоги указателей на функции в классическом С++).

Г. Работа с таблицей.

Содержимое элемента DataGridView можно формировать программно (указать какие будут столбцы и/или строки) или прямо в дизайнере.

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

Для правки визуального вида таблицы нажмите на маленькую стрелку, которая отображается в правом верхнем углу элемента. Вот так:

В редакторе содержимого таблицы для простоты обработки оставьте только «Включить правку», остальные галочки удалите.

Выберите опцию «Добавить столбец ». В появившейся форме задайте «Текст заголовка » в значение «Номер » (это будет название столбца). Поле «Имя » можно оставить. Установите галочку в поле «Только чтение » и нажмите кнопку «Добавить ».

Аналогично добавьте второй столбец с текстом заголовка «Название » и атрибутом «Только для чтения » и третий столбец с заголовком «Значение » и НЕ ставьте атрибут «Только для чтения ».

После этого, используя опцию «Правка столбцов » вы можете задать множество атрибутов индивидуально для каждого столбца.

Кроме того, если вы хотите автоматическое выравнивание ширины столбцов и высоты строк под содержимое, то в свойствах таблицы поменяйте значение атрибутов AutoSizeColumnsMode и AutoSizeRowsMode в значения AllCells.

Д. Открытие файла XML.

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

В качестве диалога открытия файла предлагаю использовать уже знакомый по Win API диалог выбора файла. В.NET есть пространство имен System::Windows::Forms – там он и находится. Т.к. это готовый компонент, можно сразу создавать объект этого класса:

OpenFileDialog ^dlg=gcnew OpenFileDialog();

После этого можно задать некоторые свойства диалога. Такие как Title, Filter, InitialDirectory и т.д.

После этого можно отобразить диалог. Если пользователь выберет файл и нажмет кнопку Ok, то это можно проверить, выяснив, что возвращает метод ShowDialog. Примерно так:

if(dlg->ShowDialog()!= System::Windows::Forms::DialogResult::OK) return;

Теперь можно получить путь и имя выбранного файла – оно хранится в свойстве FileName диалога.

Теперь надо проверить, что файл существует. Для этого воспользуйтесь методом Exists класса File в пространстве имен System::IO.

Еще для практики хорошо бы проверить, что файл не 0-го размера. Это можно сделать с помощью свойства Length класса FileInfo того же пространства имен.

В случае неудачи проверки существования или не 0-го размера файла покажите сообщение пользователю с помощью метода Show класса MessageBox. Для формирования текста сообщения используйте конкатенацию строк оператором +.

Далее вам необходимо открыть файл на чтение. Метод OpenRead класса File. Ему передайте имя файла.

Используя открытый поток, создаем объект класса XmlReader с помощью метода Create этого класса.

Теперь для открытия XML документа необходимо создать объект типа XmlDocument пространства имен System::Xml. Это основной объект, в котором будет хранится все содержимое XML файла в разобранном виде. Имейте в виду, что этот объект понадобится не только при открытии файла, но и при его сохранении. Это значит, что указатель (у меня он назван xd) нельзя делать локальной переменной. Сделайте его приватным членом класса Form1. Опять-таки в качестве практики я рекомендую создавать объект следующим образом:

xd = nullptr;

xd = gcnew XmlDocument;

Тут вначале указателю присваивается значение nullptr – это аналог нулевого указателя. В.NET это называется «неиниациализированный указатель», а потом ему присваивается адрес вновь созданного объекта XmlDocument.

Такой прием преследует 2 цели. В случае, если объект во второй строке не создастся, вы сможете проверить это в других методах путем сравнения указателя xd с nullptr.

Иногда вы создаете очень большой объект вместо другого большого объекта, используя один указатель. В таком случае когда память под второй объект будет выделяться, первый ещё не может быть освобожден, т.к. «связан» указателем. И теоретически памяти может не хватить (особенно, если это массивы). Если же до выделения памяти под второй объект «отвязать» первый присвоением указателю nullptr, то сборщик мусора может освободить первый объект и использовать его память для создания второго объекта.

После того, как объект XmlDocument создан, вызовите его метод Load, передав ему указатель на объект XmlReader.

Теперь мы можем разобрать весь XML файл. Для этого вызовем метод ProcessDom (вы можете его назвать по-своему), передав ему указатель xd. Сам метод ProcessDom добавьте самостоятельно руками в класс Form1 в приватную секцию.

После разбора документа можно закрыть объект XmlReader, вызвав для него метод Close и объект File, вызвав для него одноименный метод.

На этом обработчик клика на кнопку открытия файла заканчивается.

Е. Метод ProcessDom

Рассмотрим что стоит написать в функции ProcessDom. Вот её прототип:

void ProcessDom(XmlDocument^ xd);

Последовательность действий такова.

Очистим таблицу от всего, что в ней может находится. Для этого можно воспользоваться методом Clear коллекции Rows таблицы (по умолчанию имя таблицы будет dataGridView1, но вы можете переименовать её).

Создадим указатель на список тегов верхнего уровня XML файла. Для этого создадим указатель типа XmlNodeList и присвоим ему коллекцию ChildNodes нашего объекта XmlDocument.

Теперь воспользуемся механизмом перебора всех элементов коллекции тегов. Например, воспользуемся таким синтаксисом:

for each(XmlNode^ node in children){…}

Для каждого найденного тэга node вызовем метод добавления его в таблицу. Я назвал его AddTableRow. Вы можете выбрать любое другое название. Передавать ему следует указатель node. Я ещё передавал флаг «read only» и цвет строки. Дело в том, что тэги верхнего уровня обычно сами не хранят информацию – за то отвечают теги подуровней. Но отображать тег первого уровня будет содержимое всех дочерних тегов (сами дальше увидите). Поэтому для этих тегов я запрещал редактирование.

Теперь следует проверить, нет ли у текущего node атрибутов. Для этого можно получить указатель на коллекцию XmlAttributeCollection. Это коллекция Attributes. У нашего элемента node.

Так как атрибуты могут отсутствовать, перед использованием необходимо проверить, что указатель XmlAttributeCollection не равен nullptr. После этого аналогично случаю с тегами перебираем все XmlAttribute в коллекции XmlAttributeCollection с помощью цикла for each и вызываем для каждого метод AddTableAttrib (добавляем его руками в приватную секцию класса Form1). В этот метод передаем указатель на XmlAttribute.

Теперь следует перебрать дочерние теги. Аналогично п.2 получаем указатель на коллекцию XmlNodeList посредством свойства ChildNodes, но теперь не для документа, а для нашего текущего node.

Используя тот же метод AddTableRow, но уже без флага «read only» добавляем тэги в таблицу.

Аналогично пункту 5 получаем список атрибутов этих дочерних тегов и аналогично пункту 6 добавляем их в таблицу.

Всё. Глубже – теги 2-го уровня пока рассматривать не будем. Тем более, что в случае полного разбора выгоднее использовать рекурсию.

 

Ж. Содержание метода AddTableRow.

В данном методе необходимо данные тега, переданного указателем типа XmlNode добавить в таблицу.

Создайте новый элемент типа DataGridViewRow.

Для этого элементу установите цвет заднего фона строки через свойство DefaultCellStyle, а далее BackColor.

Добавьте этот элемент в качестве новой строки таблицы – метод Add коллекции Rows таблицы. Метод вернет вам индекс добавленной строки.

Используя этот индекс, получите доступ к первой ячейки строки: в коллекции Rows возьмите элемент с этим индексом, а в нем коллекцию Cells, у которой выберите элемент с индексом 0. Так вы получите указатель на объект типа DataGridViewCell.

В элементе ячейки установите свойство Value в значение номера строки.

Теперь аналогично пункту 4 получите указатель на ячейку 1 строки. Аналогично пункту 5 установите её значение в свойство Name элемента XmlNode. Задайте текст всплывающей подсказки – свойство ToolTipText.

Теперь аналогично пункту 4 получите указатель на ячейку 2 строки. Аналогично пункту 5 установите её значение в свойство InnerText элемента XmlNode. Задайте текст всплывающей подсказки – свойство ToolTipText. В соответствии со значением флага «read only» установите цвет ячейки. Например для редактируемой ячейки в белый.

dataGridView1->AllowUserToAddRows

dataGridView1->AllowUserToDeleteRows

Параметры, отвечающие за добавление/удаление пользователем строк. Если разрешить, то всегда будет «виртуальная» строка, добавляющаяся по мере написания теста в ячейке.

З. Содержание метода AddTableAttrib.

Данный метод полностью аналогичен методу AddTableRow с той лишь разницей, что передается не указатель на элемент типа XmlNode, а типа XmlAttribute. Однако все остальное остается аналогичным, т.к. у этого элемента так же имеется свойства Name и InnerText.

Более того п.1-5 абсолютно идентичны и их можно выделить в отдельный приватный метод.

Напишите тело метода AddTableAttrib самостоятельно.

 

И. Обработчит нажатия кнопки «Сохранить XML файл»

Обработчик события клика на кнопку «Сохранить XML файл». Его код во многом похож на обработчик открытия файла. Последовательность следующая.

Если указатель xd не инициализирован (равен nullptr), то показываем сообщение пользователю что нечего сохранять и выходим.

Создаем объект SaveFileDialog, устанавливаем ему Title, Filter и InitialDirectory. Показываем диалог и если пользователь нажал не кнопку Ok выходим.

Методом OpenWrite класса File получаем объект FileStream.

Из него методом Create класса XmlWriter создаем объект типа XmlWriter.

Вызываем функцию обновления содержимого разобранного в памяти XML документа xd. Её цель получить из таблицы измененные элементы и обновить объект xd. Пусть эта функция называется ParseDom. Она будет описана ниже.

Вызываем метод Save для объекта xd, передаваю ему объект типа XmlWriter.

Закрываем путем вызова методов Close объект XmlWriter, а потом и объект FileStream.

 

К. Метод ParseDom

Метод ParseDom напишем в приватной секции. Ему можно передать объект xd, а можно и сразу использовать его, т.к. он является членом класса Form1.

Содержимое функции практически идентично содержимому метода ProcessDom за следующим исключением.

п.1 тут не нужен.

В п.4. не вызываем никаких методов типа AddTableRow, т.к. корневые объекты XML мы договорились сделать нередактируемыми – следовательно и обновлять их из таблицы не нужно. Однако необходимо «посчитать» сколько строк в таблице мы прошли. Так что тут увеличим счетчик на 1 каждую итерацию цикла.

В п.6 вместо вызова метода AddTableAttrib вызываем метод GetTableAttrib (он будет описан далее). Так же не забываем инкрементировать счетчик строк таблицы каждый вызов метода.

В п.8. вызываем метод GetTableRaw вместо метода AddTableRow и также увеличиваем каждый раз счетчик строк.

Л. Метод GetTableRaw

Метод GetTableRaw получает указатель на объект XmlNode и номер строки таблицы (сюда надо передавать значение счетчика строк из метода ParseDom). Основная цель данного метода обновить содержимое элемента XmlNode из соответствующей строки таблицы.

Необходимо сделать следующее.

Получаем указатель на 2-ю ячейку заданной строки таблицы. Для этого берем в коллекции Rows элемент с номером строки и далее в коллекции Cells 2-й элемент.

Теперь свойство Value для этой ячейки таблицы преобразуем в строку методом ToString и присваиваем потом свойству InnerText нашего элемента XmlNode.

М. Метод GetTableAttrib

Метод GetTableAttrib практически идентичен методу GetTableRaw за одним исключением. Передается туда элемент типа XmlAttribute, а не XmlNode. Все остальное остается неизменным, т.к. и у этого элемента есть свойство InnerText.

В результате рабочий вариант программы должен выглядеть похожим на такой:

Для теста используйте следующее содержимое XML файла (откройте блокнот, скопируйте туда текст и сохраните в файле с расширением XML и в формате UTF-8):

 

<?xml version="1.0" encoding="UTF-8"?>

<content>

<Node_1>

Это текст первого тега

</Node_1>

<Node_2 Id="13" Price="too low" Day="Friday">

Это текст тега 2

</Node_2>

<Node_3>

Это текст тега 3

</Node_3>

<Node_4 Id="25" Price="very high">

Это текст тега 4

</Node_4>

</content>

 



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2018-10-25 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: