Используем Crystal Report в C#

В этой статье я приведу пример создания и использования отчёта Crystal Reports в Visual Studio.

Приведённый пример — это реально рабочий проект по подготовке к печати билетов на обычном лазерном принтере.

Начнём с того что нам потребуется источник данных поэтому вначале создадим проект в котором будем использовать наш отчёт и создадим источник данных который выгрузим в XML файл, для более простого и наглядного создания нового отчёта и его настройки.

1. Создаём обычный проект C# в Visual Studio, форму и добавляем 3 поля: префикс (на нашем билете будет штрихкод и номер, в штриховом коде будет использован префикс), начальный номер билета (с какого номера начать печать), конечный номер (каким закончить). Также добавляем кнопку, ао нажатию на которую будет происходить заполнение источника данных и вызов формы.

Вот как это выглядит:


2. Теперь по нажатию кнопки описываем процедуру создания XML файла с данными для отчёта.
private void button1_Click(object sender, EventArgs e)
        {
           XmlDocument doc = new XmlDocument();
            doc.AppendChild(doc.CreateXmlDeclaration("1.0","utf-8",null));

            XmlNode rootNode = doc.AppendChild(doc.CreateElement("ticket"));
            int j = int.Parse(textBox2.Text);
            int j1 = int.Parse(textBox3.Text);

            for (int i = j; i < j1+1; i++)
            {
                XmlNode bookNode = rootNode.AppendChild(doc.CreateElement("ticket"));

                XmlNode titleNode = bookNode.AppendChild(doc.CreateElement("bar"));
                long number = int.Parse(textBox1.Text);
                number = number * 10000;
                number = number+i;

                titleNode.InnerText = "*" + number.ToString() + "*";

                XmlNode titleNode1 = bookNode.AppendChild(doc.CreateElement("number"));
                string num = number.ToString();
                titleNode1.InnerText = num[2].ToString()+num[3].ToString()+num[4].ToString()+num[5].ToString();
                
            }
            doc.Save(@".\dataset.xml");
        }

(Код создаёт XML файл с номерами и щтриховыми кодами билетов, штриховые коды начинаются и заканчиваются символом "*" — поскольку будем использовать тип штрихкодов Code39.)

3. После того как наш XML файл сохранён приступаем к созданию непосредственно самого отчёта (отчётной формы).

Создаём новый проект в Visual Studio — Reporting -> Crystal Reports Application.
Появится проект отчёта, в который добавляем источник данных, в дереве слева:
Левой кнопкой по «Database Fields» затем Database Expert -> Database Files (выбираем созданный нашим проектом XML файл), затем выбираем поля, в нашем случае все.
Для размещения полей в отчёте просто перетаскиваем их на лист.
Вот что получилось у меня:

Чтобы сделать так, как на рисунке необходимо изменить параметры отчёта в Report->Section Expert. (там задаются основные параметры отчёта, от которых зависит какой именно вид он примет).

По завершении сохраняем наш отчёт и копируем из полученного проекта CrystalReport1.rpt в папку Debug предыдущего проекта.

4. Теперь возвращаемся к нашему проекту и пишем оставшуюся часть кода для вызова отчёта.

Прежде всего добавляем ещё одну форму (в нашем случае FormRW), и размещаем на ней элемент CrystalReportViewer — здесь мы будем отображать наш отчёт.

И дописываем процедуру нажатия кнопки:
private void button1_Click(object sender, EventArgs e)
        {
           XmlDocument doc = new XmlDocument();
            doc.AppendChild(doc.CreateXmlDeclaration("1.0","utf-8",null));

            XmlNode rootNode = doc.AppendChild(doc.CreateElement("ticket"));
            int j = int.Parse(textBox2.Text);
            int j1 = int.Parse(textBox3.Text);

            for (int i = j; i < j1+1; i++)
            {
                XmlNode bookNode = rootNode.AppendChild(doc.CreateElement("ticket"));

                XmlNode titleNode = bookNode.AppendChild(doc.CreateElement("bar"));
                long number = int.Parse(textBox1.Text);
                number = number * 10000;
                number = number+i;

                titleNode.InnerText = "*" + number.ToString() + "*";

                XmlNode titleNode1 = bookNode.AppendChild(doc.CreateElement("number"));
                string num = number.ToString();
                titleNode1.InnerText = num[2].ToString()+num[3].ToString()+num[4].ToString()+num[5].ToString();
                
            }
            doc.Save(@".\dataset.xml");
            //Создаём отчёт 
            ReportDocument myReport = new ReportDocument();
            //Задаём форму (которую создавали в пункте 3)
            myReport.Load(@".\CrystalReport1.rpt");
           
            DataSet myDS = new DataSet();
            //Указываем источнику данных вновь сформированный XML файл.
            myDS.ReadXml(@".\dataset.xml");
            //Указываем отчёту откуда брать данные.
            myReport.SetDataSource(myDS);
            //Создаём форму.
            FormRW frm = new FormRW();
            //Указываем отчёт для CrystalReportViewer
            frm.crystalReportViewer1.ReportSource = myReport;
            //Открываем форму.
            frm.Show();
        }


Для работы всех используемых в проекте компонент потребуется подключить:
using System.Xml;
using System.Xml.Schema;
using CrystalDecisions.CrystalReports.Engine;
using System.IO;


P.S.
Вот что получилось в результате (после некоторых доработок):


Скачать исходник: MakeBarTicket.rar


49 комментариев

avatar
Чтобы использовать штриховые коды, в архиве с проектом есть шрифты штрихкода, их надо скопировать в папку Windows\Fonts либо (в случае к примеру с Windows Vista) можно просто установить, что впринципе одно и то же.
avatar
Здравствуйте! А как в отчёт попали рисуночки медведя, птички?
avatar
Просто предварительно воткнул в шаблон, для красоты. :) А то, так как-то скучно смотрелось. Там можно вообще красивенькие билетики делать. Потом на цветном принтере печатать.
avatar
Приветствую! А не подскажите как картинки из xml в формате base64 вставить в отчет CrystalReports?
avatar
Я знаю про динамическую вставку картинок только через OLE object.

Этот метод простой, но ограничен тем, что нельзя для заранее не заданного количества записей, как в моем случае с билетами, задать каждому отдельную картинку (например фото). Однако можно динамически подгружать некоторый набор изображений.

Блин… Во написал, сам запутался. Вообщем это как на билетах в примере, можно скажем менять зверушек, но на всех билетах они будут одинаковые, хотя и будут отличаться от параметров вывода отчета. А вот чтобы на каждом билете сделать, это наверное и можно — но скорее всего сложно, не пробовал.

Через OLE object — так:
Правой кнопкой по отчету в редакторе там выбираем — Insert -> Ole Object…
Появится вот это
Crystal Report Добавление Ole Object
Тут надо обязательно поставить галочку связь.
Затем перед выводом отчета уже из программы — меняем файл на нужный нам (сохраняем на его место нужный нам файл). При выводе отчета его и отобразит.

Остается только из базы вытянуть файл и положить на место указанного.

P.S. Файл должен быть .BMP!!!
avatar
Добрый день! Пробую вставить в отчёт изображение.
Необходимо печатать одно изображение на одном листе, но имя берётся из БД. Как такое провернуть и возможно ли?
Делаю так:
1. Правой кнопкой по отчету, в редакторе выбираю — Insert -> Ole Object…
2. Ставлю галочку связь.
3. Перед выводом отчета подменяю файл test.bmp любым другим файлом, например Photo.bmp. Но при выводе отчета всё равно отображается test.bmp.

Необходимо, например:
Имя файла test.bmp (берётся из mbd файла — каким образом?), вставляем его в отчёт, потом подменяем этот файл любым другим, но путь динамически изменяется (в зависимости от того. где находится запускной файл).

Что не верно?
avatar
Здесь: http://kbss.ru/blog/lang_c_sharp/208.html
Описал как по нормальному добавлять изображения в отчёт.
avatar
Как вместо XML использовать данные из mysql?
avatar
Просто заполняем ДатаСет из базы, например так:
MySqlConnection connRC = new MySqlConnection("строка подключения");
MySqlCommand commRC = new MySqlCommand("здесь запрос на выборку данных", connRC);
    connRC.Open();
    MySqlDataAdapter AdapterP = new MySqlDataAdapter();
    AdapterP.SelectCommand = commRC;
    DataSet myDS = new DataSet();
    AdapterP.Fill(myDS);
    connRC.Close();
//И вставляем источник данных не из XML.
//Указываем отчёту откуда брать данные.
    myReport.SetDataSource(myDS);

Я обычно для тестов выгружаю набор данных в XML — и используя XML-ку создаю сам отчет, редактирую меняю как надо (удобно, при выгрузке из DataSet-а в XML структура данных сохраняется), потом rpt файл забираю и в приложении уже данные берутся не из XML.
avatar
А как можно передать в отчет значение переменной?
string str = "значение";
avatar
Например так:

Создать программно DataTable, задать в ней колонки допустим PARAM и VALUE, заполнить чем хотим, и затем добавить в наш DataSet.

P.S. В DataSet можно добавлять много таблиц и все они будут переданы в отчет.
avatar
Сделал так:
string str = "my parameter text";
myReport.SetParameterValue("prm", str);

В шаблоне отчета необходимо создать параметр «prm».
avatar
Спасибо. Буду знать, может пригодится. Я уже привык — DataSet заполнять всем что надо и с ним экспериментировать. :)
avatar
2 Roman.
А как можно вытягивать данные в отчет, используя написанный Вами класс для работы с MySQL?
avatar
Вот так, например:
MySqlLib.MySqlData.MySqlExecuteData.MyResultData result = new MySqlLib.MySqlData.MySqlExecuteData.MyResultData();

result = MySqlLib.MySqlData.MySqlExecuteData.SqlReturnDataset("select * from market_cards", "Database=inventar_alleya;Data Source=localhost;User Id=root;Password=240580");

            DataSet ds = new DataSet();
            ds.Tables.Add(result.ResultData);

Далее, как в примере добавляем источник данных ds в отчёт и всё.
avatar
Можете помочь, у меня ругается на строчку
ds.Tables.Add(result.ResultData);
говорит что «DataTable уже принадлежит другому DataSet»
в чем может быть баг?
avatar
Например попробовать так:
ds.Tables.Add(result.ResultData.Copy());
avatar
Отличный пример. Но не отрабатывает как надо. Вот мой код:
private void button1_Click(object sender, EventArgs e)
        {
            //Создаём отчёт 
            ReportDocument myReport = new ReportDocument();
            //Задаём форму (которую создавали в пункте 3)
            myReport.Load(@".\CrystalReport1.rpt");

            SqlConnection connRC = new SqlConnection(CS);
            SqlCommand commRC = new SqlCommand("SELECT заявка2.номер" +
                " FROM заявка2" +
                " WHERE заявка2.номер = '546'"
                , connRC);
            connRC.Open();
            SqlDataAdapter AdapterP = new SqlDataAdapter();
            AdapterP.SelectCommand = commRC;
            DataSet myDS = new DataSet();
            AdapterP.Fill(myDS);
            connRC.Close();
            //Указываем отчёту откуда брать данные.
            myReport.SetDataSource(myDS);
            //Создаём форму.
            FormRW frm = new FormRW();
            //Указываем отчёт для CrystalReportViewer
            frm.crystalReportViewer1.ReportSource = myReport;
            //Открываем форму.
            frm.Show();
        }

Отчёт создаётся и заполняется, но заполняется без учёта скульного фильтра. В чём может быть дело?
avatar
Что-то понять не могу, какого фильтра?
Запрос же вернёт набор одинаковых значений
"SELECT заявка2.номер" +
                " FROM заявка2" +
                " WHERE заявка2.номер = '546'"
Возвращает набор чисел 546.
avatar
Да. Так и задумывалось. Например, есть таблица:
код номер
1 123
2 234
3 546
После обработки фильтром, в отчет должна попасть строка с кодом 3. Но попадают все. Не может это быть связано с неправильно составленным файлом отчёта?
avatar
Я бы для начала проверил сколько там в datasete в выборке, действительно ли 1 запись:
myDS.Tables[0].Rows.Count

Если всё ОК, то копать в сторону отчёта. При создании RPT файла, он же чем-то заполняется, так вот то чем он заполняется по структуре должно быть таким же как то что передаём в момент выполнения.

Самый лучший способ правильно сделать RPT — файл это подключить к нему xml — файл источника данных, а сам xml — файл из источника данных получить очень просто:
myDS.WriteXml("ваш_файл.xml", XmlWriteMode.WriteSchema);
avatar
Вообще похоже что проблема в rpt — файле, выглядит так. Как будто для тестирования отчёт подключался к таблице со всей выборкой, и теперь просто лезет туда, в ту самую выборку упорно при вызове.

На 6 и 7 — ом версии CrystalReport, (это не применимо к Visual Studio) из проектов на Delphi когда запускал, помню как раз схожая проблема была с обновлением, там надо было специально запускать обновление данных, иначе отчёт вроде как запоминал последний результат выборки. (точно не помню, может параметр там какой-то в отчёте ставился чтобы этот эффект не проявлялся, давно было).
Всё что я описал перед этим должно сработать, пробуйте делать отчёт с использованием xml-ки.
avatar
Спасибо! С предварительным созданием xml, выборка стала попадать в отчёт. Как то он криво создаётся в VS 2008.
avatar
Теперь возник вопрос по передаче переменных, например в заголовок отчёта (всё равно куда)
Попробовал сделать как описано в посте:
«Сделал так:
string str = "my parameter text";
myReport.SetParameterValue("prm", str);

В шаблоне отчета необходимо создать параметр «prm».»

Добавил строки так:
string str = "150";//textBox1.Text;
myReport.SetParameterValue("prm", str);
SqlConnection connRC = new SqlConnection(CS);

Создал новый параметр в отчёте. И теперь при его запуске появляется запрос на ввод этого параметра. А нужно, чтобы он подцеплял string str = «150». Что не верно?
avatar
Проверил у себя, работает.

Создаём переменную в отчёте, вот так:
Создание параметра в CrystalReport

Затем, задаём параметр в коде:
myReport.SetParameterValue("param1", "Go Go Go!");

Я это сделал в самом конце, уже после того как источник данных подключил к отчёту.

И вот, что получилось:
Создание параметра в CrystalReport
avatar
Вставлял до привязки к отчёту. Поэтому и не работало… (
avatar
Возможно переменная не передаётся по той же причине, по которой без XML не обойтись…
avatar
Объясню как я эти отчётики делаю.
1. Создаю из основного проекта XML-ку с набором данных, для отчёта (чтобы было с чем экспериментировать.
2. Создаю совершенно новый проект в студии CrystalReport — не имеющий ничего общего с основным.
3. Добавляю в него в качестве данных XML-ку и использую поля для размещения на форме отчёта.
4. Сохраняю.
5. Забираю из этого проекта rpt — файл и использую его в основном проекте, обычно они у меня в отдельной папочке c отчётиками все лежат, подключая к нему напрямую источник данных — и всё работает.
avatar
Спасибо. Заработало. Но появилась новая беда. Данные удваиваются. Например, если строк в XML две (а именно столько и должно получится), то в отчёт попадает ровно в два раза больше, т.е. четыре. При проверке количества строк выдаётся верное количество. Код такой:
MessageBox.Show(myDS.Tables[0].Rows.Count.ToString());
            //Создаём отчёт 
            ReportDocument myReport = new ReportDocument();
            //Задаём форму
            myReport.Load(@".\CrystalReport1.rpt");

            myDS.WriteXml("otchet1.xml", XmlWriteMode.WriteSchema);
            //Указываем источнику данных вновь сформированный XML файл.
            myDS.ReadXml(@".\otchet1.xml");
            //Указываем отчёту откуда брать данные.
            myReport.SetDataSource(myDS);

            //myReport.SetParameterValue("param1", "Go Go Go!");
            string str = cmbZay.Text;//textBox1.Text;
            myReport.SetParameterValue("param1", str);

            //Создаём форму.
            FormRW frm = new FormRW();
            //Указываем отчёт для CrystalReportViewer
            frm.crystalReportViewer1.ReportSource = myReport;
            //Открываем форму.
            frm.Show();
avatar
Вот это вот непонятно зачем вообще делается:
myDS.WriteXml("otchet1.xml", XmlWriteMode.WriteSchema);
//Указываем источнику данных вновь сформированный XML файл.
myDS.ReadXml(@".\otchet1.xml");


А если уж без этого никак, то перед загрузкой данных из XML, неплохо бы датасет вычистить:
myDS.Clear();
avatar
Т.е. каждый раз при запуске создании отчёта не нужно это:
myDS.WriteXml("otchet1.xml", XmlWriteMode.WriteSchema);
//Указываем источнику данных вновь сформированный XML файл.
myDS.ReadXml(@".\otchet1.xml");
А как тогда происходит привязка к базе, если в отчёте мы указываем в качестве источника XML, а потом его не используем?
И второй вопрос: на машинах, где нет студии программа не запускается. Какие компоненты должны быть обязательно установлены помимо .NET Framework 2.0?
avatar
Отчёту передаётся источник данных MyDS — данные в нём уже есть из базы. А по структуре он идентичен xml — файлу, мы ведь xml — файл из него делали (делали только для того чтобы форму отчёта сделать в которой структура данных будет совпадать).

На клиенте нужны:
.NET — Не ниже той под которую писали.
И библиотеки Crystal Report-а — CRRedist2008_x86.msi или CRRedist2008_x64.msi в зависимости от разрядности платформы.

Библиотеки поиском можно найти в каталоге с VisualStudio.
avatar
Спасибо, Роман, большое! Если бы не этот блог, так и остался бы CR тёмным лесом.
Компоненты среды выполнения Crystal Reports
avatar
Доброго времени,

хочу сделать динамически создаваемый отчет без XML,
1)сейчас остановился на том что в этой строке выдает ошибку:
myReport.SetDataSource(GetAllTypeStreet());

GetAllTypeStreet() — возвращает DataTable

если у кого есть готовый пример, вышлите на hvvmail@mail.ru
спасибо
avatar
GetAllTypeStreet() — Должен возвращать DataSet.
avatar
то же самое, выдает ошибку «The report has no tables.»

в CrystalReport.rpt1 я добавлял только два поля в Parametr Fields, в которые в последующем думал подставить значения.
avatar
Если там только параметры, то зачем вообще источник данных передавать.
Просто передавайте параметры:
myReport.SetParameterValue("param1", "Go Go Go!");

pram1 — это наименование параметра.
Go Go Go! — значение, подставляете своё, и всё.
avatar
Вот пишу:
myReport.SetParameterValue("ID", GetAllTypeStreet().Tables[0].Rows.ToString());

хочу отобразить столбец, а мне выдает одну запись такого вида:
System.Data.DataRowCollection


или надо в цикле перебрать?, и как правильно отобразить значения?
avatar
Скачайте пример из статьи и посмотрите как сделано — там всё как надо.

Вам, как я понимаю необходимо передать в отчёт таки набор данных.
Для этого — сохраните ваш DataSet — в файл xml.
После чего в редакторе rpt — файла, подключите внешним источником данных этот xml файл, настройте как необходимо, при этом сможете визуально это всё просмотреть (как оно будет при выполнении).
После этого полученный rpt — можно использовать в своём проекте просто передав ему DataSet (той же структуры — что сохраняли в xml).

Вот это вот, вообще не правильно:
GetAllTypeStreet().Tables[0].Rows.ToString()

Так можно только отдельное значение из таблицы в строку передать, и правильно это будет, вот так:
string param = GetAllTypeStreet().Tables[0].DefaultView[номер_строки][номер_столбца].ToString();
//При условии что GetAllTypeStreet() - DataSet

string param = GetAllTypeStreet().DefaultView[номер_строки][номер_столбца].ToString();
//При условии что GetAllTypeStreet() - DataTable


Вот так вот, а вообще советую скачать пример и вдумчиво перечитать все комментарии от начала, в них уже есть ответы на ваши вопросы.
avatar
Спасибо за советы, но моя задача состоит в том, чтобы непосредственно из запроса передать и отобразить данные в Crystal Reports, т.е.:
1)устранить ошибку в этой части кода:
myReport.SetDataSource(GetAllTypeStreet());
;
2)в коде создать привязку элементов на отчете с данными из DataSource.

З.Ы.
С Crystal Reports пока практически не знаком, до этого работал с отчетами в Access, там я в дизайнере отчета создавал поле с именем из DataSource в Section (Details) и наблюдал ожидаемый результат. Здесь я думаю все не на много отличается.
avatar
DataSet myDS = new DataSet(); //Это DataSet !
//Откуда мы его заполним не важно, хоть из базы, хоть из файла, хоть вручную.

//Указываем отчёту откуда брать данные.
myReport.SetDataSource(myDS);

Чтобы создать отчёт, с таким же источником данных какой мы ему будем передавать, мы просто (только для создания отчёта!) сохраняем источник данных в XML файл, вот так:
myDS.WriteXml("ваш_файл.xml", XmlWriteMode.WriteSchema);
Это необходимо только чтобы создать rpt — файл! Который будет работать с таким же в точности источником данных какой мы ему будем передавать через myDS Только для создания! Далее в работе нам потребуется просто передать требуемый DataSet в отчёт и всё, без XML.

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

К отчёту RPT-файлу, для настройки:
В Field Explorer правой кнопкой по Database Fields. Там выбрать Databse Expert.
В нём подключаете файловый источник данных, как здесь:
Подключение файлового источника данных

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

XML — файл это всего лишь источник данных! Который используется для первоначальной настройки формы, далее он не нужен. Его вообще можно убрать из кода. В примере к публикации он используется для заполнения всё того-же DataSet — но это не значит, что DataSet нельзя заполнить прямо из базы, запросами.
avatar
спасибо, сейчас попробую так сделать
avatar
не пойму в чем дело, при просмотре данных из XML-файла отображается 10-строк с названия цветов, хотя должно отобразиться 7-строк с названиями типов улиц
avatar
т.е. если в Field Explorer на поле выбрать «Browse Data...» так данные такие, как и должны быть, а вот в самом отчете не те…
avatar
чудеса, при передаче в DataSet отчета непосредственно коллекцию DataSet из запроса, все данные отображаются правильно.

Спасибо. Просьба модератору объединить мои последние сообщения, или убрать лишние
avatar
ReportDocument myReport = new ReportDocument();
            myReport.Load(@".\CrystalReport1.rpt");

            DataSet myDS = new DataSet();
            
            myDS.ReadXml(@".\dataset.xml");
            myReport.SetDataSource(myDS);

Сбой при загрузке информации из базы данных.
Ошибка в файле CrystalReport1 {4B582FDD-47AC-499A-8831-36EA5955CB1E}.rpt:
Сбой при загрузке информации из базы данных.

Как исправить?
MS Visual 2010 Ultimate 10.0.30319.1
  • AmFa
  • 0
avatar
Для 2010 студии нужен «SAP Crystal Reports, version for Visual Studio 2010»
Он отличается от того что идёт в комплекте с 2008 студией, и скачивается и устанавливается отдельно.
Проект автоматически из версии 2008 в 2010 наврядли — скомпилируется. Во всяком случае я делал отдельный, новый проект.
avatar
Кстати «SAP Crystal Reports, version for Visual Studio 2010» — бесплатный. Только установится на студию не ниже Professional (на экспресс версиях работать не будет).
avatar
Я установил SAP Crystal Reports этот файлом CRforVS_13_0_2
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.