Студопедия

КАТЕГОРИИ:

АвтоАвтоматизацияАрхитектураАстрономияАудитБиологияБухгалтерияВоенное делоГенетикаГеографияГеологияГосударствоДомЖурналистика и СМИИзобретательствоИностранные языкиИнформатикаИскусствоИсторияКомпьютерыКулинарияКультураЛексикологияЛитератураЛогикаМаркетингМатематикаМашиностроениеМедицинаМенеджментМеталлы и СваркаМеханикаМузыкаНаселениеОбразованиеОхрана безопасности жизниОхрана ТрудаПедагогикаПолитикаПравоПриборостроениеПрограммированиеПроизводствоПромышленностьПсихологияРадиоРегилияСвязьСоциологияСпортСтандартизацияСтроительствоТехнологииТорговляТуризмФизикаФизиологияФилософияФинансыХимияХозяйствоЦеннообразованиеЧерчениеЭкологияЭконометрикаЭкономикаЭлектроникаЮриспунденкция

Диалоги по выбору имени файла и каталога




Класс CFileDialog библиотеки MFC предназначен для вызова стандартного диалога Windows по открытию или сохранению файла. Вот формат его использования:

CFileDialog dlg(FALSE,"dat","*.dat"); /*FALSE - сохранение, TRUE – открытие */

if(dlg.DoModal()==IDOK) /*вызов диалогового окна*/

{

  /*в этой точке программы функция dlg.GetPathName() вернет полное имя файла */

}

else        /*пользователь завершил диалог нажатием кнопки Cancel*/;

  

На самом деле конструктор класса CFileDialog имеет значительно больше параметров, описание которых см. в MSDN.

Конечно, класс CFileDialog можно использовать искусственным образом и для выбора каталога (придется заставить пользователя выбрать какой-нибудь файл в желаемом каталоге), но это некрасиво. Ниже приводится текст функции, несколько сложноватой (а что поделаешь), которая позволяет выбрать имя каталога и возвращает его как значение параметра DirName.

Функция возвращает значение true, если выбор каталога завершился без ошибки, и false в случае возникновения ошибки или при отказе пользователя от выбора каталога, т.е. при закрытии им диалогового окна с помощью кнопки «Отмена». Если пользователь не выбрал каталог и завершил диалог нажатием кнопки «ОК», то функция вернет значение true, но строка с именем каталога DirName будет пустой.

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

char DirName[MAX_PATH];// объявляем массив достаточного размера

if(GetDirNameDemo(DirName)) ; // используем каталог

   else ;// какая-то ошибка

Код функции выбора имени каталога.

bool GetDirNameDemo(char *DirName)

{

LPMALLOC pMalloc;

/* Получаем интерфейс pMalloc */

if (::SHGetMalloc(&pMalloc) == NOERROR)

{

   BROWSEINFO bi;

   LPITEMIDLIST pidl;

   // устанавливаем значения полей структуры bi - см. справку MSDN

   bi.hwndOwner = NULL;

   bi.pidlRoot = NULL;

   bi.pszDisplayName = DirName;

   bi.lpszTitle = _T("Выберите каталог");

   bi.ulFlags = BIF_RETURNFSANCESTORS | BIF_RETURNONLYFSDIRS;

   bi.lpfn = NULL;

   bi.lParam = 0;

   // В следующей строке вызывается функция выбора имени каталога

   if ((pidl = ::SHBrowseForFolder(&bi)) != NULL)

   {

       if (!::SHGetPathFromIDList(pidl, DirName)) return false;

       /* Освобождаем память для pidl, выделенную функцией

          SHGetPathFromIDList() */

       pMalloc->Free(pidl);

   }

   // Освобождаем интерфейс pMalloc

   pMalloc->Release();

   return true;

}

else return false;

}

 

Существует еще несколько функций оболочки (shell), которые предоставляют программисту различные сервисы. Имена этих функций начинаются с префикса SH. См. MSDN.



Обработка сообщений Windows

Достаточно подробно обработка сообщений (messages) Windows описана в MSDN (см. перевод соответствующего раздела в файле MMQ_Rus.doc), хотя и без примеров. Предполагается, что перед чтением этого раздела Вы ознакомитесь с этим источником или с другими. В данном разделе я постарался привести конкретные рекомендации по практическому использованию механизма сообщений и привести примеры.

Обработка сообщений в рамках одного приложения

В рамках одного приложения можно использовать механизм сообщений для передачи управления и некоторой информации между следующими сущностями:

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

2. Между разными классами одного приложения.

 

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

Еще один случай, когда имеет смысл посылка сообщений в пределах одного класса. Например, нам необходимо, чтобы немодальное диалоговое окно уничтожалось, когда пользователь щелкнет в этом окне кнопку ОК. Здесь возникает проблема: как объект вид, создавший это окно, узнает, что пользователь щелкнул ОК? Диалоговое окно, в принципе, могло бы напрямую вызвать какую-либо функцию объекта вид, но тогда это окно оказалось бы привязанным к конкретному объекту вид. Более удачное решение – отправка диалоговым окном пользовательского сообщения объекту вид. Тогда, получив такое сообщение, объект вида может уничтожить диалоговое окно, но не сам объект диалоговое окно.

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

Для того чтобы Ваш класс мог комфортно обрабатывать сообщения, он должен быть производным от одного из следующих классов: CView, CWinApp, CDocument, CWnd или CFrameWnd. Теоретически можно использовать механизм сообщений и в классе, производном от CCmdTarget, но это неудобно и здесь этот случай рассматриваться не будет.

Очень кратко об обработке сообщений. Любой класс, производный от выше названных, имеет так называемую оконную процедуру, имеющую имя WindowProc(), которая перехватывает и обрабатывает все сообщения, передаваемые ей Windows. Она содержит так называемый цикл обработки сообщений, в котором они предварительно (или окончательно) обрабатываются с помощью соответствующих обработчиков. (функцию) и передает управление ему для выполнения конкретных действий. А как нам «вклиниться» в этот цикл, чтобы выполнить свою обработку? Можно несколькими способами:

· объявить свой обработчик сообщения, воспользовавшись вкладкой Messages в окне свойств (Properties) выбранного класса. Например, так мы создаем обработчик сообщения WM_LBUTTONDOWN от щелчка ЛКМ. Когда мы это сделаем, то тем самым вклинимся в цикл обработки сообщений до того, как это сообщение обработает оконная процедура. Ясно, что этот обработчик будет получать управление только в том случае, когда пользователь щелкнет ЛКМ;

· перекрыть виртуальную функцию OnWndMsg(), воспользовавшись вкладкой Overrides все того же окна свойств. Эта виртуальная функция будет получать управление при посылке окну любого сообщения;

· по аналогии с предыдущим случаем перекрыть виртуальную функцию DefWindowProc(). Эта функция будет получать управление в том случае, если сообщение не было обработано ни обработчиком сообщения, ни оконной процедурой;

· определить свое собственное сообщение и «вручную» создать для него обработчик. Естественно, что таких сообщений может быть произвольное число;

· наконец, можно в какой-либо своей функции организовать цикл обработки сообщений. Этот способ страдает тем недостатком, что попадать в этот цикл мы будем только при выполнении данной функции, что сильно ограничивает его возможности.

 

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

BOOL CWnd::PostMessage(UINT message, WPARAM wParam = 0,

LPARAM lParam = 0);

BOOL PostMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);

BOOL PostThreadMessage(DWORD idThread, UINT Msg, WPARAM wParam,

LPARAM lParam);

 

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

Первая функция является член-функцией класса CWnd и поэтому среди ее параметров нет дескриптора окна, в которое отправляется сообщение. Следовательно, сообщение можно принять только в функциях данного класса и данного потока. С помощью этой функции можно отправить сообщение и другому окну, но для этого надо иметь указатель на это окно (объект класса). 

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

Третья функция также является глобальной и позволяет отправить сообщение в очередь сообщений другого потока с дескриптором idThread. Естественно, что если в качестве дескриптора потока указать NULL, то сообщение можно будет принять только в методах того объекта, из которого оно отправлено.

Для отправки сообщений можно использовать также и функции:

LRESULT CWnd::SendMessage(UINT message, WPARAM wParam = 0,

LPARAM lParam = 0 );

LRESULT SendMessage(HWND hWnd, UINT message, WPARAM wParam, 

 LPARAM lParam);

   

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

Пример отправки и обработки сообщений проиллюстрирован в проекте MessageHandling. Этот проект представляет собой приложение с графическим интерфейсом, с архитектурой документ-вид. В приложение включено также диалоговое окно (класс CComputeDlg), приведенное на рис. 1.

 

Рис. 1. Диалоговое окно приложения MessageHandling

 

Диалоговое окно вызывается по нажатию ЛКМ в окне вида. Обработчик нажатия кнопки Start просто запускает сравнительно длинный цикл, в котором, в частности, иллюстрируется использование глобальной функции PeekMessage() для извлечения сообщений из очереди. В класс диалога добавлен также обработчик таймера, который управляет полосой продвижения Progress Control, отражая в ней ход выполнения цикла, запущенного по нажатию кнопки Start.

Для того чтобы в классе диалога CComputeDlg «добраться» до класса окна вида CMessageHandlingView, в приложении сделано следующее.

Во-первых, в файле CMessageHandlingView.cpp объявлена и инициализирована переменная указатель на окно вида:

CView * gpView;

CMessageHandlingView::CMessageHandlingView()

{

  gpView=this;

}

 

Во-вторых, в файле ComputeDlg.cpp эта переменная объявлена как внешняя (глобально, вне компонентных функций класса): 

extern CView *gpView;

 

После этого в любом методе класса диалога мы может пользоваться gpView как указателем на окно вида.

В файле ComputeDlg.h объявлены две константы – идентификаторы пользовательских сообщений:

const UINT MyMsgInsideDlg=WM_APP;

const UINT MyMsgToFrame=MyMsgInsideDlg+1;

 

В класс вида CMessageHandlingView «вручную» добавлен обработчик сообщения MyMsgToFrame следующим образом. В карту сообщений (файл MessageHandlingView.cpp) добавлена строка, связывающая идентификатор сообщения с функцией его обработки (выделено полужирным шрифтом):

BEGIN_MESSAGE_MAP(CMessageHandlingView, CView)

  //{{AFX_MSG_MAP(CMessageHandlingView)

  ON_WM_LBUTTONDOWN()

  //}}AFX_MSG_MAP

  ON_MESSAGE(MyMsgToFrame, OnMesFromDialog)

END_MESSAGE_MAP()

 

В карту сообщений того же файла MessageHandlingView.cpp добавлена реализация обработчика:

LRESULT CMessageHandlingView::OnMesFromDialog(WPARAM wParam,

LPARAM lParam)

{

  // см. текст в файле

}

 

И, наконец, в файл MessageHandlingView.h добавлен прототип этого обработчика:

// Generated message map functions

protected:

//{{AFX_MSG(CMessageHandlingView)

afx_msg void OnLButtonDown(UINT nFlags, CPoint point);

afx_msg LRESULT OnMesFromDialog(WPARAM wParam, LPARAM lParam);

//}}AFX_MSG

DECLARE_MESSAGE_MAP()

 

В проект включены файлы MsgNames.h и MsgNames.cpp, в которых объявлена и реализована функция GetMessageName(). Эта функция предназначена исключительно для целей отладки и иллюстрации и она по номеру сообщения возвращает его (символьный) идентификатор. В этих же файлах реализована функция WCharToChar(), возвращающая строку в кодировке MS DOS преобразующая строку типа CString в строку типа char*, которая используется в качестве параметра макроса TRACE.

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

Об обработке команд

Когда пользователь выполняет команду меню приложения или нажимает кнопку на панели инструментов, Windows вызывает обработчик этой команды, который и выполняет необходимые действия. Но, кроме того, уже в классе CCmdTarget, который является родительским для классов CView, CWinApp, CDocument, CWnd и CFrameWnd, определена виртуальная функция

BOOL CCmdTarget::OnCmdMsg(UINT nID, int nCode, void* pExtra,

              AFX_CMDHANDLERINFO* pHandlerInfo);

 

которая вызывается перед выполнением любого «персонального» обработчика этой команды.

Если Вы хотите выполнить какие-либо свои действия до выполнения обработчика команды, перекройте эту функцию в классе окна-рамки или окна вида. Первый параметр этой функции nID является идентификатором команды или управляющего элемента, например, кнопки. Узнать идентификаторы команд можно из ресурса таблица строк (String table на вкладке Resource view), где хранятся не только идентификаторы команд, но и ресурсные строки приложения, подсказки для команд меню и кнопок, отображаемые в строке статуса, а также всплывающие подсказки (tool tips). Этот файл можно редактировать и нужно сохранять. Приложение может иметь произвольное число таблиц строк, но в файле ресурса сохраняется только одна. Если необходимо разработать приложение, использующее несколько языков, то необходимо для каждого языка разработать свою таблицу строк и каждую из них разместить в отдельной DLL.

Заметьте, что идентификаторы тех кнопок на панели инструментов, которые были созданы мастером (например, это кнопка открытия файла), хранятся именно в таблице строк, а идентификаторы кнопок, которые Вы размещаете, например, в диалоговом окне, хранятся в файле Resource.h.

В приложении MessageHandling перекрыта обсуждаемая функция в классе окна-рамки, окне вида и диалоговом окне. Единственное, что делают перекрытые функции – это вывод сообщения в строку статуса.

 










Последнее изменение этой страницы: 2018-04-12; просмотров: 327.

stydopedya.ru не претендует на авторское право материалов, которые вылажены, но предоставляет бесплатный доступ к ним. В случае нарушения авторского права или персональных данных напишите сюда...