Исходники
Статьи
Языки программирования
.NET Delphi Visual C++ Borland C++ Builder C/С++ и C# Базы Данных MySQL MSSQL Oracle PostgreSQL Interbase VisualFoxPro Веб-Мастеру PHP HTML Perl Java JavaScript Протоколы AJAX Технология Ajax Освоение Ajax Сети Беспроводные сети Локальные сети Сети хранения данных TCP/IP xDSL ATM Операционные системы Windows Linux Wap Книги и учебники
Скрипты
Магазин программиста
|
Класс-обертка для функции SHBrowseForFolderФункция обратного вызова BrowseCallbackProc Передача указателя на управляемый объект в неуправляемый кодКласс ValidateFailedEventArgs Перечисление BrowseDialogFlags Пример использования
ПредисловиеЕсли Вы уже имели возможность писать приложения с использованием Microsoft Windows Forms, то наверняка убедились, что это просто. Тем более, что эта библиотека содержит множество компонентов. Но парни из Microsoft не упустят случая испортить нам жизнь. И им снова это удалось. В Windows Forms, среди прочего, не хватает одной очень простой, но нужной вещи, а именно: всем хорошо знакомого диалога выбора папок. Точнее говоря, нужный класс есть (System::Windows::Forms::Design::FolderNameEditor::FolderBrowser), но нам с вами он недоступен. Почему недоступен? Потому что объявлен со спецификатором доступа private, то есть использоваться может только в содержащем его классе (FolderBrowser - это вложенный класс). Но этого парням из Microsoft показалось мало, и они "документировали" класс FolderBrowser, вот что написано в MSDN: "Этот тип поддерживает инфраструктуру .NET Framework и не предназначен для прямого использования в Вашем коде". Кстати, в System.Design.dll определено несколько пространств имен для "внутреннего употребления" Microsoft, там можно найти практически все функции Win32 API, и множество других, в том числе и SHBrowseForFolder. Как вам такая забота о пользователях? Но оставим Microsoft в покое, это далеко не первый случай подобного рода в их практике. К счастью, в нашем распоряжении все-таки есть достаточно средств для самостоятельной реализации нужных классов. В данной статье я хочу показать, как это можно сделать. Класс написан с использованием Managed Extensions for C++. В принципе, можно было бы использовать технологию PlatformInvoke и просто вызвать функцию SHBrowseForFolder или даже воспользоваться объектной моделью Internet Explorer. Но в этом случае было бы очень сложно или вообще невозможно написать свой обработчик сообщений, вызываемый через callback-механизмы. Использование же MC++ позволило достаточно просто совместить управляемый и неуправляемый код и скрыть детали реализации от пользователя. Ниже дано краткое описание функций и типов, используемых при работе BrowseFolderDialog, а также приведен пример вызова диалога из клиента на C#.
Функция обратного вызова BrowseCallbackProcЭта функция, несмотря на малое количество кода, выполняет очень важную роль – она позволяет связать код операционной системы (неуправляемый код низкого уровня) с нужным обработчиком в управляемом классе. Чтобы показать, как это сделано, я приведу весь код функции и прокомментирую его.
Прежде всего стоит сказать, что эта функция не является членом класса BrowseFolderDialog, так как методы управляемых классов (как статические, так и нестатические) имеют соглашение о вызовах clrcall, а нам нужно использовать stdcall. Проблема здесь в том, что мы не можем изменять соглашения о вызовах для методов управляемых классов, поэтому функция BrowseCallbackProc вынесена из класса. Одну проблему мы решили, но возникает другой вопрос: а как же получить объект класса, для которого была вызвана эта функция? К счастью, это просто. Параметр lpData содержит нечто, что может быть преобразовано к указателю на нужный нам объект. Делается такое преобразование в два этапа:
Теперь, когда мы имеем указатель на объект управляемого типа, мы можем смело использовать его, как нам заблагорассудится. В данном случае я просто вызываю метод BrowseFolderDialog::BrowseCallback и передаю ему полученные от ОС аргументы. Это действие производится в третьей строке.
Передача указателя на управляемый объект в неуправляемый кодС обращением к управляемым объектам из функций обратного вызова все ясно, но как же передать указатель на нужный нам объект в неуправляемый код? В нашем случае этот вопрос можно перефразировать так: как правильно заполнить соответствующие поля структуры BROWSEINFO? Я думаю, что внимательный читатель уже догадался (или посмотрел в MSDN): опять же с помощью класса GCHandle. Посмотрим на код, приведенный ниже:
Это фрагмент кода метода BrowseFolderDialog::ShowDialog. Именно здесь происходит связывание параметров для функции обратного вызова и нашего объекта. Метод GCHandle::Alloc возвращает некий описатель нашего объекта, который может быть передан в неуправляемый код, и запрещает сборщику мусора удалять объект. Поэтому необходимо по окончании работы с описателем не забыть вызвать метод Free. Вот так просто в функции, ожидающие параметры типа LPARAM передавать указатели на управляемые объекты. Я думаю, что в коде метода BrowseFolderDialog::ShowDialog есть еще один интересный момент: передача строк из управляемого в неуправляемый код. Делается это вызовом функции PtrToStringChars, которая возвращает указатель на строку Unicode-символов. Содержимое этой строки не может меняться. В свою очередь, преобразование из C-строки в управляемую строку делается с помощью методов Marshal::PtrToStringXXX.
Описание класса BrowseFolderDialogКонструкторыBrowseFolderDialogDefault-конструктор, инициализирующий поля начальными значениями.
МетодыShowDialogИспользуется для вывода диалогового окна на экран.
Возвращаемые значения и параметры метода показаны в таблицах ниже.
СвойстваFolderNameСвойство, представляющее выбранную пользователем папку. Действительно только после завершения метода ShowDialog с результатом DialogResult::OK. Это свойство доступно только для чтения.
InitialFolderNameСвойство, задающее первоначально выбранную папку. Можно не задавать.
DescriptionСвойство, задающее некоторое описание, которое может служить в качестве инструкции для пользователя. В диалоге появляется над списком папок. Можно не задавать.
RootСвойство, задающее корневую папку, с которой начинается просмотр. Можно не задавать.
FlagsСвойство, определяющее внешний вид и поведение диалогового окна. Можно не задавать. Значения являются комбинацией членов перечисления BrowseDialogFlags.
СобытияValidateFailedОбработчик события, генерируемого при вводе недопустимого имени папки. Событие генерируется при нажатии пользователем кнопки OK. Вызывается, только если установлены флаги BrowseDialogFlags.EditBox и BrowseDialogFlags.Validate. Если обработчик не определен, ошибка игнорируется и метод ShowDialog возвращает DialogResult::Cancel.
Класс ValidateFailedEventArgsКласс введен для передачи аргументов обработчику события ValidateFailed. Наследуется от класса CancelEventArgs.
КонструкторыValidateFailedEventArgs
СвойстваInvalidNameСвойство, задающее имя, вызвавшее ошибку. Только для чтения.
CancelСвойство, определяющее реакцию диалогового окна на ошибку, может меняться. Наследуется от класса CancelEventArgs. Если установлено в True – окно не будет закрыто.
Перечисление BrowseDialogFlagsЭто перечисление введено для удобства использования в программах на языках C# и Visual Basic.NET.
ДелегатыДелегат введен для того, чтобы пользователь мог определять свои правила обработки неверных имен папок.
Пример использованияНиже приведен пример вызова методов класса BrowseFolderDialog из клиента, написанного на C#.
P.S.Код писался для .NET Framework версии 1.0, в которой не было встроенного класса System.Windows.Forms.FolderBrowserDialog
|
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Рейтинги
|