Показаны сообщения с ярлыком c#. Показать все сообщения
Показаны сообщения с ярлыком c#. Показать все сообщения

среда, 16 февраля 2011 г.

Использование PDF шаблонов C#.

Исходники к статье

Продолжая тему шаблонов, хочу раскрыть использование PDF вместе с закрытым исходным кодом. В сети можно найти достаточное количество статей, которые описывают данный процесс, но в основном они используют GNU General Public License библиотеки, а это ведет к открытию своего кода. Проект менеджер услышав об этом, вновь может посылать вас в лес, искать бесплатные отвертки…

Хочу обратить ваше внимание на проект PdfSharp и его лицензию, разрешающую использование в коммерческих целях.

Как использовать шаблоны? Немного теории:

Стандартный подход прост – графически рисуется требуемая форма, расставляются закладки или некие ключевые слова (наподобие @Name@), которые после отыскиваются в документе и заменяются на необходимые значения. Но, к сожалению, PDFsharp не поддерживает парсинг документа, этот подход потерпит фиаско.

Как же быть? Есть два выхода из положения, каждый из них требует больших трудозатрат, чем использование GPL библиотек (можете поискать реализации на iText), первый это полностью генерировать бланки без использования шаблонов, но при сложном бланке на это дело можно потратить чуть ли не целый день на бланк. И второй способ предлагаемый нам PDFsharp, это использовать шаблон как холст для рисования. Представьте, что у нас на столе, под стеклом лежит бланк документа и есть карандаш или маркер, которым мы рисуем на стекле, если стекло достаточно тонкое, то человек, взглянувший на такое "художество", будет считать его единым целым. Далее, мы как раз и будем рисовать на шаблоне, с помощью System.Drawing, подставляя нужные нам данные в требуемые места.

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

Не обладая большими талантами дизайнера, я приготовил такой бланк, и самолично его утвердил. :)

c# pdf template

Создадим новое консольное приложение с именем UsingPdfTemplate, далее добавим к проекту ссылки на сборки PdfSharp.dll из каталога GDI+ (заранее скачанные с сайта проекта, на момент написания статьи версия 1_31), а так же на System.Drawing. Добавим к проекту файл template.pdf (взять можно из исходников к статье) и установим его свойство Build Action – Embedded Resource и Copy to Output Directory – Copy always.

Далее введем следующий код:



using System;
using System.Diagnostics;
using PdfSharp.Drawing;
using PdfSharp.Pdf;
using PdfSharp.Pdf.IO;

namespace UsingPdfTemplate
{
class Program
{
static void Main(string[] args)
{
PdfDocument inputDocument = PdfReader.Open("template.pdf", PdfDocumentOpenMode.Import);
PdfPage notEditablePage = inputDocument.Pages[0];

PdfDocument outputDocument = new PdfDocument();
PdfPage editablePage = outputDocument.AddPage(notEditablePage);

outputDocument.Save("newPdf.pdf");
Process.Start("newPdf.pdf");
}
}
}



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

Теперь заполним поля «Должность», «Имя», «Email», добавим фотографию и таблицу посещений:




PdfPage editablePage = outputDocument.AddPage(notEditablePage);

XGraphics gfx = XGraphics.FromPdfPage(editablePage);
DrawFields(gfx);
DrawImage(gfx);
DrawTable(gfx);


outputDocument.Save("newPdf.pdf");




Получаем объект XGraphics и передаем его в методы DrawFields(), DrawImage(), DrawTable().

DrawFields отрисует поля на шаблоне, код метода:



private static void DrawFields(XGraphics gfx)
{
XPdfFontOptions fontOptions = new XPdfFontOptions(PdfFontEncoding.Unicode, PdfFontEmbedding.Always);
XFont font = new XFont("Times New Roman", 10, XFontStyle.Bold, fontOptions);

gfx.DrawString("Программист", font, XBrushes.Black, 100, 135);
gfx.DrawString("Александр Кобелев", font, XBrushes.Black, 100, 160);
gfx.DrawString("Kobelev.Alexander@gmail.com", font, XBrushes.Black, 100, 185);
}



Выставляем поддержку юникода и отрисовываем три строчки текста в вымеренных позициях.

Для отрисовки фотографии добавим в проект фото (можно так же взять из исходников фото моего кота), и пометим его свойство Build Action – Embedded Resource и Copy to Output Directory – Copy always.

DrawImage отрисует фотографию, код метода:



private static void DrawImage(XGraphics gfx)
{
XImage img = XImage.FromFile("cat.jpg");
gfx.DrawImage(img, 350, 125, 120, 160);
}



Очень простой метод - мы лишь получаем изображение из файла (XImage можно так же получить, используя System.Drawing.Image, который можно получить из потока) и отрисовываем его на документе по координатам с заданными размерами.

С таблицей будет посложнее, дело в том что PdfSharp ничего не знает о таблицах и способен их нарисовать только из линий, но не стоит падать духом, так как есть замечательная библиотека MigraDoc, которая поставляется вместе с PdfSharp. Нам лишь надо добавить ссылку на библиотеку MigraDoc.DocumentObjectModel.dll и MigraDoc.Rendering.dll.

DrawTable отрисует таблицу, код метода:



...
using MigraDoc.DocumentObjectModel;
using MigraDoc.DocumentObjectModel.Tables;
using MigraDoc.Rendering;

private static void DrawTable(XGraphics gfx)
{

Document doc = new Document();

Table table = new Table();
table.Borders.Width = 0.3;
table.AddColumn(76);
table.AddColumn(78.2);
table.AddColumn(77.6);

for (int day = 7; day > 0; day--)
{
var row = table.AddRow();
row[0].AddParagraph(DateTime.Now.AddDays(-day).ToString("dd/MM/yyyy"));
row[1].AddParagraph(DateTime.Now.AddDays(-day).ToString("HH:mm"));
row[2].AddParagraph(DateTime.Now.AddDays(-day).AddHours(8).ToString("HH:mm"));
}

doc.AddSection().Add(table);

DocumentRenderer docRenderer = new DocumentRenderer(doc);
docRenderer.PrepareDocument();
docRenderer.RenderObject(gfx, 36, 257, 0, table);
}



В этом методе мы вначале создаем MigraDoc.DocumentObjectModel.Document, потом таблицу, выставляем у таблицы ширину бордюра и размеры колонок. После чего заполняем семь строк данными и добавляем таблицу в документ, после создаем объект класса MigraDoc.Rendering.DocumentRenderer, который принимает в качестве параметра конструктора документ содержащий таблицу, подготавливаем и отрисовываем его, используя объект XGraphics переданный в метод.

Как результат у нас появляется заполненный PDF документ на основе шаблона:

c# completed pdf template

Александр Кобелев aka Megano

вторник, 22 июня 2010 г.

Использование WORD шаблонов.


Внимание! Обратите внимание на обновленную версию статьи "Использование WORD шаблонов C# 4.0 "



Инструментарий:
• Microsoft Visual Studio 2010
• Microsoft Office 2010 Professional Beta

исходники к статье


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

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

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

Начнем с простого шаблона, который будет представлять из себя фирменный бланк компании и единственное поле для вставки данных. Создадим новый WORD документ и уменьшим отступы (Page Layout -> Margins -> Narrow).

Margins


После этого развернем документ в альбомную ориентацию (Page Layout -> Orientation -> Landscape).

Orientation

Далее создадим шапку (Insert -> Header –> Alpabet).

Footer

Напишем название. Далее впишем дополнительную информацию о компании и пометим ее курсивом с выравниванием по правому краю.

После создания шапки в документ автоматически был добавлен пустой подвал, перейти в редактирование которого можно просто перенеся туда курсор. Визуально отделим его от тела документа при помощи разделительной линии. (Insert –> Shapes -> Lines)

Line

Далее, в подвале сделаем выравнивание по правому краю и создадим закладку (Bookmark), которую будем отыскивать в коде и на ее место вставлять данные (Insert -> Bookmark).

InsertBookmark

И назовем ее AuthorName.

На этом завершим создание фирменного бланка, сохраним шаблон с именем screwdriver.dotx (Word Template)

Следующим шагом создадим из кода новый документ Word, на основе нашего шаблона, впишем автора документа и сохраним его на диск.

Создадим в Visual Studio WPF приложение с названием UsingWordTemplate ( File -> New -> Project -> Windows -> WPF Application).

Добавим на форму Label (Content="Автор:"), TextBox (Name=”autorTxtb”), в который будем вписывать имя автора документа и кнопку (Content="Сохранить" Name=”saveBtn”), с помощью которой будем сохранять созданный документ.

Добавим ссылку на .Net библиотеку Microsoft.Office.Interop.Word Version 14. В шапке кода главного окна добавим


using Word = Microsoft.Office.Interop.Word;


После чего определим несколько переменных


Word._Application oWord = new Word.Application();
object oMissing = System.Reflection.Missing.Value;


Переменная oWord будет представлять процесс WINWORD.EXE в памяти, oMissing представляет из себя аргумент представляющий значение по умолчанию, который требуют методы из пространства Microsoft.Office.Interop, так как они не принимают Null.

Так же определим событие формы Closing, в котором мы будем закрывать процесс WINWORD.EXE


private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
oWord.Quit(ref oMissing, ref oMissing, ref oMissing);
}


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

Вызовем контекстное меню текущего проекта и добавим в него заранее созданный шаблон screwdriver.dotx (Add -> Existing Item -> screwdriver.dotx), в его свойствах определим Copy To Output Directory как Copy always.

Определим обработчик кнопки:


private void saveBtn_Click(object sender, RoutedEventArgs e)
{
Word._Document oDoc = LoadTemplate(Environment.CurrentDirectory + "\\screwdriver.dotx");
SetTemplate(oDoc);
SaveToDisk(oDoc, Environment.CurrentDirectory + "\\New.docx");
oDoc.Close(ref oMissing, ref oMissing, ref oMissing);
}


Здесь все сводится к вызову трех методов LoadTemplate(),SetTemplate(), SaveToDisk(). Загружаем сохраненный шаблон, заполняем его данными с формы и сохраняем на диск, после чего закрываем документ.


Примечание:

Остерегайтесь использования Environment.CurrentDirectory так как значение может измениться в процессе работы.


static void Main(string[] args)
{
Console.WriteLine(Environment.CurrentDirectory);
Environment.CurrentDirectory = "c:\\";
Console.WriteLine(Environment.CurrentDirectory);
}


Как показано выше мы можем сами изменить ее значение, в данном случае Environment.CurrentDirectory больше указывает не на каталог приложения, а на корень диска C.


Ниже приведен код вызванных методов:


private Word._Document LoadTemplate(string filePath)
{
object oTemplate = filePath;
Word._Document oDoc = oWord.Documents.Add(ref oTemplate, ref oMissing, ref oMissing, ref oMissing);
return oDoc;
}

private void SetTemplate(Word._Document oDoc)
{
object oBookMark = "AuthorName";
oDoc.Bookmarks.get_Item(ref oBookMark).Range.Text = autorTxtb.Text;
}

private void SaveToDisk(Word._Document oDoc, string filePath)
{
object fileName = filePath;
oDoc.SaveAs(ref fileName, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing);
}


Сигнатуры вызываемых методов из пространства имен Microsoft.Office.Interop можно посмотреть по адресу:

http://msdn.microsoft.com/ru-ru/library/microsoft.office.interop.word%28en-us%29.aspx

Введем в текст бокс данные автора документа “Александр Кобелев” и сохраним документ на диск нажав кнопку “Сохранить”. В папке Debug нашего приложения появится файл New.docx, содержащий введенные данные на месте bookmark AuthorName.

Теперь добавим еще одну кнопку (Content="Print" Name="prntBtn") на форму, которая будет распечатывать документ на принтер и определим ее обработчик.


private void prntBtn_Click(object sender, RoutedEventArgs e)
{
Word._Document oDoc = LoadTemplate(Environment.CurrentDirectory + "\\screwdriver.dotx");
SetTemplate(oDoc);
PrintDoc(oDoc);
object notSave = Word.WdSaveOptions.wdDoNotSaveChanges;
oDoc.Close(ref notSave, ref oMissing, ref oMissing);
}


Здесь все то же самое, что и в предыдущем случае, только вместо метода SaveToDisk вызывается метод PrintDoc и перед закрытием документа явно определяется, что документ не стоит сохранять. В предыдущем случае, при сохранении документа на диск, он помечался как сохраненный и не требовалось явно указывать этот параметр


private void PrintDoc(Word._Document oDoc)
{
oDoc.PrintOut(ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing
, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing
, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing
, ref oMissing, ref oMissing, ref oMissing);
}


Метод PrintDoc просто вызывает метод PrintOut с параметрами по умолчанию.

Теперь добавим на шаблон таблицу 3 на 4, которая будет отображать продажи по городам. Верхняя строчка обозначит ключи и отвертки, а первый столбик города: Москва, Санкт-Петербург, Челябинск. Разукрасим таблицу по своему вкусу.

table

Далее мы могли бы поставить шесть закладок (boomark), задать им уникальные имена и искать их как в предыдущем случае, но это слишком накладно. Можно поступить проще – выделить всю таблицу и добавить закладку на все выделение сразу.

selectTable

Назовем новую закладку “SaleTable”.

Добавим на форму DataGrid, со свойством AutoGenerateColumns="True", а в код новую переменную

В конструкторе вызовем метод, который заполнит DataTable и свяжет его с DataGrid


public MainWindow()
{
InitializeComponent();
InitializeDataGrid();
}

private void InitializeDataGrid()
{
table = new DataTable();
table.Columns.Add();
table.Columns.Add();
table.Columns.Add();
var row = table.NewRow();
row[0] = "Город";
row[1] = "Ключи";
row[2] = "Отвертки";
table.Rows.Add(row);
table.Rows.Add(table.NewRow()[0] = "Москва");
table.Rows.Add(table.NewRow()[0] = "Санкт-Петербург");
table.Rows.Add(table.NewRow()[0] = "Челябинск");
dataGrid1.DataContext = table;
}


Добавим в метод SetTemplate() вызов метода SetTable() который будет заполнять данными таблицу.


private void SetTemplate(Word._Document oDoc)
{
. . .
SetTable(oDoc,"SaleTable",table);
}

private void SetTable(Word._Document oDoc,string bookmark, DataTable dataContext)
{
object oTableBookMark = bookmark;
var tbl = oDoc.Bookmarks.get_Item(ref oTableBookMark).Range.Tables[1];
int tblRow = 0;
int tblCell = 0;
foreach (Word.Column col in tbl.Columns)
{
foreach (Word.Cell cell in col.Cells)
{
cell.Range.Text = (string) dataContext.Rows[tblRow][tblCell];
tblRow++;
}
tblCell++;
tblRow = 0;
}
}


Как видно из листинга, мы перебираем по очереди все ячейки в шаблоне и сопоставляем их с данными из DataTable.

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

Для реализации немного изменим метод SetTable() , заменив явное присваивание на вызов метода SetCell().


...
foreach (Word.Cell cell in col.Cells)
{
SetCell(cell, (string)dataContext.Rows[tblRow][tblCell]);
tblRow++;
}
...

private void SetCell(Word.Cell cell, string text)
{
int val = 0;
if (int.TryParse(text, out val))
{
if (val < 100) cell.Shading.BackgroundPatternColor = Word.WdColor.wdColorRose;
if (val > 500) cell.Shading.BackgroundPatternColor = Word.WdColor.wdColorLightGreen;
}
cell.Range.Text = text;
}


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

Перейдем к графикам.

Для использования графиков потребуется ссылка на .Net библиотеку Microsoft.Office.Interop.Excel Version 14. Так же в шапке кода главного окна добавим using


using Excel = Microsoft.Office.Interop.Excel;


Сделаем несколько отступов вниз, для создания пространства между таблицей и графиком, добавим в шаблон график (Insert -> Chart -> Column -> 3d Column)

insertChart

3dColumn

и выставим все значения по нулям.

chartDefValue

После чего выделим график на шаблоне (кликнув по краю графика) и создадим новую закладку “ChartBookmark”, далее сохраним изменения в шаблоне.

Добавим в метод SetTemplate() вызов метода SetChart(), который будет заполнять данными график.


private void SetTemplate(Word._Document oDoc)
{
...
SetChart(oDoc, "ChartBookmark", table);
}

private void SetChart(Word._Document oDoc, string bookmark, DataTable dataContext)
{
object oChartBookMark = bookmark;
Word.Chart chart = oDoc.Bookmarks.get_Item(ref ChartBookMark).Range.InlineShapes[1].Chart;
Word.ChartData chartData = chart.ChartData;
chartData.Activate();

Excel.Workbook dataWorkbook = (Excel.Workbook)chartData.Workbook;
Excel.Worksheet dataSheet = (Excel.Worksheet)dataWorkbook.Worksheets[1];
dataSheet.Cells.get_Range("B2", oMissing).FormulaR1C1 = (string)dataContext.Rows[1][1];
dataSheet.Cells.get_Range("B3", oMissing).FormulaR1C1 = (string)dataContext.Rows[1][2];
dataSheet.Cells.get_Range("C2", oMissing).FormulaR1C1 = (string)dataContext.Rows[2][1];
dataSheet.Cells.get_Range("C3", oMissing).FormulaR1C1 = (string)dataContext.Rows[2][2];
dataSheet.Cells.get_Range("D2", oMissing).FormulaR1C1 = (string)dataContext.Rows[3][1];
dataSheet.Cells.get_Range("D3", oMissing).FormulaR1C1 = (string)dataContext.Rows[3][2];
dataWorkbook.Close(oMissing, oMissing, oMissing);
}


Теперь приложение полностью готово к работе, введем некоторые данные, чтобы провести функциональный тест. В строке автор я введу “Александр Кобелев” а значения продаж по городам у меня будут такие: Ключи/Отвертки Москва – 700/400, Санкт-Петербург – 50/300, Челябинск – 100/200. После нажатия кнопки “Сохранить” в папке Debug приложения появился файл New.docx, такого вида:

end

Александр Кобелев aka Megano

понедельник, 21 июня 2010 г.

Архивирование, разархивирование потоков, файлов и папок с использованием SharpZipLib .net & .net compact

Инструментарий: Visual Studio 2008 PRO, C#, SharpZipLib 0.85.5.0 и классы из пространства имен SharpZipLib.Zip

Содержание статьи:
1. работа с потоками
2. Работа с потоками, файлы и папки
3. Проще простого или FastZip class
4. Адаптируем под .net compact
5. Заключение

Исходники к статье

Данная статья поможет разобраться с API SharpZipLib, но раскрывает его только поверхностно и использует классы из пространства имен ICSharpCode.SharpZipLib.Zip. Более полные данные вы можете найти в документации на официальном сайте.

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

Создадим новое консольное приложение с названием SharpZipLibAndStream. Добавим ссылку на библиотеку SharpZipLib и добавим в код:


using ICSharpCode.SharpZipLib.Zip;
using System.IO;


Теперь создадим эталонный поток, с которым в последствии сверим сжатый поток.


MemoryStream memoryStream = new MemoryStream();


Заполним его данными с помощью метода FillStream


FillStream(memoryStream,10);


Код метода FillStream:


private static void FillStream(Stream stream, int times)
{
string temp = "The quick brown fox jumps over the lazy dog";
string data = string.Empty;
for (int i = 0; i < times; i++) data += temp;
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(data);
stream.Write(buffer, 0, buffer.Length);
}


Как видно, из кода в поток помещается десять копий одной строки


Console.WriteLine(string.Format("Изначальная длинна потока: {0} bytes", memoryStream.Length));


Создадим экземпляр класса ZipOutputStream, в своем конструкторе он принимает единственный аргумент – поток, в который он будет сжимать данные.


MemoryStream zippedMemoryStream = new MemoryStream();
ZipOutputStream zipStream = new ZipOutputStream(zippedMemoryStream);


Теперь нам необходимо ( в противном случае мы получим InvalidOperationException:No Open Entry, при попытке записи данных в поток) добавить запись в оглавление архива, представленное классом ZipEntry, один из конструкторов которого принимает строку, как имя.


ZipEntry zipEntry = new ZipEntry("myData");
zipStream.PutNextEntry(zipEntry);


Поток готов к заполнению данными, заполним его аналогично memoryStream.


FillStream(zipStream,10);


После чего закроем ZipEntry


zipStream.CloseEntry();
Console.WriteLine(string.Format("Длинна потока в архиве: {0} bytes", zippedMemoryStream.Length));


Теперь можно сравнить длинну потоков – memoryStream 430 bytes и zippedMemoryStream 104.
Если мы изменим значение times в FillStream на 0, то получим значения 0 и 56,
56 bytes – столько занимает текущая запись в заголовке архива. Далее поставим значение 1 - выходные данные изменились на 43 и 100, 100 – 56 = 44, чуть больше самой строки (строка “The quick brown fox jumps over the lazy dog” включает в себя все буквы латиницы и довольно сложна для сжатия), что показывает малую эффективность при работе с данными небольшого размера. Изменим значение на 100 и получим 4300 и 125 как видно из результатов, ZipOutputStream прекрасно справляется с дублированием.

Разархивируем сжатый поток zippedMemoryStream.

Для извлечения данных из заархивированного потока используется класс ZipInputStream, конструктор которого принимает заархивированный поток.


ZipInputStream zipInputStream = new ZipInputStream(zippedMemoryStream);


Так как мы уже работали с zippedMemoryStream, переставим курсор в начало.


zippedMemoryStream.Position = 0;


После этого создадим в памяти новый поток для извлечения результатов.


MemoryStream fromZippedMemoryStream = new MemoryStream();


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


ZipEntry entry = zipInputStream.GetNextEntry();
byte[] outputBuffer = new byte[zipInputStream.Length];
zipInputStream.Read(outputBuffer, 0, outputBuffer.Length);
fromZippedMemoryStream.Write(outputBuffer, 0, outputBuffer.Length);
Console.WriteLine(string.Format("Длинна разархивированного потока: {0} bytes", fromZippedMemoryStream.Length));



Работа с потоками, файлы и папки.


В данной части мы заархивируем два текстовых файла с помощью ZipOutputStream, один из которых будет лежать в корне архива, а другой будет находиться внутри нескольких вложенных папок. После чего произведем распаковку архива на диск с помощью ZipInputStream.
Первоначально добавим в наш solution новое консольное приложение, с именем SharpZipLibStreamFilesAndFolders и сделаем его активным. Добавим ссылку на библиотеку SharpZipLib и добавим в код:


using ICSharpCode.SharpZipLib.Zip;
using System.IO;


Далее вызовем метод


CreateFilesAndDirectiries();


Код метода:


private static void CreateFilesAndDirectiries()
{
List files = new List();
files.Add(File.Create("file1.txt"));
files.Add(File.Create("file2.txt"));
files.ForEach(FillHelloWorldInStream);
files.ForEach(fStream => fStream.Close());
}

private static void FillHelloWorldInStream(FileStream file)
{
byte[] buffer = Encoding.UTF8.GetBytes("Hello World");
file.Write(buffer, 0, buffer.Length);
}


Как видно из кода – мы создаем в папке приложения два файла с именами file1.txt и file2.txt, потом записываем в каждый из файлов строку “Hello World” и закрываем потоки.


Займемся архивированием.


Далее мы создадим в папке с приложением новый файл my.zip, и передадим его FileStream поток в конструктор класса ZipOutputStream.


ZipOutputStream zipOutStream = new ZipOutputStream(File.Create("my.zip"));


После чего вызовем метод CreateFileZipEntry.


CreateFileZipEntry(zipOutStream, "file1.txt", "file1.txt");


Код метода:


private static void CreateFileZipEntry(ZipOutputStream zipOutStream, string name, string filePath)
{
ZipEntry fileZipEntry = new ZipEntry(name);
zipOutStream.PutNextEntry(fileZipEntry);
FileStream fileStram = File.Open(filePath, FileMode.Open);
byte[] buffer = new byte[fileStram.Length];
fileStram.Read(buffer, 0, buffer.Length);
zipOutStream.Write(buffer, 0, buffer.Length);
zipOutStream.CloseEntry();
}


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


Первоначально метод создает новую запись ZipEntry с переданным в метод именем (имя влияет на название файла внутри архива и на его местонахождение), после чего добавляет ее к оглавлению архива. Далее извлекается FileStream из файла, имя которого так же было передано в метод, читаются все его данные и записываются в ZipOutputStream, который архивирует их. После чего закрывается текущая ZipEntry.

Далее повторим вызов метода для второго файла, но изменим имя.


CreateFileZipEntry(zipOutStream, @"folder1\folder2\folder3\file2.txt", "file2.txt");


В результате этого вызова в архиве будет создана иерархия каталогов, в последнем из которых будет находиться file2.txt.

Все необходимые данные переданы в архив, после чего вызываем


zipOutStream.Close();



Теперь разархивируем только что созданный нами архив.


Создадим директорию в папке приложения, куда разархивируем данные.


Directory.CreateDirectory("ZipOutPut");


Создадим экземпляр класса ZipInputStream и передадим в его конструктор экземпляр FileStream, нашего архива.


ZipInputStream zipInputStream = new ZipInputStream(File.Open("my.zip", FileMode.Open));


Получим первую запись из заголовка архива.


ZipEntry zipEntryFromZippedFile = zipInputStream.GetNextEntry();


Разархивируем все в директорию ZipOutPut


while (zipEntryFromZippedFile != null)
{
if (zipEntryFromZippedFile.IsFile)
{
FileInfo fInfo = new FileInfo(string.Format("ZipOutPut\\{0}", zipEntryFromZippedFile.Name));
if (!fInfo.Directory.Exists) fInfo.Directory.Create();

FileStream file = fInfo.Create();
byte[] bufferFromZip = new byte[zipInputStream.Length];
zipInputStream.Read(bufferFromZip, 0, bufferFromZip.Length);
file.Write(bufferFromZip, 0, bufferFromZip.Length);
file.Close();
}
zipEntryFromZippedFile = zipInputStream.GetNextEntry();
}
zipInputStream.Close();


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

Проще простого или FastZip class

Если необходимо быстро создать архив или разархивировать архивный файл, то самый простой и быстрый способ использовать FastZip class.

Добавим в наш solution новое консольное приложение, с именем SharpZipLibFastZip, и сделаем его активным. Добавим ссылку на библиотеку SharpZipLib и добавим в код:


using ICSharpCode.SharpZipLib.Zip;
using System.IO;


Теперь вызовем метод CreateFilesAndDirectiries(), рассмотренный нами ранее, но с небольшими изменениями.


private static void CreateFilesAndDirectiries()
{
Directory.CreateDirectory("ToZip");
List files = new List();
files.Add(File.Create("ToZip\\file1.txt"));
files.Add(File.Create("ToZip\\file2.txt"));
files.ForEach(FillHelloWorldInStream);
files.ForEach(fStream => fStream.Close());
}


В папке приложения создается директория с названием “ToZip” и уже в ней будут находиться два текстовых файла.

Далее все гораздо проще, чем в предыдущих разделах.


FastZip zip = new FastZip();
zip.CreateZip("my.zip","ToZip", true,null);


Вот и все ;)

В папке приложения будет создан архив с именем my.zip и будет содержать в себе файлы из каталога ToZip.

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

С разархивированием все так же просто:


zip.ExtractZip("my.zip", "New", null);


первый параметр – файл архива, второй – каталог, куда будет разархвивирован архив(даже если его не существует, как в данном случае, FatZip создаст его), третий – fileFilter.


Адаптируем под .net compact


Сделаем адаптацию на первом примере. Создадим новый compact проект Device Application, добавим ссылку на SharpZipLib библиотеку (из папки netcf-20) и заменим автоматически сгенерированный код на:


using System;
using System.Text;
using System.Windows.Forms;
using System.IO;
using ICSharpCode.SharpZipLib.Zip;

namespace CompactZipStreams
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e)
{
MemoryStream memoryStream = new MemoryStream();
FillStream(memoryStream, 100);

MemoryStream zippedMemoryStream = new MemoryStream();
ZipOutputStream zipStream = new ZipOutputStream(zippedMemoryStream);
ZipEntry zipEntry = new ZipEntry("myData");
zipStream.PutNextEntry(zipEntry);
FillStream(zipStream, 100);
zipStream.CloseEntry();

ZipInputStream zipInputStream = new ZipInputStream(zippedMemoryStream);
zippedMemoryStream.Position = 0;
MemoryStream fromZippedMemoryStream = new MemoryStream();
ZipEntry entry = zipInputStream.GetNextEntry();
byte[] outputBuffer = new byte[zipInputStream.Length];
zipInputStream.Read(outputBuffer, 0, outputBuffer.Length);
fromZippedMemoryStream.Write(outputBuffer, 0, outputBuffer.Length);

MessageBox.Show(string.Format("Изначальная длинна: {0} bytes \n В архиве: {1} bytes \n Разархивированно в: {2} ", memoryStream.Length,zippedMemoryStream.Length,fromZippedMemoryStream.Length));


}
private static void FillStream(Stream stream, int times)
{
string temp = "The quick brown fox jumps over the lazy dog";
string data = string.Empty;
for (int i = 0; i < times; i++) data += temp;
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(data);
stream.Write(buffer, 0, buffer.Length);
}
}
}


Как можно заметить: изменилась только ссылка на библиотеку и способ вывода.
Аналогично и со всеми другими примерами, код которых можно найти в исходниках к данной статье.


Заключение

В данной статье была поверхностно рассмотрена работа с классами ZipOutputStream, ZipInputStream, ZipEntry и FastZip из популярной библиотеки SharpZipLib. Для более полного ознакомления используйте официальную документацию, вы сможете увеличить или уменьшить степень сжатия архива, задать пароль, добавить комментарии к архиву, тестировать и изменять существующие архивы. К тому же данная библиотека позволяет работать не только с zip архивами.

Александр Кобелев aka Megano

воскресенье, 20 июня 2010 г.

Подключение к Access

Редакция статьи от 26.04.2013

В своей работе я буду использовать Visual Studio 2008 Pro & C#

Довольно часто на форуме всплывает данный вопрос, по сему решил обобщить разрозненную информацию в данном сообщении.

Я покажу в трех частях как можно подключиться и взаимодействовать с файлом базы данных Access.

Часть 1 - Где с помощью визуальных средств добавляем к нашему проекту копию файла данных.

Часть 2 - Где с помощью визуальных средств подключаем существующий файл без создания копии в проекте.

Часть 3 - Чисто код.

Первоначально понадобится таблица с данными в файле Access.
Имя файла db1.mdb, таблица People c данными:



Код_People — Счетчик, Имя — Текст, ДатаРождения — Дата/Время

Скачать Access базу:
https://skydrive.live.com/?cid=3A4FDD8B3B890BE4&id=3A4FDD8B3B890BE4%21315
зеркало:
https://drive.google.com/folderview?id=0B1sG6jmDKzlvT2JQY0prWGNNelE

Часть 1
Где с помощью визуальных средств добавляем к нашему проекту копию файла данных.

Создаем новый Windows Forms проект.
File → New → Project → Windows → Windows Forms Application и называем его VisualAccessConnect

правой кнопкой мыши по проекту → Add → Existing Item... → В типах файлов выбираем Data Fiels → после чего находим наш db1.mdb и жмем ОК.

Автоматически открывается Data Source Configuration Wizard → Отмечаем, что мы хотим выбрать все таблицы.



Жмем Finish.

После чего у нас появляется в проекте строго типизированный DataSet содержащий в себе DataTable People и PeopleTableAdapter



Теперь добавляем на Form1 DataGridView

И в его свойстве DataSource выбираем нашу таблицу People



Все готово к отображению данных и при нажатии ctrl + F5 вы увидите такую картину:



Если у вас вывалилось исключение - the 'microsoft.jet.oledb.4.0' provider is not registered on the local machine
Вам необходимо зайти в настройки проекта и выставить Platform Target x86



Добавим кнопку, которая нам поможет внести изменения в базу.
Перетащим на Form1 Button, сделаем двойной клик мышью, на ней, и впечатаем следующий код в обработчике события

this.peopleTableAdapter.Update(this.db1DataSet.People);


Теперь можно запускать наш проект, вносить изменения в данные и сохранять их.

Некоторые пояснения:

Первоначально, когда мы добавляем файл данных в проект - добавляется его копия, а оригинальный файл остается на своем месте, в неизменном состоянии.

То же самое происходит когда мы нажимаем ctrl + F5 в папку debug копируется копия уже скопированного в наш проект файла данных и если вы сделаете Rebuild проекта, то все изменения откатятся к первоначальному состоянию(так как файл в папку debug будет скопирован вновь из нашего проекта), но в релизной версии все будет работать превосходно.

Если у вас возникли проблемы по первой части, можете посмотреть видео, которое проходит все описанные выше шаги с применением Visual Studio 2010:



Часть 2
Где с помощью визуальных средств подключаем существующий файл без создания копии в проекте.

Создаем новый Windows Forms проект.
File → New → Project → Windows → Windows Forms Application и называем его VisualAccessConnectViaFilePath

Нажимаем View → Server Explorer или ctrl + W + L
Вызываем контекстное меню у Data Connections → Add Connection



В открывшемся окне нажимаем кнопку Change напротив DataSource

ChangeDataSource

И выбираем Microsoft Access Database File

MsAccesDbFile

После чего выбираем путь к нашему файлу и проверяем соединение

TestConnection

Теперь добавляем на форму DataGridView, в его свойстве DataSource выбираем Add Project Data Source

AddPrDataSource

Data Base → И в выпадающем меню выбираем строку подключения к нашему файлу, которую мы создали ранее, после чего появится предупреждение что файл базы находится вне приложения с предложением сделать копию, выбираем NO

alertNO

После чего в открывшемся мастере выбираем таблицу People ( см часть1) и ОК
Так же добавляем кнопку для сохранения изменений, аналогично как и в первой части.

Теперь наше приложение отображает данные с текущего файла данных и изменяет его при сохранении.

Будьте внимательны при развертывании приложения, так как ответственность за путь к файлу данных лежит на ваших плечах.

Если у вас возникли проблемы по второй части, можете посмотреть видео, которое проходит все описанные выше шаги с применением Visual Studio 2010:



Часть 3
Чисто код.

Создадим консольное приложение.
File → New → Project → Windows → Console Application и назовем его AccessConnectPureCode

И полностью заменяем весь автоматически сгенерированный код на

using System;
using System.Data;
using System.Data.OleDb;

class AccessConnectPureCode
{
    public static void Main()
    {
        string connectionString = @"provider=Microsoft.Jet.OLEDB.4.0; data source=C:\Data\db1.mdb";
        OleDbConnection myOleDbConnection = new OleDbConnection(connectionString);
        OleDbCommand myOleDbCommand = myOleDbConnection.CreateCommand();
        myOleDbCommand.CommandText = "SELECT * FROM People";
        myOleDbConnection.Open();
        
        // Считываем данные
        OleDbDataReader myOleDbDataReader = myOleDbCommand.ExecuteReader();
        while (myOleDbDataReader.Read())
        {
            Console.WriteLine(string.Format("Id: {0}, Name: {1}, Birthday: {2}",
                myOleDbDataReader["Код_People"],
                myOleDbDataReader["Имя"],
                myOleDbDataReader["ДатаРождения"]));
        }
        myOleDbDataReader.Close();

        // Вносим изменения
        OleDbCommand changeDashaName = myOleDbConnection.CreateCommand();
        changeDashaName.CommandText = @"UPDATE People SET Имя = 'Даша' WHERE Имя = 'Dasha'";
        changeDashaName.ExecuteNonQuery();

        myOleDbConnection.Close();
    }
}


Нужно проследить, чтоб строка подключения указывала на существующий файл.

Александр Кобелев aka Megano.