Visual Basic. Программирование на Visual Basic

..........................................................................................................................

[ Главная ] [ Статьи ] [ Для новичков ] [ Примеры ] [ Программы ] [ Microsoft Agent 2.0 ] [ Пособие ] [ Уроки ] [ Разное ]
..........................................................................................................................


Диалоговые панели Microsoft Common Dialog Control

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

Во первых, Common Dialog - действительно элемент управления, а нам это как раз и не нужно. Он должен находится в каком ни будь контейнере, например в форме, а часто бывает необходимо использовать его в программе без форм.  Но мои основные претензии в том, что его архитектура... Как бы выразится по мягче... Отсутствует напрочь. Его разработали в те далёкие времена, когда элементы управления были единственным средством, обеспечивающим повторное использование, и его интерфейс представляет собой мешанину свойств. Сейчас компоненты которым не нужны визуальный интерфейс и события, можно поставлять в гораздо более удобной форме открытых классов.

    Опять же у счастью, Visual Basic включает в себя не визуальный компонент Microsoft Dialog Automation Objects (DLGOBJS.DLL). Забудьте об элементах управления. Теперь можно создавать диалоговые объекты и вызывать их методы и свойства без всяких форм. Я был приятно удивлен обнаружив этот компонент в каталоге \Tool\Unsupprt на установочном диске Visual Basic. Им намного проще пользоваться чем элементом управления Common Dialog. Но увы, когда говорят "Не поддерживается", значит так оно и есть. Я нашел у него одно серьёзное ограничение, которое смог обойти, и несколько небольших которые обойти не смог. Надеюсь, что в следующей версии Visual Basic диалоговые объекты станут "поддерживаться" и будут интегрированы в библиотеку самого языка.

    А тем временем... элемент управления. Стоп!!! Какой элемент управления?! Не нужен нам этот ужасный Common Dialog!

Реализация стандартных диалоговых окон: Windows плюс Basic.

    Сравним несколько подходов к стандартным диалоговым окнам. Начнём с одного из самых простых - предназначенного для выбора цвета - и рассмотрим способы вывода его на экран.

    Первый - традиционный приём с использованием Common Dialog. Здесь предполагается, что элемент управления расположен на Form, UserControl или PropertyPage:

Function OptionColor(Optional ByVal clr As Long = vbBlack) As Long
With dlgColor
    ' в VB нет константы CC_SOLIDCOLOR, но всё работает
    .Flags = cdlCCRGBInit Or CC_SOLIDCOLOR
    ' Убедимся, что это RGB формат
    .Color = TranslateColor(clr)
    .hWnd = hWnd
    ' Узнаем об отмене, перехватывая ошибку
    .CancelError = True
    On Error Resume Next
    .ShowColor
    ' Возвращаем цвет, всё ли прошло удачно
    If Err Then
        OptionColor = clr
    Else
        OptionColor = .Color
    End If
End With
End Function

    Этот код не без шероховатостей. Управление поведением требует записи в свойство Flags определённой константы, а VB представляет н все константы. Хорошо когда знаешь что этот элемент управления лишь очень тонкая оболочка API - функций и распознаёт любые константы, какие только упомянуты в документации Windows API. Обработка кнопки Cancel организована просто отвратительно, но раньше было ещё хуже. Кто работал с более ранними версиями знает, о чём я говорю, а кто не работал, тому и знать не зачем.

    Версия с Dialog Automation Objects выглядит гораздо лучше:

Function OptionColor(Optional ByVal clr As Long = vbBlack) As Long
Dim choose As New ChooseColor
With choose
    ' Убедимся, что это RGB формат.
    .Color = TranslateColor(clr)
    .hWnd = hWnd
    ' Нет свойства позволяющие показать лишь основные цвета.
    ' Возвращаем цвет, всё ли прошло удачно.
    If .Show Then
        OptionColor = choose.Color
    Else
        OptionColor = clr
    End If
End With
End Function

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

    И наконец, моя версия:

Function OptionColor(Optional ByVal clr As Long = vbBlack) As Long
    ' Убедимся, что это RGB формат.
    clr = TranslateColor(clr)
    ' Возвращаем только основные цвета
    Call VBChooseColor(Color:=clr, AnyColor:=False, Owner:=hWnd)
    ' Возвращаем цвет, всё ли прошло удачно.
    OptionColor = clr
End Function

    В место свойств я применил именованные аргументы. Поскольку разработчик я, то я позаботился обо всех нужных мне параметрах, но Вам, возможно, понадобятся и другие. В этом примере мой код короче, но так бывает не всегда. Я, конечно, предпочёл бы более структурированный интерфейс доступа к диалоговым объектом, и, как только в Dialog Automation Objects появится всё, что мне надо, тут же выброшу свою версию.

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

Использование стандартных диалоговых окон.

    Если вы видели хоть одну функцию стандартного диалогового окна, значит, Вы видели все. Поэтому я расскажу только о создании самой сложной и самой нужной из них - функции, открывающей  диалоговое окно Open. Диалоговое окно Save As ему практически идентично, Font и Color сравнительно просты, а Print Setup и Page Setup, хоть они и несколько отличаются я не стану разбирать в деталях.

    Вызываем функцию VBGetOpenFileName.

Реализация оболочки - VBGetOpenFileName.

    И наконец, о самом трудном - о реализации оболочки для GetOpenFileName. Фокус в том, чтобы заполнить поля UDT (User Defended Type),ожидаемый GetOpenFileName, замаскировав этот процесс именованными аргументами. Все закрытые типы и объявления, используемые в открытых функциях VBGetOpenFileName, VBGetSaveFileName, VBChooseFont, VBPrintDlg и VBPageSetupDlg находятся в модуле COMDLG.

Структура OPENFILENAME

Private Type OPENFILENAME
    lStructSize As Long         
' Размер UDT
    hwndOwner As Long           
' Связанно с Owner
    hInstance As Long            ' Игнорируется (используется только для шаблонов)
    lpstrFilter As String       
' Связанно с Filter
    lpstrCustomFilter As String 
' Игнорируется
    nMaxCustFilter As Long      
' Игнорируется
    nFilterIndex As Long        
' Связанно с FilterIndex
    lpstrFile As String         
' Связанно с FileName
    nMaxFile As Long            
' Для внутренних целей
    lpstrFileTitle As String    
' Связанно с FileTitle
    nMaxFileTitle As Long       
' Для внутренних целей
    lpstrInitialDir As String   
' Связанно с InitDir
    lpstrTitle As String        
' Связанно с DlgTitle
    Flags As Long               
' Связанно с Flags
    nFileOffset As Integer      
' Игнорируется
    nFileExtension As Integer   
' Игнорируется
    lpstrDefExt As String       
' Связанно с DefaultExt
    lCustData As Long           
' Игнорируется (требуется для ловушек)
    lpfnHook As Long            
' Игнорируется (требуется для ловушек)
    lpTemplateName As String    
' Игнорируется (используется только для шаблонов)
End Type

    Это громоздкий UDT, но не все его поля нужны. Некоторые можно игнорировать, так как это просто выкрутасы. Вам например совершенно не зачем получать здесь позицию начала имени файла и его расширение в полном пути, когда это необходимо то проще самому разбить путь или вызвать GetFullPathName. Обрабатываемых полей вполне достаточно в 98% случаев.

В прежних версиях применение ловушек (hooks) и шаблонов (templates) для настройки стандартных диалоговых окон было уделом богов. Но оператор AdddressOf сделал эту задачу посильной и нам, крутым смертным. Удачи!

    Поля UDT служат и для входных, и для выходных данных. Перед вызовом инициализируются одни поля, после вызова считываются другие. Некоторые (к примеру Flags) работают в обе стороны. Наша функция может получить выходные данные через переменные передаваемые по ссылки. Конечно, переменные нужны не всегда, так как все аргументы, кроме одного, необязательные. Можно , например, опустить параметр Flags или передать его как константу. В последнем случае функция не заметит разницы и запишет результат во временную переменную, созданную Visual Basic. Ничего страшного не произойдёт, Вы просто не увидите результатов.

Обработка не обязательных параметров.

    Не обязательные параметры обрабатывает первая часть функции VBGetOpenFileName, присваивая всем пропущенным значения по умолчанию:

Function VBGetOpenFileName(FileName As String, _
                           Optional FileTitle As String, _
                           Optional FileMustExist As Boolean = True, _
                           Optional MultiSelect As Boolean = False, _
                           Optional ReadOnly As Boolean = False, _
                           Optional HideReadOnly As Boolean = False, _
                           Optional filter As String = "All (*.*)| *.*", _
                           Optional FilterIndex As Long = 1, _
                           Optional InitDir As String, _
                           Optional DlgTitle As String, _
                           Optional DefaultExt As String, _
                           Optional Owner As Long = -1, _
                           Optional Flags As Long = 0) As Boolean

    Dim opfile As OPENFILENAME, s As String, afFlags As Long
With opfile
    .lStructSize = Len(opfile)


   
' Задаём конкретные флаги и сбрасываем не поддерживаемые в VB
    .Flags = (-FileMustExist * OFN_FILEMUSTEXIST) Or _
             (-MultiSelect * OFN_ALLOWMULTISELECT) Or _
             (-ReadOnly * OFN_READONLY) Or _
             (-HideReadOnly * OFN_HIDEREADONLY) Or _
             (Flags And CLng(Not (OFN_ENABLEHOOK Or _
                                  OFN_ENABLETEMPLATE)))
    ' Владелиц может работать с окном
    If Owner <> -1 Then .hwndOwner = Owner
    ' InitDir - Строка с именем начальной директории
    .lpstrInitialDir = InitDir
    ' DefaultExt - расширение по умолчанию
    .lpstrDefExt = DefaultExt
    ' DlgTitle - заголовок окна
    .lpstrTitle = DlgTitle   

    Как видите параметров много, но лишь один обязательный - FileName, поскольку без него функция недееспособна.

Остальные параметры определяют входные и в нескольких случаях выходные данные. Так например HideReadOnly указывает, надо ли помещать в диалоговое окно флажок Open As Read-Only, а параметр ReadOnly задаёт начальное состояние этого флажка. На выходе из функции в параметр ReadOnly записывается значение, которое сообщает конечное состояние флажка Open As Read-Only. И вновь вы должны передать переменную. Если вы передадите константу, то поместить результат будет некуда.

    Параметры FileMustExist, MultiSelect, ReadOnly и HideReadOnly представляют наиболее часто используемые флаги. Сейчас их свыше десятка, а в будущих версиях Windows к ним могут добавится новые. Параметр Flags позволяет обрабатывать любые интересующие вас флаги. Иными словами, моя реализация Вас ни чем не сковывает.

Обработка фильтров, имён файлов и флагов.

    VBGetOpenFileName ожидает, что строки фильтров разделены вертикальной чертой ( | ) или двоеточием. Элемент управления Common Dialog допускает в качестве разделителя только первый символ, но я следую стилю компонента Dialog Automation Objects, где описание можно отделить от соответствующего ему расширения двоеточием:

Rich text files (*.rtf): *.rtf| .....

    Работает и формат Common Dialog. Но ни один из этих форматов не устраивает Windows, которая ждёт в качестве разделителя нулевой символ и два нулевых символа в конце списка. Приходится самому  преобразовывать строку:

    ' Чтобы создать фильтр в стиле Windows, заменяем | и : null-символами
    Dim ch As String, i As Long
    For i = 1 To Len(filter)
        ch = Mid$(filter, i, 1)
        If ch = "|" Or ch = ":" Then
            s = s & vbNullChar
        Else
            s = s & ch
        End If
    Next
    ' Помещаем в конец два null-символа
    s = s & vbNullChar & vbNullChar
    .lpstrFilter = s
    .nFilterIndex = FilterIndex

    Создав новую строку фильтра, Вы просто записываете её в поле UDT-переменной.

    Поля lpstrFile и lpstrFileTitle должны указывать на строковые буферы, размер которых достаточен для размещения в них любого возможного значения. Этот максимальный размер задаётся в полях nMaxFile и nMaxFileTitle. Вы записываете в них константы cMaxPath и cMaxFile, а строковые буфера заполняете строками этой длинны. Это позволяет избежать неприятностей из-за слишком малого буфера. Полученные строки преобразуем в формат Visual Basic:

    ' Создаём буфер максимальной длинны
    s = FileName & String$(cMaxPath - Len(FileName), 0)
    .lpstrFile = s
    .nMaxFile = cMaxPath
    s = FileTitle & String$(cMaxFile - Len(FileTitle), 0)
    .lpstrFileTitle = s
    .nMaxFileTitle = cMaxFile
    ' Остальные поля приравниваем к нулю

Остальное пусть делает Windows.

    К этому моменту мы уже поместили все необходимые входные данные в UDT-переменную и готовы вызвать API-функцию GetOpenFileName. Данные, которые диалоговое окно вернёт вам, будут представлены в формате Windows с нулевым символом на конце. Придётся слегка преобразовать их чтобы вернуть вызывающей программе в формате Visual Basic:

    If GetOpenFileName(opfile) Then
        VBGetOpenFileName = True
        FileName = MUtility.StrZToStr(.lpstrFile)
        FileTitle = MUtility.StrZToStr(.lpstrFileTitle)
        Flags = .Flags
        ' Возвращаем индекс фильтра
        FilterIndex = .nFilterIndex
        ' Подставляем, выбранный пользователем в диалоговом окне
        filter = FilterLookup(.lpstrFilter, FilterIndex)
        If (.Flags And OFN_READONLY) Then ReadOnly = True
    Else
        VBGetOpenFileName = False
        FileName = sEmpty
        FileTitle = sEmpty
        Flags = 0
        FilterIndex = -1
        filter = sEmpty
    End If
End With
End Function

    Строки в UDT-переменной - на самом деле буферы максимальной длинны. Windows запишет в буфер выходную строку, но не изменит размер буфера. Вы должны сами отсечь лишние символы и вернуть строку в ссылочную переменную, переданную пользователем. Если же пользователь такой переменной не передал, Visual Basic создаст временную переменную, но результат будет утерян.

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

   VBGetOpenFileName служит моделью для других функций, написанных на Visual Basic и поддерживающие стандартные диалоговые окна. Все они находятся в модуле COMDLG.BAS.



..........................................................................................................................

[ Главная ] [ Диски ] [ Книги ] [ Архив рассылки ] [ Архив новостей ] [ Готовые кусочки программ ] [ Карта сайта ]
..........................................................................................................................

По страницам сайта Visaul Progs
или Изучение Visual Basic
Рассылка 'По страницам сайта Visaul Progs' >>> Подпишись на рассылку - будешь получать новые статьи , примеры и много полезной информации из первых рук!!! >>>Если у вас есть статья которой нет на сайте
пришлите ее мне-------->
Послать статью
>>>Если вы хотите задать вопрос
пишите-------->
Мне нужна помощь


Рейтинг сайтов YandeG Rambler's Top100
Реклама:

...:::Design by Mystf0rse 2005-2010 year:::...