Пособие-самоучитель on-line "Visual Basic с нуля"
Глава 15. ActiveX: Объект общего диалога СommonDialog. Стандартные окна Open, Save as, Font, Color, и Print.
Скачать исходник программы StandWin
Дата создания 23.02.2005 {Автор 4us}

Практически каждая программа должна дать возможность пользователю загружать и сохранять файлы. Конечно, для этих целей можно использовать объекты Drive, Dir и File, об их использовании мы уже говорили в Главе 6. Однако, если ты заметил, большинство программ используют стандартные виндусовые окна, придающие программному продукту солидный вид. Имеем такую возможность и мы. Использование стандартных диалоговых окон проще и удобнее с точки зрения написания кода. Кроме того они позволяют пользователю выбирать цвет, шрифт, вводить свои установки для принтера и использовать Help (последнее мы опустим, так это сейчас не актуально).

Стандартные окна общего диалога CommonDialog

В Главе 12 мы уже использовали два элемента ActiveX - ProgressBar и Slider, добавив в панель инструментов средство Microsoft Windows Common Controls 6.0. Сейчас то же самое надо сделать с элементом ActiveX - Microsoft Common Dialog Control 6.0. Если ты не помнишь, как это делать, посмотри Главу 12, там все подробно расписано. После того, как ты отметишь галочкой (Проект - Компоненты - Microsoft Common Dialog Control 6.0) этот компонент и щелкнешь OK, у тебя в панели инструментов появится новый значок "CommonDialog".
Этот компонент позволяет нам использовать отдельно стандартные окна Windows
- открытия файлов (Open) с помощью метода ShowOpen
- сохранения файлов (Save As) c помощью метода ShowSave
- менять установки принтера (Printer) c помощью метода ShowPrinter
- выбор шрифтов и стилей (Font) c помощью метода ShowFont
- выбор цвета из палитры (Color) c помощью метода ShowColor

Рассмотрим все окна на примере маленькой программки DialogWindows. Создадим новый exe-проект, положим на форму наш новый компонент CommonDialog1, текстбокс Text1 и пять командных кнопок Command, в каждой из которых будет процедура для своего стандартного окна. Мы будем работать с текстовыми файлами. Итак первое окно Open в процедуре кнопки Command1:

Диалоговое окно открытия файлов Open (метод ShowOpen)

Чтобы открыть окно для открытия файлов, мы в процедуре командной кнопки Command1 пишем одну единственную строчку:

CommonDialog1.ShowOpen

Раз и готово. Теперь, запуская программу и нажимая кнопку Command1, мы тем самым открываем диалоговое окно Open. И можем радостно шоркаться по дискам, папкам и файлам. Можно даже папку создать. Но, никакого файла это окно не откроет, сколько на OK не жми. Поскольку для работы окна нам надо написать программный код. Подумаем, какой программный код мы можем написать.
Во-первых, нам надо, чтобы окно называлось так как мы хотим, а не как хочет Bill Gates. Для того, чтобы поменять название диалогового окна на твое собственное, можно использовать свойство .DialogTitle приблизительно так:
CommonDialog1.DialogTitle = "Загрузить текстовой файл"
Во-вторых, сейчас наше диалоговое окно показывает все файлы. А поскольку юзер имеет привычку тыкать во все файлы без разбора, надо ему эту возможность ограничить. Чтобы юзер случайно не пытался загружать графический файл в текстовое поле или наоборот, надо заставить наше окно показывать только те файлы, которые может корректно загружать наша программа. Для этого у нашего объекта CommandDialog есть свойство .Filter, которое и позволяет нам показывать в диалоговом окне файлы, соответствующие шаблону. Поскольку работать мы будем с текстовыми файлами, то и открывать надо именно их. Текстовые файлы имеют раширение, к примеру, .txt, или dat. Поэтому перед открытием окна мы должны написать например такое:
CommonDialog1.Filter = "Текстовые файлы (*.txt)|*.txt"
Как видишь, опции фильтра - строковые данные, взятые в кавычки. Они разделены вертикальной черточкой. Слева от черты - то что выведется в окне, т.е. то, что будет видеть юзер в диалоговом окне, а справа - указан шаблон, по которому происходит фильтрация. Указать здесь можно, что угодно, вплоть до конкретного файла.
Теперь, допустим, мы хотим показать несколько типов файлов. Добавим, например, в наш фильтр файлы .dat. Тогда слева от вертикальной черты, дописываем как угодно информацию о новом формате, а справа, через точку с запятой дописываем новый шаблон:
CommonDialog1.Filter = "Текстовые файлы (*.txt) Файлы данных (*.dat)|*.txt;*.dat"
Теперь наше окно будет показывать оба типа файлов одновременно. И в выпадающем списке в диалоговом окне оба типа файлов будут на одной строчке.
А вот если мы хотим предоставить юзеру право выбора, какие файлы ему просматривать, .dat или .txt, и чтобы они находились на разных строчках в выпадающем окне, надо написать следующее:
CommonDialog1.Filter = "Текстовые файлы (*.txt)|*.txt|Файлы данных(*.dat)|*.dat"
Здесь мы пишем пояснение для первого типа файла, отделяем вертикальной чертой, пишем первый шаблон, снова отделяем вертикальной чертой, затем пишем пояснение для второго типа, отделяем вертикальной чертой, пишем второй шаблон. В это случае, наши типы файлов в диалоговом окне будут в выпадающем списке на разных строчках и пользователь сможет выбрать, какие файлы будет показывать диалоговое окно, .dat или .txt.
В-третьих, нам надо передать полный путь и имя файла в инструкцию Open (хочешь не хочешь, а файл придется открывать обычным оператором Open). Нет ничего проще. Eсть свойство .FileName, которое принимает значение полного пути + имя файла, то есть все, что нужно для оператора Open:
Open CommonDialog1.FileName For Input As #F
Далее организуем цикл Do...Loop (объявив необходимые переменные) и считываем тeкстовой файл в Text1. Вот что у нас должно приблизительно получиться в итоге:

Option Explicit
Private Sub Command1_Click()
Dim F As Long
Dim Stroka As String
Text1.Text = ""
CommonDialog1.Filter = "Текстовые файлы (*.txt) Файлы данных (*.dat)|*.txt;*.dat"
CommonDialog1.ShowOpen
F = FreeFile
Text2.Text = CommonDialog1.FileName
Open CommonDialog1.FileName For Input As #F
Do While Not EOF(F)
Line Input #F, Stroka
Text1.Text = Text1.Text & Stroka
Loop
Close
#F

End Sub


Радостно потирая руки запускаем нашу фигню. Вроде все работает. Однако давайте внимательно проанализируем код и работу диалогового окна.
И в результате исследования мы прийдем к неутешительному выводу: наша программа корректно работает только в одном-единственном случае - когда файл выбран (поле File Name (Имя файла) - не пустое) и юзер нажал кнопку Open (то же самое, что и при двойном щелчке по имени файла). То есть, когда все случилось удачно, путь и имя файла есть, после закрытия окна они передаются в оператор Open и все происходит как по маслу.

Что же происходит в менее благополучных случаях:
- юзер файл не выбрал, ну забыл, (поле File Name (Имя файла) - пустое), но тем не менее жмакает кнопку Open. Тогда диалоговое окно просто не закрывается, а ждет чего-нибудь более рационального. Наша программа ни взад, ни вперед. Тогда юзер плюет на открытие файла, решает, что лучше написать в Text1 новый текст, чем грузить старый файл (тем более, что не задалось) и естественно жмет кнопку Cansel. А поскольку у нас нет имени файла, диалоговое окно передает в оператор Open пустую строку. Такой файл Open открыть не может. Возникает ошибка "Run-time error 75. Ошибка доступа пути или файла". Фатал эрор. Такого быть не должно.

Поэтому нам надо первым делом отследить нажатие пользователем кнопки Cansel. Для этого у нашего объекта CommonDialog есть свойство .CanselError. Его можно устанавливать и в окне свойств, но мы сделаем это программно. Работает оно так. Если значение свойства .CanselError равно True, то при щелчке мышью по кнопке Cansel в диалоговом окне всегда возникает ошибка. По умолчанию свойство .СanselError=False, т.е ошибка не возникает. То есть, иными словами, если мы поставим свойство .CanselError в состояние True, то показателем того, что юзер нажал кнопку Cansel является именно возникновение ошибки. Нам остается ее только отследить и грамотно обработать.
Без установки и обработки этой ошибки отследить нажатие кнопок Cansel и OK довольно затруднительно. Правда иногда в примерах или если программой пользуется сам разработчик, который заведомо не будет хулиганить и нажимать все кнопки правильно, для упрощения кода применяется такой прием. В большинстве случаев, при нормальном открытии файлов свойство .Flags возвращает значение 1024, а при нажатии кнопки Cansel - 0. Исходя из этой сомнительной посылки, можно взять операторы открытия файла в такой оператор If:

If CommonDialog1.Flags = 1024 Then
Операторы открытия и считывания файла
End If


Тогда ошибок открытия файла не возникнет и в принципе все будет работать, кроме отдельных случаев, типа, когда наш предприимчивый юзер не удовлетворился выбранным именем файла и начал редактировать это имя в поле File Name. В результате этого может получится так, что в поле File Name будет записано имя несуществующего файла. Тогда свойство .Flags хоть и возвратит число 1024, но оператор Open открыть несуществующий файл не сможет, и все равно возникнет ошибка. Да мало-ли чего может натворить юзер. Поэтому здесь мы этот рабочий прием использовать не будем.
Вообще свойство .Flags - очень большое и важное свойство. С его помощью можно управлять внешним видом окна, его поведением и собственно работой. Для управления работой разных диалоговых окон существует множество констант, каждая из которых задает окну какое-либо свойство. Эти константы можно посмотреть в меню VB - кнопка "Проводник объектов". Константы могут быть использованы либо в виде константы (что более понятно в коде), либо как число - десятичное или шестнадцатеричное. Если надо использовать несколько констант, то они записываются в одном операторе через логическое ИЛИ - Or. Например, чтобы диалоговое окно контролировало, выбрал ли юзер файл из существующих и одновременно контролировало правильность пути до файла, надо написать следующее:
CommonDialog1.Flags = cdlOFNFileMustExist Or cdlOFNPathMustExist
Все константы мы перебирать не будем, а будем говорить лишь о самых необходимых в нашем нелегком деле. Вот некоторые, применительно к окнам Open и Save As.
cdlCancel = 32755 (&H7FF3) - выбрана кнопка Cansel
cdlInvalidFileName = 20477 (&H4FFD) - имя файла неправильно
cdlOFNFileMustExist = 4096 (&H1000) - позволяет выбирать файлы только из существующих
cdlOFNNoChangeDir = 8 - устанавливает текущую директорию для последующих вызовов
cdlOFNOverwritePrompt = 2 - спрашивает пользователя, если файл уже существует
cdlOFNPathMustExist = 2048 (&H800) - юзер может вводить только правильные пути для файла


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

Option Explicit
'Объявим переменную для номера открываемого файла
Dim F As Long
Private Sub Command1_Click()
'объявим переменную, в которую мы будем считавать строки из файла
Dim Stroka As String
'напишем оператор для отслеживания ошибки и в случае возникновения переходим по метке Cansel
On Error GoTo Cansel
'установим, чтобы возникала ошибка при щелчке по кнопке Cansel
CommonDialog1.CancelError = True
'установим фильтры для текстовых файлов
CommonDialog1.Filter = "Текстовые файлы (*.txt) Файлы данных (*.dat)|*.txt;*.dat"
'установим свойство .Flags, которое не позволяет открывать несуществующие файлы
CommonDialog1.Flags = cdlOFNFileMustExist
'напишем наше собственное название диалогового окна

CommonDialog1.DialogTitle = "Загрузить текстовой файл"
'а вот теперь окно можно и открыть
CommonDialog1.ShowOpen

'открываем файл и считываем его содержимое в Text1
F = FreeFile
Text1.Text = ""
Open CommonDialog1.FileName For Input As #F
Do While Not EOF(F)
Line Input #F, Stroka
Text1.Text = Text1.Text & Stroka
Loop
Close
#F
'файл считан
'выход из процедуры до ее завершения, чтобы не обрабатывалась ошибка, если ее нет

Exit Sub

Cansel:
'анализируем номер ошибки. Если нажата Cansel, выходим из процедуры
If Err.Number = cdlCancel Then
Exit Sub
Else

'в противном случае, если возникла какая-то другая ошибка, выводим ее описание
MsgBox Err.Description
End If
' заканчиваем процедуру
End Sub

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

Диалоговое окно сохранения файлов Save As (метод ShowSave)

В кнопке Command2 пишем код для сохранения текста в файле из Text1.
Код у нас будет совершенно такой же, как и для окна Open, ну конечно файл надо открывать для записи, а не для чтения. Наш прием для отслеживания кнопки Cansel мы будем использовать во всех окнах. Кроме того, поскольку мы записываем файл, то теперь нам нет необходимости выбирать файл из существующих. Зато есть необходимость предупреждать юзера, что он можеть записать новый файл, уничтожив старый (при совпадении их имен). Для предотвращения этого ужаса присвоим свойству .Flags такую константу
CommonDialog1.Flags = cdlOFNOverwritePrompt
Тогда при попытке записать файл под уже существующим именем, Windows будет выводить предупреждающее окно. И наш код будет выглядеть следующим образом:

Private Sub Command2_Click()
F = FreeFile
On Error GoTo Cansel
CommonDialog1.CancelError = True
CommonDialog1.Filter = "Текстовые файлы (*.txt) Файлы данных (*.dat)|*.txt;*.dat"
CommonDialog1.Flags = cdlOFNOverwritePrompt
CommonDialog1.DialogTitle = "Сохранить текстовой файл как..."
CommonDialog1.ShowSave
Open CommonDialog1.FileName For Output As #F
Print #F, Text1.Text
Close #F
Exit Sub
Cansel1:
If Err.Number = cdlCancel Then
Exit Sub
Else

MsgBox Err.Description
End If
End Sub

Диалоговое окно выбора цвета Color (метод ShowColor)

Ну с этим окном все предельно просто. Мы просто открываем окно (используя приемы описанные выше). У CommonDialog есть свойство .Color, которое и определяет выбранный пользователем цвет. Нам надо только его передать какому-нибудь объекту нашего проекта и готово. Например, чтобы изменить цвет фона нашей формы пишем:
Form1.BackColor = CommonDialog1.Color
Конечно и здесь есть кое-какие значения свойства .Flags. Их всего четыре:
cdlCCFullOpen = 2 'выводит полное диалоговое окно с настройкой цветов
cdlCCPreventFullOpen = 4 'выводит часть окна с цветами по-умолчанию и делает недоступной кнопку Define Custom Colors (определение настраиваемых цветов)
cdlCCHelpButton = 8 'делает видимой кнопку Help
cdlCCRGBInit = 1 'выводит часть окна с цветами по-умолчанию но кнопка Define Custom Colors (определение настраиваемых цветов) доступна для расширения окна


Напишем код, который будет менять цвет фона в Text1:

Private Sub Command3_Click()
CommonDialog1.Flags = cdlCCRGBInit
On Error GoTo Cansel
CommonDialog1.CancelError = True
CommonDialog1.ShowColor
Text1.BackColor = CommonDialog1.Color
Exit Sub
Cansel:
If Err.Number = cdlCancel Then
Exit Sub
Else

MsgBox Err.Description
End If
End Sub


Хочу заметить, что поменять название этого окна, как мы делали с окнами Open и Save As с помощью свойства .DialogTitle не удасться.

Диалоговое окно изменения вида шрифта Font (метод ShowFont)

Одним оператором CommonDialog1.ShowFont, как мы делали раньше, открыть окно шрифтов не удастся. Это лишь вызовет ошибку 24574 - "No font exist" - "Нет существующих шрифтов". Это происходит потому, что для окна необходимо определить, какие шрифты ему показывать - экранные, принтерные или оба. Это определяют параметры свойства .Flags:

CommonDialog1.Flags=cdlCFScreenFonts 'экранные шрифты
CommonDialog1.Flags=cdlCFPrinterFonts 'принтерные шрифты
CommonDialog1.Flags=cdlCFBoth 'оба вида шрифтов

Кроме того полезны для использования следующие константы свойства .Flags:
cdlCFForceFontExist= 65536 (&H10000) - вызов сообщения об ошибке при выборе несуществующего шрифта или стиля
cdCFLimitSize = 8192 (&H2000) - ограничивает размер показываемых шрифтов с размера, заданного свойством .Min по размер, заданный свойством .Max.
cdlCFEffects = 256 (&H100) - отображает дополнительные параметры форматирования - подчеркивание, перечеркивание цвет.

Заданные в диалоговом окне параметры должны быть обработаны программой. Для этого имя шрифта, его размер, стиль, цвет передаются из CommonDialog1 соответствующими свойствами в свойства соответствующего объекта. В нашем примере мы будем менять шрифт нашего текста в Text1:

Private Sub Command4_Click()
On Error GoTo Cansel
CommonDialog1.CancelError = True
'Установим флаги экранного шрифта, дополнительных параметров форматирования
' и включим ограничение размера шрифтов

CommonDialog1.Flags = cdlCFScreenFonts Or cdlCFEffects Or cdlCFLimitSize
'установим вывод шрифтов размером от 10 до 16
CommonDialog1.Min = 10
CommonDialog1.Max = 16
'открываем окно
CommonDialog1.ShowFont
'передаем свойства окна свойствам Text1
Text1.Font.Name = CommonDialog1.FontName
Text1.Font.Size = CommonDialog1.FontSize
Text1.Font.Bold = CommonDialog1.FontBold
Text1.Font.Italic = CommonDialog1.FontItalic
Text1.Font.Strikethrough = CommonDialog1.FontStrikethru
Text1.Font.Underline = CommonDialog1.FontUnderline
Text1.ForeColor = CommonDialog1.Color
Exit Sub
Cansel:
If Err.Number = cdlCancel Then
Exit Sub
Else

MsgBox Err.Description
End If
End Sub

Диалоговое окно установки параметров принтера и печати Print (метод ShowPrinter)

Это окно открывается командой :
CommonDialog1.ShowPrinter
С помощью этого окна юзер может выбрать принтер, номера печатаемых страниц, а также число копий.
Некоторые полезные константы для свойства .Flags:

cdlPDPageNums = 2 - устанавливает или возвращает статус радиокнопки Pages (Делает ее доступной при использовании свойств .Min и .Max)
cdlPDCollate = 16 (&H10) - устанавливает или возвращает статус галочки Collate
cdlPDNoSelection = 4 - выключает радиокнопку Selection
cdlPDSelection = 1 - устанавливает или возвращает статус радиокнопки Selection
cdlPDAllPages = 0 - устанавливает или возвращает статус радиокнопки All Pages
cdlPDPrintSetup = 64 (&H40) - выводит на экран SetUp принтера раньше, чем диалоговое окно


Чтобы управлять ориентацией листа (горизонтальная печать или вертикальная) , надо явно передать константу ориентации из диалогового окна окна в объект Printer.
Printer.Orientation = CommonDialog1.Orientation

Если свойству .Flags задать значение cdlPDPageNums, а затем установить свойства .Min и .Max (Минимальное и максимальное количество печатаемых страниц, то в диалоговом окне станет доступно поле Pages. По умодчанию в него можно задать номера страниц с помощью свойств .FromPage (начальная страница печати) и .ToPage (конечная страница печати):

CommonDialog1.Flags = cdlPDPageNums
CommonDialog1.Min = 0 ' минимальное количество страниц
CommonDialog1.Max = 10 ' максимальное количество страниц
CommonDialog1.FromPage = 1 'начальная страница печати
CommonDialog1.ToPage = 5 'конечная страница печати
Можно также по умолчанию задать количество копий с помощью свойства .Copies:
CommonDialog1.Copies = 3

Вот приблизительно, как может выглядеть код для печати содержимого нашего Text1:

Private Sub Command5_Click()
On Error GoTo Cansel
CommonDialog1.CancelError = True
CommonDialog1.Flags = cdlPDPageNums Or cdlPDCollate Or cdlPDNoSelection Or cdlPDAllPages
CommonDialog1.Copies = 2 'количество копий по умолчанию
CommonDialog1.Min = 0
CommonDialog1.Max = 10
CommonDialog1.FromPage = 1
CommonDialog1.ToPage = 2
CommonDialog1.ShowPrinter

Printer.Orientation = CommonDialog1.Orientation 'ориентация листа
Printer.Copies = CommonDialog1.Copies 'количество копий
Printer.Print Text1.Text
Printer.EndDoc
Exit Sub
Cansel:
If Err.Number = cdlCancel Then
Exit Sub
Else

MsgBox Err.Description
End If
End Sub

А в принципе, с работа с принтером - дело непростое, попробуй, исходя из этого поэкспериментировать со свойством .Flags и прочими делами.

Вместо методов .ShowOpen, .ShowSave и т. д. можно использовать свойство .Action. Это свойство не соответствует по своим признакам свойствам, так как заставляет объект выполнять действие (открывать диалоговое окно). Таким образом, метод .ShowOpen аналогичен свойству .Action:

оператору CommonDialog1.ShowOpen соответствует оператор CommonDialog1.Action = 1
оператору CommonDialog1.ShowSave соответствует оператор CommonDialog1.Action = 2
оператору CommonDialog1.ShowColor соответствует оператор CommonDialog1.Action = 3
оператору CommonDialog1.ShowFont соответствует оператор CommonDialog1.Action = 4
оператору CommonDialog1.ShowPrinter соответствует оператор CommonDialog1.Action = 5

Скачать исходник примера можно вверху страницы.

Copyright © 2005 4us




Сайт создан в системе uCoz