Visual Basic, .NET, ASP, VBScript
 

   
 
Описание для автора не найдено
 
     
   
 

Использование True DBgrid при создании приложений для управления базами данных на Visual Basic 6.0 (Часть2)

 

Введение (обновленное и дополненное)

Как только я собрался писать вторую часть статьи, посвященной программированию на VB6, так Microsoft объявила об окончании поддержки Visual Basic 6.0. Окончательным сроком было объявлено 31 марта 2005 года. В Интернете на http://www.classicvb.org/ развернули сбор подписей в поддержку VB6 . В эхе fido7.ru.visual.basic развернулась весьма бурная дискуссия по этому поводу. Все это, а также мое собственное осознание того, что я, увы, отстаю от прогресса, поставило под угрозу выход второй части. Я долго колебался между необходимостью продолжить труд и переходом на VB.NET. Лихорадочно перечитывал сообщения в эхе, читал в Интернете статьи многих уважаемых программистов. И Постепенно пришел к выводу, что статью все же закончить надо, а VB6 проживет еще достаточно долго. И тому есть примеры: FoxPro для DOS.

 

Итак, в первой части мы рассмотрели:

 

  • сам TrueDbGrid;
  • промежуточные компоненты: AdoDc, TrueDbGridDown;
  • использование TDataLite;
  • различные режимы фильтрации;
  • и, наконец, перехват ошибок.

 

 

Статья, как это ни странно, вызвала определенное количество откликов. Часть вопросов, которые мы дальше рассмотрим, были навеяны перепиской с читателями. Сегодня мы обязательно остановимся на сохранении параметров TrueDbGgrid и восстановлении их при следующем запуске программы, на сортировке данных в TrueDbGgrid и отображении информативной стрелки направления сортировки. Мы посмотрим, как можно выполнить экспорт и импорт данных средствами TrueDbGgrid и ADO. Попробуем избавиться от компонента Adodc. А также обязательно остановимся на печати табличных данных при помощи самого TrueDbGgrid и при помощи компоненты VSView.

 

Отказываемся от AdoDc

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

 

Примечание. Компания ComponetOne, прекрасно понимая ущербность AdoDC, написала TData (не путайте с TDataLite). Это своего рода симбиоз AdoDC и TDataLite с некоторыми дополнительными возможностями. В частности в эту компоненту заложены функции автоматического обновления Recodset, что позволяет отказаться от ручного обновления и, соответственно, от таймеров.

 

Итак, какова же методика отказа от AdoDc? Для начала создадим глобальную переменную Connect, которая будет глобальна для всего проекта.

Public connection As New ADODB.connection

 

Понято, что ссылка на ADO в вашем проекте уже должна стоять! (Смотри часть первую)

 

Далее подключение к БД нужно открыть. Это все очень похоже на AdoDc.

 

    connection.ConnectionString = "PROVIDER=SQLOLEDB" & _

        ";SERVER=" & SerName & _

        ";UID=" & UserName & _

        ";PWD=" & pss & _

        ";DATABASE=CDR_SI"

 

    connection.Open

 

Переменные SerName, UserName, pss можно заменить непосредственно на нужные значения, а можно эти переменные определить и присвоить значения. Эта строка предназначена для подключения к SQL Server. В случае с MDB это будет выглядеть так:

 

connection.ConnectionString = “Provider=Microsoft.Jet.OLEDB.4.0;Data “ & _ “Source=C:\kross\krossav.mdb;Persist Security Info=False”

    connection.Open

 

 

Все это мы уже рассматривали в первой части. Ничего нового пока нет.

После выполнения строки

 

connection.Open

 

через объект connection будет открыто подключение к БД, и оно будет открыто до тех пор, пока вы явно его не закроете:

 

connection.Close

 

или

 

Set connection=Nothing

 

 

Примечание. Завершение программы без явного закрытия подключения, скорое всего, не закроет открытого подключения. При этом в случае с MDB файлами оно обычно закрывается, а в случае с SQL Server не закрывается никогда! Учитывая тот факт, что количество подключений к SQL Server ограничено, такая ситуация будет пустым расточительством ресурсов. Расскажите своему системному администратору о том, что для отключения пользователей от SQL Server достаточно его перезапустить, а сам сервер перезагружать не нужно.

 

Для получения данных из БД и передачи их для отображения в Grid нам необходимо создать объект Recordset. Сначала опишем переменную:

 

Dim rsa As New ADODB.Recordset

 

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

Чтобы открыть Recordset используется следующий код:

 

strSql = "SELECT     Id, DateDay " & _

                "From dbo.CDR_DAY " & _

                    "Where (Id = 1)"

 

 

rsa.Open strSql, connection, adOpenKeyset, adLockOptimistic

 

Понятно, что вместо переменной strSql можно непосредственно написать текст SQL запроса. Вы также можете указать тип курсора и тип блокировки данных.

 

Вот собственно говоря и все. Осталось передать открытый Recordset в Grid, делается это просто:

 

Set Me.TDBGrid1.DataSource = rsa

 

Для закрытия объекта Recordset используем конструкции:

 

rsa.Close

 

или

 

Set rsa=Nothing.

 

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

Экспорт и импорт данных в TrueDbGrid и ADO

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

Все это привело к тому, что солидная программа должна обладать средствами экспорта и импорта данных. Задача экспорта намного проще импорта, так как БД используется только для чтения. Импорт, возможно, потребует более детального рассмотрения проблемы, формирование дополнительных таблиц и преобразования данных.

ADO

Экспорт данных средствами ADO выполняется при помощи метода Save объекта Recordset. Имеется возможность сохранить данные Recordset в формате XML и в формате ADTG. Последний – это специфический бинарный формат, предназначенный для обмена данными между различными Recordset. Формат XML сегодня становится практически основным обменным форматом. Однако, несмотря на то, что формат XML, строго говоря, стандартизирован, он имеет много различных версий и реализаций. Поэтому прочитать ваш экспортный файл сторонним программным обеспечением не проблема, но вот считать в Recordset, файл XML, созданным другой программой, весьма затруднительно.

 

Для сохранения данных Recodset в файле используется следующий код:

 

rsa.Save "sss.xml",adPersistXML

 

Метод Save делает копию Recodset на диске, но не закрывает его. Имя файла и путь к нему может быль любым подходящим. Чтобы сохранить файл в формате ADTG, второй параметр метода можно не указывать. Так как данные по умолчанию сохраняются в этом формате. Но лучше параметр все же указывать, потом код программы будет легче читать.

 

rsa.Save "sss.dat", adPersistADTG

 

Чтобы открыть ранее сохраненный файл-Recordset используется метод Open.

 

rsa.Open "sss.dat", "Provider=MSPersist;", adOpenKeyset, _

dLockOptimistic, adCmdFile

 

Все параметры нам уже известны, кроме одного "Provider=MSPersist;". Это провайдер OLE DB позволяющий работать с отсоединенными наборами строк. Собственного говоря все, что рассказано выше имеет свое специальное предназначения в ADO. Все это средства работы с отсоединенными объектами Recordset, которые позволяют продолжить работу с набором данных даже после того, как объект будет отсоединен от источника данных. Эта методика применяется для обеспечения мобильности пользователям, работающим при непостоянном соединении. Отсоединенный Recordset можно изменять всеми доступными через ADO средствами, а затем синхронизировать с основной БД.

Чтобы выполнить синхронизацию БД и отсоединенного набора строк нужно выполнить следующее:

 

rsa.ActiveConnection=connection

 

rsa.UpdateBatch

 

Примечание. Мне трудно сказать как будут работать отсоединенные объекты от файловой БД (MDB), насколько корректно они будут синхронизироваться с основной БД и так далее? Я никогда этого в случае с MDB не делал. В случае с SQL Server все работает. Однако обработку коллизий программист должен взять на себя, так как все изменения, вносимые в отсоединенный Recordset будут применены к оригинальным данным в БД.

DBGrid

Экспорт данных в True DBGrid представлен более обширными форматами и средствами, по сравнению с ADO. Кроме обычных средств имеются средства для экспорта конкретной строки или части строк.

Чтобы выполнить экспорт в формате HTML использует метод ExportToFile, например, так:

 

Me.TDBGrid1.ExportToFile "ssss.html", False

 

У метода ExportToFile следующий синтаксис:

 

TDBGrid.ExportToFile pathname, append, [selector], [tablewidth]

 

Где параметр pathname – это имя файла с полным путем к нему. Второй  параметр append ­­может принимать значение False или True – указывает, следует ли дописывать строки к существующим в файле или нет. Параметр Selector предназначен для указания какие строки должны быть экспортированы: 0 – dbgAllRows – все строки, 1 – dbgSelectedRows

– только выбранные строки, 2 – dbgCurrentRow – только текущую строку. Параметр tablewidth используется при генерации тэга <table> в формате HTML.

 

Вы можете применять фильтр к Grid, а после этого вызывать экспорт, чтобы экспортировать только отфильтрованные записи. Параметр Selector  не так уж и удобен, как может показаться. Проще фильтровать записи средствами ADO (пусть даже и через Grid), чем пользоваться средствами SelBookmarks. Раз уж мы начали этот разговор давайте рассмотрим как можно пользоваться средства SelBookmarks. В комплекте с TrueDBGrid поставляет пример (Tutoral 5), который рассматривает работу с SelBookmarks подробно.

 

Dim SelBks As TrueOleDBGrid70.SelBookmarks

Set SelBks = TDBGrid1.SelBookmarks

 

Применять SelBookmarks рекомендуется на клоне Recordset.

 

Dim dclone As ADODB.Recordset

Set dclone = rsa.Clone

 

Далее начинаем выделять строки:

 

        SelBks.Add dclone.Bookmark

 

Напомню, что свойство Bookmark по-умолчанию соответствует текущей строке в Recordset. Понятно, что выполнять эту строку можно по условию, в процессе перебора строк, как это сделано в примере №5.

 

Чтобы снять выделение следует выполнить такой код:

 

While SelBks.Count

    SelBks.Remove 0

Wend

 

Пользователь может выделять строки щелкая их мышью, совместно с клавишей CTRL. При этом свойство AllowRowSelect должно быть установлено как True.

 

 

Обратите внимание, что изучая средства экспорта, мы изучили еще одно важное средство TDBGrid – SelBookmarks. На практике такое применение SelBookmarks используется редко, но умение определения записей, которые пользователь предварительно выделил может выручить вас на просторах программирования – ситуации бывают разные.

 

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

 

TDBGrid1.ExportToDelimitedFile pathname, [delim], [selector], _

 [prefix], [suffix]

Параметр delim – это и есть разделитель полей. Параметр selector – рассмотрен выше. Параметры prefix и suffix используются для ограничения поля сначала и с конца значения. Часто для этого используют кавычки.

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

 

Импорт данных

Строго говоря импорт никакого отношения к Grid не имеет. Так это применительно только к БД. Однако тема эта весьма важная и умение применять средства VB для импорта пригодится вам в дальнейшем. Как правило импорт выполняется из текстовых файлов и из файлов CSV. Остается только гадать, почему Microsoft не встроила средства импорта CSV в ADO? На практике, возможно, потребуется импортировать данные из Excel. Это отдельный вопрос, требующий более детального рассмотрения. Excel мощная программа, импорт из нее можно выполнить разными способами. В некоторых случаях файлы *.XLS могут представляться как файлы БД и позволяют обращаться к ним средствами ADO. Другими словами, на этом мы останавливаться не будем. Это тема отдельной публикации.

Остановимся на импорте текстовых файлов.

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

 

18.04.20057294321822559498         11:01:07000302001

18.04.2005729409189202162913       11:01:30000102001

18.04.2005729407821456             10:44:03010670018

18.04.20057294049822368421         11:02:36000472001

18.04.2005729409393689             11:04:17000020001

18.04.2005729411493511             11:04:20000070001

 

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

 

                Примечание. Обратите внимание, что ни Word, ни Total Commander не помощники в этом вопросе. Посчитать в Word пробелы и символы крайне затруднительно. И здесь самый лучший вариант использовать DOS-программы, или псевдо-DOS, которой и является FAR.

В нашем примере можно ничего не считать, так как описание на формат есть:

  10
     <Date>
     Дата в формате <DD.MM.YYYY>

      7
     <Subscriber A>
    
Номер абонента A

      18
     <Subscriber B>
     Номер вызываемого абонента


      8
     <Time>
     Время начала разговора в формате <HH.MM.SS>

      5
     <TalkTime Sec>
     Время разговора в секундах

      1
     <TarifType>
     0 - местный / 2 - междугородний вызов

      3
     <TalkTime Min>
     Поминутный тарификатор

 

 

Где цифры в таблице означают размер поля, далее идет его описание. Импорт такого файла не представляется сложным.

 

Для начала заведем переменную типа (запись)

 

Type SaveConnect

  AbonentA As Long

  AbonentB As String

  ProdW As Long

  DateW As String

End Type

 

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

 

Опишем переменную:

 

Public SConnect As SaveConnect


Далее код открытия файла:

 

  FileNum = FreeFile

  Close FileNum

  Open “c:\tarif.log” For Input As #FileNum


Line Input #FileNum, ReadString

 

 

 

 

While Not EOF(FileNum)

 

SConnect.DateW = Replace(Format(DateValue(Mid(ReadString, 1, 10)), _

"dd/MM/yyyy"), ".", "/") & " " & _

Format(Mid(ReadString, 36, 8), "HH:NN:SS")

 

SConnect.ProdW = Val(Mid(ReadString, 44, 5))

 

SConnect.AbonentB = Trim(Mid(ReadString, 18, 18))

 

SConnect.AbonentA = Val(Mid(ReadString, 11, 7))

 

‘ сюда добавить код записи в БД

 

Wend

 

Остается только записать полученные значения в БД. Это отдельный вопрос и здесь мы его рассматривать не будем.

 

Давайте рассмотрим приведенный код более подробно.

 

SConnect.DateW = Replace(Format(DateValue(Mid(ReadString, 1, 10)), _

"dd/MM/yyyy"), ".", "/") & " " & _

Format(Mid(ReadString, 36, 8), "HH:NN:SS")

Здесь мы читаем из строки ReadString подстроку с символа 1 позиции до символа 10 позиции, затем полученная дата преобразуется к специальному формату, который необходим для корректной записи в БД. Кстати, применение функции Format здесь является избыточным. Это предусмотрено специально, если вдруг формат даты в исходном файле изменится. Впрочем это бывает редко. Замена точки на слеш – это обязательная процедура – так как ADO в качестве разделителя в дате обязательно использует слеш. Далее мы читаем время из строки ReadString с символа 36 позиции до символа 44 позиции. Также применяется специальный формат, что в данном случае также излишество.

Замечание. Излишнее применение функции Format может показаться некорректным программированием. Но практика использования программ на разных ОС (русифицированных и чисто англоязычных, с неверно установленными регионами и так далее) показала, что такое преобразование гарантирует правильное представление дат. Рекомендую переменную DateW описать как String, чтобы исключить ее преобразование средствами VB. На практике вы очень быстро поймете, насколько неожиданным может оказаться результат при работе с датами в БД. Есть еще одно замечание: при добавлении записей в БД средствами ADO (метод AddNew) дату можно записывать в формате dd/mm/yyyy. А вот в случае в SQL инструкцией Insert, дата должна быть в формате mm/dd/yyyy. Кстати, чтобы раз и навсегда исключить разночтения дат лучше месяц представлять словом, а не цифрой.

 

Переменная SConnect.AbonentB имеет тип String. Записывать незначащие пробелы ни к чему. Поэтому используется функция Trim, удаляющая незначащие пробелы. Переменная SConnect.AbonentA, напротив, имеет тип Long, потому следует текст преобразовать в число, для это и используется функция Val, которая автоматически отбрасывает незначащие пробелы. И, наконец, везде используется функция Mid, которая возвращает часть строки с определенной позиции и определенной длины. Ничего сложно в таком импорте нет.

 

Перейдем к файлам с идентификаторами. Смысл идентификатора в том, чтобы как-то указать, что данная информация является принадлежностью определенного поля, но его длина не фиксирована. Ниже предлагаю примере такого файла:

 

25.03.2003(Tu) 18:35:41   1   148s AB  N= 25453 A=7294059.1 IC=128 EC=192

25.03.2003(Tu) 18:38:50   1    27s AB  N= 25598 A=7294059.1 IC=128 EC=192

25.03.2003(Tu) 18:38:59   1    54s AB  N=89038513388 A=7294094.1 IC=128 EC=192

25.03.2003(Tu) 18:41:40   1     2s AB  N= 22149 A=7294016.1 IC=128 EC=192

 

Как видите, вначале каждая строка имеет фиксированный формат и размер полей, а вот далее, поле N= может менять свою длину и соответственно все поля далее сдвигаются.

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

 

 

SConnect.DateW = Replace(Format(DateValue(Mid(ReadString, 1, 10)), _

"dd/MM/yyyy"), ".", "/") & " " & Format(Mid(ReadString, 16, 8), "HH:NN:SS")

 

SConnect.ProdW = Val(Mid(ReadString, 28, 6))

 

Здесь все просто и аналогично предыдущему примеру. А вот далее начинаются сложности. Переменная  SConnect.AbonentB. Позиция идентификатора N= всегда постоянно, а вот позиция идентификатора A= может меняться. Чтобы получить размер поля, достаточно получить разницу из позиций за минусом 3, так как нужно выбросить из результата A= и незначащий пробел перед ним. Функция InStr возвращает позицию найденного контекста. Надо сказать, что и начальную позицию идентификатора N= мы получаем через функцию InStr.

 

SConnect.AbonentB = Trim(Mid(strZvon, InStr(1, ReadString, "N=") + 2, _

 InStr(1, strZvon, "A=") - InStr(1, ReadString, "N=") - 3))

 

Аналогична ситуация и с переменной SConnect.AbonentA.

 

SConnect.AbonentA = Val(Trim(Mid(ReadString, InStr(1, ReadString, "A=") _

+ 2, InStr(1, ReadString, "A=") - InStr(1, ReadString, ".") - 1)))

 

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

 

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

 

401698;2925087;30.08.04 10:12:16;0734021683;09;;23400

401699;2925088;30.08.04 10:12:18;0734021492;22759;;22080

401700;2925089;30.08.04 10:10:26;9056505313;21956;;134350

401701;2925090;30.08.04 10:12:32;0734022453;21529;;9990

 

Для импорта файлов CSV воспользуемся функцией Split. Учитывая, что количество полей всегда заранее известно, можно описать массив. В нашем случае полей 7 и в качестве разделитя используется точка с запятой. Обратите внимание, что предпоследнее поле не имеет значения и поэтому там появились две точки с запятой. Первые два поля для нас могут быть не информативными (это уникальный номер и идентификатор процесса в оборудовании), поле 6 пустое, а вот все остальные это то, что нужно. Однако проще определить массив размером 7, а потом просто выбрать из него только те значения, которые необходимы. Не забываем, что по умолчанию нижняя граница массива 0.

 

Dim stroka(6) As String

 

stroka=Split(ReadString, “;”)

 

SConnect.DateW = Trim(stroka(2))

 

SConnect.ProdW = Val(stroka(6))

 

SConnect.AbonentB = Trim(stroka(4))

 

SConnect.AbonentA = Trim(stroka(3))

 

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

 

 

Сортировка списков в True DbGrid

Речь пойдет, собственно, не о самой сортировке, а о том, как это «красиво» представить средствами интерфейса Grid. Обычно сортировка списков выполняется при нажатии мышью на заголовок столбца, при этом на заголовке столбца появляется информационная стрелка, указывающая направление сортировки. True DBGrid позволяет это сделать весьма эффективно. Для начала определимся: мы будем это делать использую стили оформления. В TrueDbGrid имеется ряд предопределенных стилей, но мы создадим свои. Предварительно вы должны подготовить два файла в формате BMP с изображением стрелки вверх и вниз. Размер рисунка можно подобрать самому, по-моему 10х10 очень неплохо смотрится. В комплекте с True DbGrid поставляются специальные картинки, в том числе и стрелки. Единственный недостаток формата BMP заключается в том, что он не поддерживает прозрачность. Возможно в более поздних версиях Grid эта проблема решена.

Чтобы заголовок поля списка Grid вел себя как кнопка необходимо свойство ButtonHeader установить True для каждой колонки, где это нужно. Обычно так делают для всех колонок.

 

Теперь перейдем к Style Factory. Чтобы облегчить себе жизнь создадим два новых стиля на основе стиля Heading. Назовем их, соответственно «Up» и «Down». Отличаться эти два стиля от стиля Heading будут лишь двумя свойствами: ForegroundPicture и ForegroundPicturePosition. Свойство ForegroundPicture хранит картинку, обозначающую направление сортировки. Для стиля Up – это стрелка вверх, а для стиля Down – стрелка вниз. Свойство ForegroundPicturePosition устанавливается в 1-Right для новых стилей, это означает, что картинка всегда будет показываться справа, независимо от ширины колонки.

 

 

Работы с интерфейсом закончены, осталось запрограммировать корректное поведение Grid. При нажатии мышью на заголовок колонки генерируется событие TDBGrid1_HeadClick, а в качестве параметра возвращается номер колонки, по которой осуществилось нажатие. Код данного события будет выглядеть так:

 

Private Sub TDBGrid1_HeadClick(ByVal ColIndex As Integer)

  SetSortHen TDBGrid1, rsa, ColIndex

End Sub

 

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

 

Public Sub SetSortHen(ByRef tdg As TDBGrid, ByRef rs As Recordset, _

ByVal ColIndex As Integer)

 

Dim i As Integer

Dim book As Variant

 

On Error Resume Next

 

 

 Screen.MousePointer = vbHourglass показали часики

   DoEvents

 

  For i = 0 To tdg.Columns.Count – 1

‘ пробежали все колонки и применили к ним стандартный стиль оформления

 

    tdg.Columns(i).HeadingStyle = tdg.Styles("Heading")

  Next i

  

  

  

   If rs.Sort = "" Then

   'сортировка туда (по возрастанию), первая сортировка

 

      ‘ передали в Recordset имя поля, по которому следует сортировать

        rs.Sort = tdg.Columns(ColIndex).DataField

     

      ‘ применили стиль Down, стрелочка появилась   

          tdg.Columns(ColIndex).HeadingStyle = tdg.Styles("Down")

   ElseIf rs.Sort <> "" And rs.Sort <> tdg.Columns(ColIndex).DataField Then

   ‘ снова туда

 

      ‘ передали в Recordset имя поля, по которому следует сортировать

         rs.Sort = tdg.Columns(ColIndex).DataField

 

      ‘ применили стиль Down, стрелочка появилась   

         tdg.Columns(ColIndex).HeadingStyle = tdg.Styles("Down")

 

   ElseIf rs.Sort <> "" And InStr(rs.Sort, "DESC") > 0 Then

     ' снова туда, если была применена обратная сортировка 

 

        rs.Sort = tdg.Columns(ColIndex).DataField

           tdg.Columns(ColIndex).HeadingStyle = tdg.Styles("Down")

  

   Else

    ' обратно

           rs.Sort = tdg.Columns(ColIndex).DataField & " DESC"

 

‘применили стиль Up, стрелочка появилась

           tdg.Columns(ColIndex).HeadingStyle = tdg.Styles("Up")

    End If

  

    

Screen.MousePointer = vbDefault ‘вернули курсор мыши на нормальный

End Sub

 

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

 

 

 Сохранение параметров TrueDbGgrid

Под сохранением параметров здесь понимаются параметры внешнего вида Grid: ширина колонки и столбца. Средства TrueDbGgrid позволяют сохранять все его параметры в файл, а затем восстанавливать их в нужный момент. Понятно, что параметры сортировки придется сохранять вручную, например, в реестре Windows и применять их при открытии приложения. Есть много и других подобных параметров, которые, строго говоря, к Grid не имеют отношения, а относятся к самому приложению. Более того, сохранить, например, установленную пользователем ширину столбцов не проблема.

 

Public Sub SaveWidhColumn(tdg As TDBGrid)

Dim i As Long

 

On Error Resume Next

For i = 0 To tdg.Columns.Count - 1

    SaveSetting "Avtokross", "SettingsCol", tdg.Name & i, tdg.Columns(i).Width

Next i

End Sub

 

А затем восстановить из при запуске приложения:

 

Public Sub SetWidhColumn(tdg As TDBGrid)

Dim i As Long

For i = 0 To tdg.Columns.Count - 1

    tdg.Columns(i).Width = GetSetting("Avtokross", "SettingsCol", tdg.Name & i, 1500)

Next i

End Sub

        

В качестве параметра tdg передается Grid. Раздел реестра (в примере Avtokross) можно указать по своему желанию. Работает это также хорошо, как и рассматриваемый ниже способ, но мы то с вами учимся использовать все возможности Grid, поэтому будем программировать в таком направлении.

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

 

Чтобы произвести запись изменений ширины столбцов воспользуемся соответствующим событием:

 

Private Sub TDBGrid1_ColResize(ByVal ColIndex As Integer, Cancel As Integer)

On Error Resume Next

 TDBGrid1.LayoutFileName = App.Path & "\cdrview.grx"

 TDBGrid1.Layouts.Add "MainLayout"

End Sub

 

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

 

Замечание. Насколько я понял из документации в файле Layout сохраняются все (!) параметры Grid.

 

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

Для восстановления сохраненных значений используется следующий код:

 

    TDBGrid1.LayoutName = "MainLayout"

    TDBGrid1.LayoutFileName = App.Path & "\cdrview.grx"

    TDBGrid1.LoadLayout

 

И последнее. Будьте внимательны. При изменении параметров Grid в режиме Design их нужно сохранить в файле Layout, например, путем специального вызова той части программы, которая вызывает запись параметров. Если этого не сделать, после чтения данных из файла Layout, они будут восстановлены во время выполнения программы, но при этом (!) в режиме Design они не изменятся. Вот и получается, что вы меняете свойства, а они как будто, не применяются. Однажды я долго менял свойства, пока не додумался временно отключить приведенные выше три строки кода из события Form_Load.

 

Печать табличных данных средствами TrueDBGrid

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

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

 

Private Sub Command1_Click()

    With TDBGrid1.PrintInfo

       

 

' устанавливаем шрифт и текст заголовка

 

        .PageHeaderFont.Italic = True

        .PageHeader = "Пробная таблица"

       

        ' повторяем заголовки столбцов на каждой странице

        .RepeatColumnHeaders = True

       

        ' показать номер страницы по центру

        .PageFooter = "\tPage: \p"

 

        ' вывести на предварительный просмотр

        .PrintPreview

    End With

End Sub

 

При выполнении кода получится приблизительно следующее:

 

 

 

Красиво, но не очень удобно. Все средства управления: меню, кнопки, всплывающие подсказки – по-английски.

Многие параметры предварительного просмотра можно установить вручную в процессе разработки программы на вкладке PrintInfo свойства Custom самого Grid.

 

Еще больше параметров можно поменять в процессе выполнения программы. Ну, например, добавим строку

 

        .SetMenuText dbgpMenuFile, "Файл"

 

перед строкой

 

.PrintPreview

 

И, о чудо, меню File стало называться «Файл».

 

           

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

 

 

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

Замечание. Далее в примерах используется VSView версии 7.0 фирмы ComponentOne. Для корректной работы проекта требуется на панель объектов интерфейса добавить компоненту VideSoft VSPrinter. А в ссылках References, если они не добавятся автоматически, установить галочки ComponentOne VSReport 7.0 Control и VideSoft VSPrinter 7.0. Далее в статье я рассматриваю только малую часть средств этой компоненты.

 

Печать табличных данных средствами VsView

Разработка средств печати в приложении при помощи компоненты VsView можно разделить на два этапа. Первый – это собственно программирование на VB, а второй – это создание макета отчета в конструкторе, очень похожем на конструктор отчетов в Access. Начнем с программирования. Создаем новую форму. Ее размер подбирайте сами. Расположим на ней объект VSPrinter и назовем его «vp». Также можно на форму добавить меню Файл, для экспорта, вызова окна параметров страницы и всего остального чего пожелаете. Также следует добавить на форму объект Label, обычно выше VSPrinter и где-то в удобном месте кнопку Command1 и измените свойство Caption на «Отмена». По умолчанию эта кнопка Visible=False. Пример на рисунке ниже:

 

 

Обратите внимание, что кнопки перебора страниц, кнопка с лупой и принтером появятся автоматически. Настроим объект VSPrinter. Для этого вызовем свойство Custom. Появится диалоговое окно. Нас интересуют только две вкладки: Navigation и Settings. Первая вкладка предназначена для настройки панели навигации (переход по страницам).

 

Можно выбрать место расположения панели навигации. Максимальный и минимальный Zoom, а также шаг его изменения.

 

 

 

На вкладке Setings можно по-русски указать что будет написано на маленьком окошке, которое будет появляться при отправке документа на принтер. На нем будет также кнопка для отмены печати.

 

В поле NavBarMenuText можно по-русски указать стандартные значения Zoom. В качестве разделителя используется символ «|».

Собственно, настройка VSPrinter закончена. Удобно, что и ни говори.

 

При изменении размеров диалогового окна, хотелось бы, чтобы и объект VSPinter также изменял свои размеры. Сделать это легко:

 

Private Sub Form_Resize()

    On Error Resume Next

    vp.Move vp.Left, vp.Top, ScaleWidth - 2 * vp.Left, _

ScaleHeight - vp.Left - vp.Top

    Me.Label1.Width = Me.Width - 50

End Sub

 

Пришло время создать шаблон отчета. Для этого воспользуемся приложением vsrpt7.exe VSReport Desiner. Создадим новый файл: File-New. Затем следует нажать на кнопку New Report. Приложение запросит строку подключения к данным. Все это совпадает с тем, к чему подключен сам Grid или компонента AdoDc, если вы ее все еще используете. Пример на рисунке ниже:

 

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

 

 

 

 

Дальнейшая методика работы с Designer ничем не отличается от конструктора отчетов в Access. С небольшим отличиями. Свойства объектов все по-английски, это одно из самых главных неудобств и они достаточно сильно отличаются от Access, но постепенно привыкаешь.

Основным рабочим объектом отчета является DataField. Вы можете их располагать как в теле отчета, так и в колонтитулах листов. Привязка к данным осуществляется через свойство Text! Если к источнику данных вы подключились правильно, то при создании отчета запускается мастер, напоминающий аналогичный мастер в Access. Там просто нужно выбрать поля и стиль оформления. Потом можно легко подправить, но так проще создавать все поля данных. Как видно из рисунка – набор свойств у объекта весьма обширен. Большинство из них нам известны по VB и Access. Некоторые, например RTF, никогда не встречались. Описывать их все здесь я не имею возможности. Это явно выходит за рамки статьи. Но методика создания отчета понятна. В моем случае получился отчет, который я назвал «rpCDR». Шаблон отчета сохраняется в файле *.xml. В одном xml файле может быть несколько отчетов с уникальными именами.

 

 

 

Часто бывает проще импортировать похожий отчет из Access. Возможно вам будет первое время проще создавать отчеты в Access, а затем импортировать их. Чтобы выполнить импорт следует нажать на кнопку Import. На кнопке нарисован ключ – стандартная эмблема Access. Работает это средство на ура! Очень удобно. Шаблон отчета создан.

Его можно посмотреть, если щелкнуть кнопку Preview. Чтобы кнопка стала активной щелкните предварительно название отчета в подокне слева.

Для предварительно просмотра отчет будет подключен к данным и в соответствии с запросом SQL (или таблицей, если она является источником) покажет их.

При работе с шаблоном из приложения VB на vпотребуется подключить данный файл (*.xml) к программе, указать какой отчет показывать и подключить его уже к тем данным, с которыми у нас работает пользователь. Сделать это легко и ниже я покажу как это сделать. Report Designer можно закрыть.

 

 

 

 

Замечание. Report Designer зарубежная программа. Файл xml сохраняется с кодировкой Win 1252. Нам нужно Win-1251. Придется каждый раз вручную менять тэг кодировки. Сделать это легко при помощи Far manager. Не забывайте этого делать. Периодически делайте резервные копии xml файла для сложных отчетов. Были случаи потери русскоязычного оформления безвозвратно.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Теперь подключим отчет к Vb-проекту. Для начала добавим на форму объект VSReport.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

После этого в процедуру, которая должна будет у вас в программе передать данные на печать добавьте следующий код:

 

‘аналогия DoVents в Бейсике

VSR.DoEvents = True

   

      ‘указали путь к файлу xml и имя рабочего шаблона

    VSR.Load App.Path & "\templates.xml", "rpCDR"

     

      ‘ подключили к шаблону нужный нам Recordset

        VSR.DataSource.Recordset = rsa

     

      ‘  программно изменили текст в одном из полей в заголовке отчета

      VSR.Fields("F_sostoan").Text = “По состоянию на 2005 год”

 

      ‘ передали отчет на предварительный просмотр

        VSR.Render vp

 

Все! У меня этот код находится в процедуре Form_Activate. Так как предварительный просмотр открывается в новом окне.

 

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

 

Private Sub VP_StartDoc()

‘запретили доступ к меню на время формирования отчета

mnuFile.Enabled = False

DoEvents

показали надпись на форме

Me.Label1.Caption = "Формирование отчета..."

DoEvents

показали кнопку Отмена

Me.Command1.Visible = True

End Sub

 

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

 

Private Sub Command1_Click()

If VSR.IsBusy Then

    VSR.Cancel = True

End If

Unload Me

End Sub

 

Формирование отчета прерывается мгновенно!

Учитывая, что пользователь может попытаться закрыть окно отчета раньше, чем он сформируется, закрытие окна нужно выполнить корректным образом:

 

Private Sub Form_Unload(Cancel As Integer)

On Error Resume Next

 

If VSR.IsBusy Then

    VSR.Cancel = True

End If

 

End Sub

 

Чтобы после окончания формирования отчета пропали надпись «Формирование отчета» и кнопка «Отмена» следует запрограммировать другое событие:

 

Private Sub VP_EndDoc()

DoEvents

mnuFile.Enabled = True

DoEvents

Me.Label1.Caption = ""

DoEvents

Me.Command1.Visible = False

End Sub

 

Экспорт из VSReport

VSReport обладает встроенными средствами экспорта. Ниже предлагаю готовую процедуру экспорта:

 

Public Sub exExportPrint(ByVal VSR As VSReport, ByVal hw As Long, _

ByVal fr As Form)

Dim strFilename As String

 

On Error GoTo err

 

‘здесь нужно сделать запрос имени файла.

‘ я предположил, что файл уже известен

    strFilename = “export.pdf”

   

    If strFilename = "" Then Exit Sub

   

    If Dir(strFilename) <> "" Then

    If MsgBox("Файл " & strFilename & " уже существует. Переписать?", vbInformation + vbYesNo, "SQL Error") = vbYes Then

        Kill strFilename

    Else

        Exit Sub

    End If

End If

       

MousePointer = vbHourglass

DoEvents

         

       

Me.Label1.Caption = "Экспорт..."

Me.Command1.Visible = True

mnuFile.Enabled = False

       

подключаем данные, так как после

‘        VSR.Render vp

данные из VSR пропадают

 

            VSR.DataSource.Recordset = rsa

 

      

DoEvents

       

       

MousePointer = vbHourglass

       

собственно экспорт       

If InStr(1, strFilename, ".txt") > 0 Then

       VSR.RenderToFile strFilename, vsrText

ElseIf InStr(1, strFilename, ".html") > 0 Then

       VSR.RenderToFile strFilename, vsrHTML

ElseIf InStr(1, strFilename, ".pdf") > 0 Then

       VSR.RenderToFile strFilename, vsrPDF

End If

 

 

Me.Label1.Caption = ""

Me.Command1.Visible = False

mnuFile.Enabled = True

 

MousePointer = vbDefault

DoEvents

 

 

Exit Sub

 

err:

MousePointer = vbDefault

Me.Label1.Caption = ""

Me.Command1.Visible = False

     

 

End Sub

 

Как видно из кода я предлагаю выполнять экспорт в трех форматах: TXT, HTML, PDF. Сразу предупреждаю, что на любом этапе экспорта данных может возникнуть проблема с кодировкой символов.

Резюме

Все ли мы теперь рассмотрели? Боюсь, что все равно нет! Очень многие вопросы остались без внимания. Я предполагаю, что по мере работы и накопления опыта вы все свои проблемы решите сами, а данная статься является только определенным толчком на курс в нужном направлении.

 

 

Воронеж, июнь 2005 года

 

 
     

   
   
     
  VBNet рекомендует