Теперь давайте рассмотрим как изменять уже имеющийся XML-документ?



 

Для вставки элемента в иерархию XML-документа необходимо создать элемент типа XmlNode и задать его родителя, делается это следующим образом:

 

// Создаём node

// book - имя узла

XmlNode node = xmlDoc.CreateElement("book");

// Добавляем его в качестве ребенка

parentNode.AppendChild(node);

 

Удаление узла?

 

// Удаление узла

parentNode.RemoveChild(node);

 

Работа с атрибутами:

 

// Создаём новый атрибут

// genre - имя атрибута

XmlAttribute newAttr = doc.CreateAttribute("genre");

newAttr.Value = "novel"; // Задаём его значение

// Добавляем атрибут в коллекцию атрибутов элемента

node.SetNamedItem(newAttr);

// Удаляем атрибут

node.Attributes.RemoveNamedItem("genre");

// Изменяем значение атрибута

XmlAttributeCollection Attribs = node.Attributes;

XmlAttribute attr = (XmlAttribute)Attribs.GetNamedItem("genre");

attr.Value = "fiction";

 

Настало время разработать более-менее полезную функциональность. Используем возможности XML и сделаем на основе него полноценную базу данных.

 

Будем использовать загрузку данных из XML файла в DataSet, отметив, что после загрузки данных, доступ к данным ничем не отличается от работы с данными при их загрузке из таблиц БД.

 

Для XML справедливо: «Содержимым элемента (content) называется всё, что расположено между открывающим и закрывающим тегами. Это текст, вложенные элементы, комментарии и т.п.». Все спецификации XML документов подчёркивают, что именно текст, то, что является содержимым документа. Все средства для работы с XML документами, в том числе и методы класса XmlDocument, также ориентированы на текст (CreateTextNode и т.п.).

 

Из сказанного можно сделать вывод: хранить двоичные данные можно, только если они будут представлены как текстовые строки. Однако, прямое преобразование всего массива байт (двоичных данных) в текстовую строку не будет решением в силу того, что возможно присутствие в двоичном коде значений, совпадающих со служебными символами кодировки строк и значений, равных завершению строки. Как результат - мы сможем поместить в контент тэга только часть строки (до первого совпадения двоичного символа со значением окончания строки) при возможном отображении помещенного кода на нескольких строках XML файла для одного содержимого контента. Следовательно, строка должна формироваться и присваиваться значению контента как единое целое. Способы выполнить это могут быть разные, от посимвольного преобразования байт в значение char и последовательного добавления результата в содержимое контента (медленный способ), до использования StringBuilder (быстрый способ).

 

Наиболее часто хранение двоичных данных связано с задачей хранения рисунков (хотя приведенный ниже код будет равно хорошо работать и при использовании его для хранения в виде двоичных данных Web-страниц, Word или Excel-документов и прочее). Содержимое файла рисунка: массив шестнадцатеричных символов. Как следствие, и код представления рисунка в content опирается на шестнадцатеричное отображение строк.

 

Так что же это будет за функциональность? Приложение будет по указанному через диалог изображению создавать запись в базе данных (XML-файл), куда будет помещаться как сведения об изображении, так и само изображение в виде длинной строки символов. Также по номеру (ID рисунка) из базы можно будет восстановить это изображение и просмотреть его. Изображения можно будет только добавлять в базу, и просматривать добавленное уже из базы. Для этой функциональности у нас предусмотрена кнопка База данных обоев. А также заранее заготовленный файл Wallpapper - DB . xml с содержанием:

 

<?xml version="1.0" encoding="windows-1251"?>

<NewDataSet>

<wallpapper>

<id></id>

<name></name>

<pichash></pichash>

<picext></picext>

<size></size>

</wallpapper>

</NewDataSet>

 

Немного о тэге «NewDataSet». Поскольку мы будем работать с DataSet, то при сохранении содержимого DataSet в XML файл, данный тэг добавляется автоматически. Также этот тэг является параметром свойства DataSetName.

Первым делом нам нужна новая форма. Добавим её в наше приложение: Проект -> Добавить новый элемент... (Ctrl+Shift+A). В открывшемся окне ищем Форма Windows Forms, в поле Имя ниже вводим LWP 10 Wallpapper . cs:

 

Свойства формы:

(Name): LWP10Wallpapper
Text: Работа с базами данных (C#) :: База данных обоев
Size: 500; 500

 

Расставим элементы как показано на рисунке ниже:

 

Рис. 5. 4. Расстановка элементов в новой форме

 

Основным элементом здесь является PictureBox ( ), который разместим в самом центре (пунктирный прямоугольник). Остальные элементы это три кнопки, четыре TextBox, два «именных» Label’а слева под рисунком (Имя файла: и Разрешение рисунка:).

 

Свойства элементов:

 

Button:

(Name): B_OPEN
Text: Выбрать рисунок
Size: 200; 23

Button:

(Name): B_SEARCH
Text: Выбрать рисунок для добавления
Size: 200; 23

Button:

(Name): B_SAVE
Text: Сохранить рисунок в базе данных
Size: 200; 23

TextBox:

(Name): TB_NUMBER

TextBox:

(Name): TB_FORMAT

TextBox:

(Name): TB_SIZE
ReadOnly: True

TextBox:

(Name): TB_NAME

PictureBox:

(Name): PB_MAIN

OpenFileDialog:

(Name): OFD_FIND
InitialDirectory: D:\
Filter GIF-файлы|*.gif|BMP-файлы|*.bmp|JPEG-файлы|*.jpg

Label:

(Name): L_NAME
Text: Имя файла:

Label:

(Name): L_SIZE
Text: Разрешение рисунка:

 

Обработчик события Click для кнопки База данных обоев главной формы выглядит так:

 

   private void B_XML_DB_Click(object sender, EventArgs e)

   {

       LWP10Wallpapper DBWallpapper = new LWP10Wallpapper();

       DBWallpapper.ShowDialog();

   }

 

В начало файла LWP 10 Wallpapper . cs добавим следующие строчки:

 

using System.Xml;

using System.IO;

using System.Drawing.Imaging;

 

В этом же файле найдём:

 

public partial class LWP10Wallpapper : Form

{

 

Добавим после:

 

   // Директория выполнения приложения

   private string String_Path = String.Empty;

    XmlDocument XML;

   // Классы для работв с XML-документом как с объектом базы данных

   DataTable WallpapperDataTable = null;

   DataSet WallpapperDataSet = null;

 

Событие Load нашей дочерней формы:

 

   private void LWP10Wallpapper_Load(object sender, EventArgs e)

   {

       //Path = Directory.GetCurrentDirectory(); // Текущая директория, из которой запущено приложения

       String_Path = @"D:\\";

       L_NAME.Text = "Имя рисунка: ";

       L_SIZE.Text = "Разрешение рисунка: ";

       // Инициализируем объект StreamReader, считывающий символы из потока байтов в определённой кодировке

       using (StreamReader SR = new StreamReader(String_Path + @"\Wallpapper-DB.xml", System.Text.Encoding.UTF8))

       {

           WallpapperDataSet = new DataSet();

           WallpapperDataSet.ReadXml(SR, XmlReadMode.Auto); // Считываем XML-схему и данные в DataSet

           WallpapperDataTable = WallpapperDataSet.Tables[0];

       }

    }

 

Обратим внимание на параметр метода ReadXml: XmlReadMode.Auto - он позволит DataSet правильно создать схему с учётом значений мегатэгов, а также на значение Encoding.UTF8. Причина использования именно этой кодировки: DataSet по умолчанию будет сохранять данные в кодировке UTF-8.

В самом этом коде инициализируются основные пути к файлу базы данных, а также создаётся объект StreamReader (который занимается считыванием символов с файла в определённой кодировке, в данном случае UTF -8), после чего на основе XML-схемы формируется DataSet (столбцы таблицы).

 

Событие Click кнопки Выбрать рисунок для добавления:

 

   private void B_SEARCH_Click(object sender, EventArgs e)

   {

       OFD_FIND.InitialDirectory = String_Path;

 

       if (OFD_FIND.ShowDialog() == DialogResult.OK)

       {

           PB_MAIN.Image = Image.FromFile(OFD_FIND.FileName);

           TB_FORMAT.Text = Path.GetExtension(OFD_FIND.FileName);

           TB_NAME.Text = Path.GetFileNameWithoutExtension(OFD_FIND.FileName);

           TB_SIZE.Text = PB_MAIN.Image.Width.ToString() + "x" + PB_MAIN.Image.Height.ToString();

       }

   }

 

Событие Click кнопки Сохранить рисунок в базе данных:

 

   private void B_SAVE_Click(object sender, EventArgs e)

   {

       // Сохранять не будем - если что-то не ввели

       if (PB_MAIN.Image == null) return;

       if (TB_NAME.Text == "") return;

       if (TB_FORMAT.Text == "") return;

       if (TB_SIZE.Text == "") return;

       // Строки для метода SELECT в XML-документе

       DataRow[] datarows = null;

       // Ищем максимальное ID в DataSet (в DataTable)

       string s = string.Empty;

 

       try

       {

           datarows = WallpapperDataTable.Select("id=max(id)");

           s = datarows[0]["id"].ToString();

       }

       catch (Exception ex)

       {

           MessageBox.Show(ex.Message, "Работа с базами данных (C#) :: База данных обоев");

       }

 

       if (s == "" || s == string.Empty) // Если база данных пустая, то...

       {

           s = "0";

       }

       // Для формирования строки рисунка создаём StringBuilder

       StringBuilder SB = new StringBuilder();

       int i = int.Parse(s) + 1; // Если база пустая, то начинаем с 1, иначе, с максимального номера + 1

       // Создаём новую строку для WallpapperDataSet

       DataRow datarow = WallpapperDataSet.Tables[0].NewRow(); // Формируем DataRow на основе DataSet

       // Присваиваем значения столбцам строки

       datarow[0] = Convert.ToString(i);

       datarow[1] = TB_NAME.Text.Trim();

       // Формируем строковое представление рисунка

       using (MemoryStream MS = new MemoryStream())

       {

           PB_MAIN.Image.Save(MS, ImageFormat.Gif); // Сохраняем изображение в потом MemoryStream, расширение *.gif

           byte[] b = new byte[MS.Length]; // 8-битное число (массив) длины потока в байтах

           //memorystream.Read(b, 0, (int)memorystream.Length);

           b = MS.GetBuffer(); // Присваиваем byte b массив байтов потока

           s = string.Empty;

           foreach (Byte zb in b)

           {

               int a = (int)zb;

               SB.Append(a.ToString("X2")); // Формируем окончательную строку (путём добавления) из данных массива байтов в шестнадцатеричном виде (X2) (шестнадцатеричное представление каждого байта рисунка)

               //value = 123456789;

               //Console.WriteLine(value.ToString("X"));

               // Выведет: 75BCD15

               //Console.WriteLine(value.ToString("X2"));

               // Выведет: 75BCD15

           }

           datarow[2] = Convert.ToString(SB); // Отправляем всю строку в столбец pichash нашей базы данных

       }

       datarow[3] = TB_FORMAT.Text.Trim();

       datarow[4] = TB_SIZE.Text.Trim();

       WallpapperDataSet.Tables[0].Rows.Add(datarow); // Формируем всю запись базы данных в DataSet

       // Удаляем строку с пустыми значениями, которые при первоначальной

       // загрузке были использованы для формирования схемы

       if (i == 1)

       {

           WallpapperDataSet.Tables[0].DefaultView.AllowDelete = true;

           WallpapperDataSet.Tables[0].DefaultView.Delete(0);

       }

       PB_MAIN.Image = null;

       TB_SIZE.Text = "";

       TB_FORMAT.Text = "";

       TB_NAME.Text = "";

       // Сохраняем данные

       WallpapperDataSet.WriteXml(String_Path + @"\Wallpapper-DB.xml", XmlWriteMode.WriteSchema);

       WallpapperDataSet = new DataSet();

       // Вновь загружаем сохраненные данные

       WallpapperDataSet.ReadXml(String_Path + @"\Wallpapper-DB.xml", XmlReadMode.Auto);

       WallpapperDataTable = WallpapperDataSet.Tables[0];

      }

 

Этот код выполняем основные функции по сохранению данных выбранного рисунка в базе данных. Вначале проверяется заполнение всех TextBox’ов (кроме верхнего). Если данные были введены и рисунок указан, далее проверяется наличие в базе ещё добавленных записей (по максимально числу в тэгах < id > X </ id >). Если БД пустая, начинаем заполнение с номера 1. После чего инициализируется объект StringBuilder и производятся основные действия по переведению байтов рисунков в строчное представление для сохранение в базе. Данные TextBox’ов также заносятся в базу.

 

Теперь инициализируем извлечение данных из базы. Событие KeyPress для TB _ SEARCH (верхний TextBox):

 

   private void TB_NUMBER_KeyPress(object sender, KeyPressEventArgs e)

   {

       // Введённые символы должны быть только цифрами, иначе ввода не будет (символ не введеётся)

       if (!Char.IsDigit(e.KeyChar))

       {

           e.Handled = true;

       }

   }

 

Событие Click кнопки Выбрать рисунок:

 

   private void B_OPEN_Click(object sender, EventArgs e)

   {

 

       if (TB_NUMBER.Text.Trim() != "")

       {

           TB_NAME.Text = "";

           TB_FORMAT.Text = "";

           TB_SIZE.Text = "";

           PB_MAIN.Image = null;

           GetPicture(TB_NUMBER.Text.Trim()); // Отправляем номер функции, которая вытащит из XML-документа все данные

       }

 

   }

 

И код функции для выбора по номеру, вводимому в TB _ NUMBER:

 

   private void GetPicture(string X)

   {

       DataRow[] datarows = null;

 

       try

       {

           datarows = WallpapperDataTable.Select("id=" + X); // Получаем все данные DataTable по ключу <id>X</id>

       }

       catch (Exception ex)

       {

           MessageBox.Show(ex.Message, "Работа с базами данных (C#) :: База данных обоев");

           return;

       }

 

       if (datarows.Length > 0) // Не пусто ли?

       {

 

           foreach (DataRow datarow in datarows) // Перебираем все столбцы записи

           {

               L_NAME.Text = "Имя файла: " + datarow["name"].ToString();

               string s3 = datarow["pichash"].ToString();

               L_SIZE.Text = "Разрешение рисунка: " + datarow["size"].ToString();

               using (MemoryStream MS = new MemoryStream())

               { 

                  

                   for (int i = 0; i < s3.Length / 2; i++) // Обходим половину знаков строки из <pichash>s3</pichash> в верхнем цикле, макс. i = 249 если s3.Length = 500

                   {

 

                       if (i * 2 + 1 < s3.Length) // Последнее условие: 249 * 2 + 1 < 500

                       {

                           string s02 = Convert.ToString(s3[i * 2]); // Чётные символы (посл.: 249 * 2 = 498 символ, предпоследний в строке)

                           string s03 = Convert.ToString(s3[i * 2 + 1]); // Нечётные символы (посл.: 249 * 2 + 1 = 499 символ, последний в строке)                             

                           string s04 = s02 + s03; // Объединяем в одну строку (A + B)

                           int a = int.Parse(s04, System.Globalization.NumberStyles.HexNumber); // 32 разряда получаем из двух 16 разрядных чисел юникода (A + B)

                           MS.WriteByte(Convert.ToByte(a)); // Восстанавливает один байт за проход (из 32 разрядного представления знакового целого числа), всего 250 проходов по два символа за раз

                       }

                   }

                   PB_MAIN.Image = Image.FromStream(MS); // Восстанавливаем рисунок (создаём рисунок из потока байтов)

               }

           }

       }

   }

 

Все данные для одного рисунка по номеру вытаскиваются этим кодом из файла базы данных в объект DataTable. Данные выводятся из файла через массив DataRow [] с непосредственным выбором по именам узлов. Из строки символов < pichash >...</ pichash > формируются байты рисунка и затем передаются элементу PictureBox.

 

Готово. Можно компилировать и проверять работоспособность.

 

Завершающая часть

 

Компилируем приложение (Release) и запускаем. Результат работы показан ниже (Рис. 6. 1):

Рис. 6. 1. Модифицированное приложение Windows Forms : результат работы приложения по сохранению рисунка в базе данных (сохранение выбранного рисунка в базе)

Рис. 6. 2. Модифицированное приложение Windows Forms : результат работы приложения по сохранению рисунка в базе данных (выбор рисунка из базы по номеру)

 

Содержание базы данных (файл Wallpapper - DB . xml):

 

Рис. 6. 3. Содержимое файла Wallpappaer - DB . xml: четыре рисунка, длинные строчки < pichash >...</ pichash > содержат символы рисунков (окончания строк не видны)

 

Недостаток хранения данных в XML-файлах: длина строки, хотя это не значит, что при хранении в таблицах баз данных мы выиграем в объёме или скорости обработки (скорее наоборот проиграем за счёт сетевого трафика).

 

Если имена рисунков4 будут на русском (символы кириллицы), они будут корректно сохранены в базе.

 

ПРИМЕЧАНИЕ № 4: Точно таким образом можно сохранять и восстанавливать любые данные (Web-страницы, Word или Excel-документы и прочее). Подобный способ хранения может быть полезен, для решения многих задач связанных с хранением и передачей информации, в том числе и конфиденциальной (например, после преобразования файла всю строку можно закодировать паролем через MD5-хэш).

 

Если по каким-то причинам русские символы не читаются, сделаем следующее:

 

Найдём:

 

       using (StreamReader SR = new StreamReader(String_Path + @"\Wallpapper-DB.xml", System.Text.Encoding.UTF8))

 

И заменим:

 

       using (StreamReader SR = new StreamReader(String_Path + @"\Wallpapper-DB.xml", System.Text.Encoding.Default))

 

Найдём:

 

       // Сохраняем данные

       WallpapperDataSet.WriteXml(String_Path + @"\Wallpapper-DB.xml", XmlWriteMode.WriteSchema);

       WallpapperDataSet = new DataSet();

       // Вновь загружаем сохраненные данные

       WallpapperDataSet.ReadXml(String_Path + @"\Wallpapper-DB.xml", XmlReadMode.Auto);

       WallpapperDataTable = WallpapperDataSet.Tables[0];

 

Заменим:

 

       XML = new XmlDocument();

       XML.InnerXml = WallpapperDataSet.GetXml();

       XmlDeclaration XMLDeclaration = XML.CreateXmlDeclaration("1.0", "windows-1251", "yes");

       XML.InsertBefore(XMLDeclaration, XML.DocumentElement);

       XML.Save(String_Path + @"\Wallpapper-DB.xml");

       WallpapperDataSet = new DataSet();

       WallpapperDataSet.ReadXml(String_Path + @"\Wallpapper-DB.xml", XmlReadMode.Auto);

       WallpapperDataTable = WallpapperDataSet.Tables[0];

 


Дата добавления: 2019-09-13; просмотров: 164; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!