|
|
(не показано 11 промежуточных версий 2 участников) |
Строка 1: |
Строка 1: |
− | ===Как сделать обработчик сообщения для нескольких контролов (элементов управления) сразу?===
| + | #REDIRECT [[FAQ:WinAPI_VCPP]] |
− | | + | |
− | Без помощи визарда (Wizard) это можно сделать переопределением виртуальной функции OnCommand():
| + | |
− | | + | |
− | <syntaxhighlight lang="cpp">
| + | |
− | BOOL CMyDialog::OnCommand(WPARAM wParam, LPARAM lParam)
| + | |
− | {
| + | |
− | WORD wMess = (wParam >> 16); //командное сообщение
| + | |
− | int nID = (int)(wParam &0x0000ffff)); //ID контрола
| + | |
− | HWND hW = (HWND)lParam; //хендл контрола
| + | |
− |
| + | |
− | // смотрим, какой контрол
| + | |
− | switch (nID)
| + | |
− | {
| + | |
− | //кнопки
| + | |
− | case ID_BN1:
| + | |
− | case ID_BN2:
| + | |
− | case ID_BN3:
| + | |
− | {
| + | |
− | //смотрим, какое сообщение
| + | |
− | switch (wMess)
| + | |
− | {
| + | |
− | case BN_CLICKED: { ... } break;
| + | |
− | }
| + | |
− | }
| + | |
− | break;
| + | |
− |
| + | |
− | // едиты
| + | |
− | case ID_ED1:
| + | |
− | case ID_ED2:
| + | |
− | case ID_ED3:
| + | |
− | case ID_ED4:
| + | |
− | {
| + | |
− | // смотрим, какое сообщение
| + | |
− | switch (wMess)
| + | |
− | {
| + | |
− | case EN_CHANGE: { ... } break;
| + | |
− | case EN_KILLFOCUS: { ... } break;
| + | |
− | }
| + | |
− | }
| + | |
− | break;
| + | |
− | }
| + | |
− |
| + | |
− | return CDialog::OnCommand(wParam, lParam);
| + | |
− | }
| + | |
− | </syntaxhighlight>
| + | |
− | | + | |
− | ===Когда я запускаю программу, все надписи на русском языке теряются - показываются вопросики. Что делать?===
| + | |
− | | + | |
− | Лечится проблема так: непосредственно после создания проекта открываем дерево ресурсов и в свойствах каждого элемента дерева ставим язык Russian. Если этого не сделать сразу, то все русские буквы при компиляции ресурсов потеряются.
| + | |
− | | + | |
− | ===Как в проекте VC6 MFC программно получить путь, откуда был запущен экзешник (исполняемый модуль) самой программы?===
| + | |
− | | + | |
− | Нужно использовать функцию GetModuleFileName():
| + | |
− | | + | |
− | <syntaxhighlight>
| + | |
− | TCHAR pszFileName[MAX_PATH];
| + | |
− | pszFileName[0] = 0;
| + | |
− | | + | |
− | GetModuleFileName(NULL, pszFileName, MAX_PATH);
| + | |
− | CString stModulePath = pszFileName;
| + | |
− | | + | |
− | // ищем первый слеш с конца и удаляем
| + | |
− | // его вместе с именем файла EXE
| + | |
− | int nEnd = stModulePath.ReverseFind('\\');
| + | |
− | stModulePath.Delete(nEnd, stModulePath.GetLength() - nEnd);
| + | |
− | | + | |
− | // stModulePath - содержит путь
| + | |
− | </syntaxhighlight>
| + | |
− | | + | |
− | ===Как получить доступ к элементам управления панели CReBar, принадлежащей классу MainFrame?===
| + | |
− | | + | |
− | <syntaxhighlight lang="cpp">
| + | |
− | #include "MainFrm.h" | + | |
− | | + | |
− | // CMyApp - класс вашего приложения. theApp - глобальная переменная,
| + | |
− | // поэтому для доступа к ней используем extern
| + | |
− | | + | |
− | extern CMyApp theApp;
| + | |
− | | + | |
− | void CMyView::F()
| + | |
− | {
| + | |
− | // Получаем главное окно приложения в любом месте программы
| + | |
− | CMainFrame* pMainFrame = (CMainFrame*)(theApp.m_pMainWnd);
| + | |
− | | + | |
− | pMainFrame->m_wndDlgBar ....; // Делаем что хотим
| + | |
− | ...
| + | |
− | }
| + | |
− | </syntaxhighlight>
| + | |
− | | + | |
− | ===Как загрузить и показать один из стандартных курсоров?===
| + | |
− | | + | |
− | <syntaxhighlight lang="cpp">
| + | |
− | HCURSOR hCursor;
| + | |
− | | + | |
− | hCursor = AfxGetApp()->LoadStandardCursor(IDC_UPARROW);
| + | |
− | | + | |
− | if (hCursor)
| + | |
− | SetCursor(hCursor);
| + | |
− | </syntaxhighlight>
| + | |
− | | + | |
− | Идентификаторы стандартных курсоров:
| + | |
− | * IDC_ARROW
| + | |
− | * IDC_IBEAM
| + | |
− | * IDC_WAIT
| + | |
− | * IDC_CROSS
| + | |
− | * IDC_UPARROW
| + | |
− | * IDC_SIZENWSE
| + | |
− | * IDC_SIZENESW
| + | |
− | * IDC_SIZEWE
| + | |
− | * IDC_SIZENS
| + | |
− | * IDC_SIZEALL
| + | |
− | | + | |
− | ===Как запретить пользователю закрыть программу нажатием на кнопку с крестиком?===
| + | |
− | | + | |
− | Для этого нужно добавить обработчик сообщения WM_CLOSE (функция OnClose() в MFC) в главное окно программы. Для диалоговых приложений такое окно - это главный диалог, для одно- и много-документных приложений - это CMainFrame.
| + | |
− | | + | |
− | <syntaxhighlight lang="cpp">
| + | |
− | void CMainFrame::OnClose()
| + | |
− | {
| + | |
− | if (....)
| + | |
− | {
| + | |
− | // не разрешаем закрыть
| + | |
− | return;
| + | |
− | }
| + | |
− |
| + | |
− | CFrameWnd::OnClose();
| + | |
− | }
| + | |
− | </syntaxhighlight>
| + | |
− | | + | |
− | ===Как создать на диалоге группу элементов "RadioButton" и как задать порядок их обхода клавишей Tab?===
| + | |
− | | + | |
− | Последовательность действий следующая:
| + | |
− | # Помещаем на форму нужное количество элементов RadioButton;
| + | |
− | # У первого элемента из группы ставим свойство Group, у остальных в группе - убираем это свойство;
| + | |
− | # Порядок обхода (Tab Order) задаётся так: нажимаем Ctrl+D (загораются номера таб-порядка). Затем щёлкаем элементы в группе в таком порядке, который требуется.
| + | |
− | | + | |
− | ===Где лучше устанавливать начальные значения элемента CComboBox?===
| + | |
− | | + | |
− | Это можно сделать парой способов:
| + | |
− | # В редакторе форм - открыть свойства элемента CComboBox, "данные" (для ввода очередной строки данныхых нажать Ctrl+Enter);
| + | |
− | # В функции OnInitDialog (в случае диалога) или в функции OnInitialUpdate (для CView).
| + | |
− | | + | |
− | ===Как перевести RichEdit в режим замены символов?===
| + | |
− | | + | |
− | Имеется несколько способов.
| + | |
− | | + | |
− | 1. Программно, если известен хендл элемента (hWnd), это делается так:
| + | |
− | | + | |
− | <syntaxhighlight lang="cpp">
| + | |
− | // помещаем в конец очереди сообющений контрола сообщение WM_KEYDOWN
| + | |
− | // с нужными параметрами
| + | |
− | ::PostMessage(hWnd, WM_KEYDOWN, VK_INSERT, 1);
| + | |
− | </syntaxhighlight>
| + | |
− | | + | |
− | 2. Пользователь во время работы может нажать клавишу Insert.
| + | |
− | | + | |
− | ===Как вызвать метод главного окна (если используется класс CMainFrame) из любого места программы?===
| + | |
− | | + | |
− | Это можно сделать так:
| + | |
− | | + | |
− | <syntaxhighlight lang="cpp">
| + | |
− | AfxGetApp()->m_pMainWnd-> ...;
| + | |
− | </syntaxhighlight>
| + | |
− | | + | |
− | или так
| + | |
− | <syntaxhighlight lang="cpp">
| + | |
− | extern CMyAppXXX theApp; // здесь CMyAppXXX - название класса вашего приложения
| + | |
− | theApp.m_pMainWnd-> ...;
| + | |
− | </syntaxhighlight>
| + | |
− | | + | |
− | ===Как запретить появление полос прокруток на форме класса CFormView, когда пользователь делает размер главного окна меньше размера формы ?===
| + | |
− | | + | |
− | Это можно сделать методом SetScaleToFitSize():
| + | |
− | | + | |
− | <syntaxhighlight lang="cpp">
| + | |
− | void CMyView::OnInitialUpdate()
| + | |
− | {
| + | |
− | CFormView::OnInitialUpdate();
| + | |
− | | + | |
− | // скрываем полосы прокрутки
| + | |
− | GetParentFrame()->RecalcLayout();
| + | |
− | ResizeParentToFit(); // это уберёт полосы прокрутки со вьюхи
| + | |
− | SIZE s = {0, 0};
| + | |
− | SetScaleToFitSize(s);
| + | |
− | //далее вызовется обработчик OnPaint(), о котором ниже
| + | |
− | | + | |
− | // ...
| + | |
− | }
| + | |
− | </syntaxhighlight>
| + | |
− | | + | |
− | Также обратите внимание на одну особенность: в отладочной версии программы вызов OnPaint после выполнения SetScaleToFitSize() с параметром s = {0, 0} происходит с ошибкой (программа при этом завершается).
| + | |
− | Обходится это так:
| + | |
− | | + | |
− | <syntaxhighlight lang="cpp">
| + | |
− | void CMyView::OnPaint()
| + | |
− | {
| + | |
− | #ifdef _DEBUG
| + | |
− | CPaintDC dc(this);
| + | |
− | #else
| + | |
− | CFormView::OnPaint();
| + | |
− | #endif
| + | |
− | }
| + | |
− | </syntaxhighlight>
| + | |
− | | + | |
− | Или так:
| + | |
− | | + | |
− | <syntaxhighlight lang="cpp">
| + | |
− | void CMyView::OnPaint()
| + | |
− | {
| + | |
− | CPaintDC dc(this);
| + | |
− | }
| + | |
− | </syntaxhighlight>
| + | |
− | | + | |
− | ===Как нарисовать прямоугольник с вертикальным цветовым градиентом?===
| + | |
− | | + | |
− | Нужно задасть граничные значения цветов, затем равномерно нарисовать несколько прямоугольников постепенно меняющегося цвета. Количество прямоугольников, а, значит - плавность градиента, задаётся точностью.
| + | |
− | | + | |
− | Пример:
| + | |
− | | + | |
− | <syntaxhighlight lang="cpp">
| + | |
− | // вертикальный градиент
| + | |
− | // pdc - указатель на контекст устройства
| + | |
− | // pSize - указатель на структуру
| + | |
− | // SIZE с размером прямоугольника
| + | |
− | // dwdColor1, dwdColor2 - начальный и конечный цвет
| + | |
− | // bySteps - количество шагов градиента (1...255)
| + | |
− | void sFillVertGradientRect(CDC* pdc, SIZE *pSize, COLORREF dwdColor1,
| + | |
− | COLORREF dwdColor2, BYTE bySteps)
| + | |
− | {
| + | |
− | WORD i;
| + | |
− | long W, H, x1, x2;
| + | |
− | BYTE R1, G1, B1, R2, G2, B2;
| + | |
− | float dh, dR, dG, dB, y1, y2, Rc, Gc, Bc;
| + | |
− |
| + | |
− | if (!bySteps)
| + | |
− | bySteps = 1;
| + | |
− | | + | |
− | //ширина и высота
| + | |
− | W = pSize->cx;
| + | |
− | H = pSize->cy;
| + | |
− |
| + | |
− | // раскладываем цвета на их составляющие
| + | |
− | // для удобства дальнейших вычислений
| + | |
− | R1 = (BYTE)((dwdColor1 & 0x000000ff));
| + | |
− | G1 = (BYTE)((dwdColor1 & 0x0000ff00) >> 8);
| + | |
− | B1 = (BYTE)((dwdColor1 & 0x00ff0000) >> 16);
| + | |
− | R2 = (BYTE)((dwdColor2 & 0x000000ff));
| + | |
− | G2 = (BYTE)((dwdColor2 & 0x0000ff00) >> 8);
| + | |
− | B2 = (BYTE)((dwdColor2 & 0x00ff0000) >> 16);
| + | |
− |
| + | |
− | // высота разноцветных прямоугольников
| + | |
− | dh = ((float)H) / ((float)bySteps);
| + | |
− | | + | |
− | // величина шагов составляющих цветов
| + | |
− | dR = (((float)R2) - ((float)R1)) / ((float)bySteps);
| + | |
− | dG = (((float)G2) - ((float)G1)) / ((float)bySteps);
| + | |
− | dB = (((float)B2) - ((float)B1)) / ((float)bySteps);
| + | |
− | | + | |
− | // выводим прямоугольники
| + | |
− | x1 = 0;
| + | |
− | x2 = W;
| + | |
− | y1 = 0;
| + | |
− | y2 = dh;
| + | |
− | Rc = R1;
| + | |
− | Gc = G1;
| + | |
− | Bc = B1;
| + | |
− | | + | |
− | for (i = 0; i < bySteps; i++)
| + | |
− | {
| + | |
− | // текущий цвет
| + | |
− | pdc->FillSolidRect(
| + | |
− | x1,
| + | |
− | (int)y1, x2 - x1,
| + | |
− | (int)(y2 - y1),
| + | |
− | RGB((BYTE)Rc,
| + | |
− | (BYTE)Gc,
| + | |
− | (BYTE)Bc)
| + | |
− | );
| + | |
− | | + | |
− | // следующий цвет и координаты
| + | |
− | y1 += dh;
| + | |
− | y2 += dh;
| + | |
− | Rc += dR;
| + | |
− | Gc += dG;
| + | |
− | Bc += dB;
| + | |
− | }
| + | |
− | }
| + | |
− | </syntaxhighlight>
| + | |
− | | + | |
− | Пример вызова:
| + | |
− | | + | |
− | <syntaxhighlight lang="cpp">
| + | |
− | SIZE Size = {100, 100};
| + | |
− | sFillGradientRect(&dc, &Size, RGB(200, 0, 0), RGB(0, 200, 0), 10);
| + | |
− | </syntaxhighlight>
| + | |
− | | + | |
− | ===Как под Windows отслеживать изменение файла?===
| + | |
− | | + | |
− | Нужно использовать функции:
| + | |
− | * FindFirstChangeNotification
| + | |
− | * FindNextChangeNotification
| + | |
− | * FindCloseChangeNotification
| + | |
− | | + | |
− | (подробности - в MSDN)
| + | |
− | | + | |
− | ===Как конвертировать массив char[] в CString?===
| + | |
− | | + | |
− | Это можно сделать методами самого класса CString:
| + | |
− | | + | |
− | <syntaxhighlight lang="cpp">
| + | |
− | char buf[] = "text";
| + | |
− | // строка должна обязательно заканчиваться нулём!!!
| + | |
− | | + | |
− | // конвертируем так
| + | |
− | CString txt(buf);
| + | |
− | | + | |
− | // или так
| + | |
− | CString txt;
| + | |
− | txt = buf;
| + | |
− | </syntaxhighlight>
| + | |
− | | + | |
− | ===Как зарезервировать в CString буфер нужной длины?===
| + | |
− | | + | |
− | Это можно сделать при помощи методов класса:
| + | |
− | | + | |
− | <syntaxhighlight lang="cpp">
| + | |
− | CString::GetBuffer(...);
| + | |
− | </syntaxhighlight>
| + | |
− | | + | |
− | и
| + | |
− | | + | |
− | <syntaxhighlight lang="cpp">
| + | |
− | CString::GetBufferSetLength(...);
| + | |
− | </syntaxhighlight>
| + | |
− | | + | |
− | Разница между GetBuffer(nLen) и GetBufferSetLength(nLen) в том, что первый возвращает строку ''не меньше'' заданной длины, а второй возвращает строку, точно равную заданной длине. Обе могут перераспределять память, если необходимо. В обычных случаях лучше использовать GetBuffer.
| + | |
− | | + | |
− | Если содержимое буфера менялось, то после этого нужно вызвать CString::ReleaseBuffer с указанием новой длины. Значение -1 в вызове CString::ReleaseBuffer означает, что длина строки будет вычислена автоматом (функцией strlen) в методе. "-1" удобно использовать, если известно, что строка заканчивается нулем.
| + | |
− | | + | |
− | ===Как передать больше одного параметр в процедуру потока?===
| + | |
− | | + | |
− | Для этого нужно определить структуру со всеми параметрами (или указателями на них), которые нужно передать. А в процедуру потока передать лишь указатель на заполненную структуру.
| + | |
− | | + | |
− | Пример:
| + | |
− | | + | |
− | <syntaxhighlight lang="cpp">
| + | |
− | struct mystr
| + | |
− | {
| + | |
− | CEdit* pEd;
| + | |
− | CDialog* pDlg;
| + | |
− | DWORD* pdwd;
| + | |
− | int *pn;
| + | |
− | };
| + | |
− | </syntaxhighlight>
| + | |
− | | + | |
− | запуск потока:
| + | |
− | | + | |
− | <syntaxhighlight lang="cpp">
| + | |
− | mystr *pparam = new mystr; // экземпляр не должен быть временным!!!
| + | |
− | | + | |
− | memset(pparam, 0, sizeof(*pparam));
| + | |
− | pparam->pEd = ...;
| + | |
− | pparam->pdwd = ...;
| + | |
− | | + | |
− | ::AfxBeginThread(thread, pparam);
| + | |
− | // тут экземпляр *(pparam) уже нельзя использовать в нашем примере, так как
| + | |
− | // он у нас удалиля в процедуре потока
| + | |
− | </syntaxhighlight>
| + | |
− | | + | |
− | Процедура потока:
| + | |
− | | + | |
− | <syntaxhighlight lang="cpp">
| + | |
− | // поток:
| + | |
− | UINT threadLoader(LPVOID pParam)
| + | |
− | {
| + | |
− | // копируем данные из структуры в локальную структуру
| + | |
− | mystr data = *((mystr*)pParam);
| + | |
− | // освобождаем память временной структуры
| + | |
− | delete ((mystr*)pParam);
| + | |
− | pParam = 0;
| + | |
− | | + | |
− | ...
| + | |
− | data.pDlg->...;
| + | |
− | (*data.pdwd) = ...;
| + | |
− | ...
| + | |
− | }
| + | |
− | </syntaxhighlight>
| + | |
− | | + | |
− | ===Как убрать главное меню из окна CMainFrame?===
| + | |
− | | + | |
− | Это можно сделать в виртуальной функции PreCreateWindow:
| + | |
− | | + | |
− | <syntaxhighlight lang="cpp">
| + | |
− | BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
| + | |
− | {
| + | |
− | // обнуляем хендл меню до вызова CFrameWnd::PreCreateWindow
| + | |
− | cs.hMenu = 0;
| + | |
− |
| + | |
− | if (!CFrameWnd::PreCreateWindow(cs))
| + | |
− | return FALSE;
| + | |
− | | + | |
− | ...
| + | |
− | }
| + | |
− | </syntaxhighlight>
| + | |
− | | + | |
− | ===Как работающая программа может определить, что пользователь завершает работу Windows?===
| + | |
− | | + | |
− | Когда пользователь завершает работу Windows, всем процессам посылается сообщение WM_QUERYENDSESSION. Его нужно отловить в обработчике OnQueryEndSession(). Если вернуть из обработчика значение 0, то Windows продолжит работу.
| + | |
− | | + | |
− | ===Как сделать всплывающую подсказку для класса CWnd и классов, от него производных?===
| + | |
− | | + | |
− | Допустим, имеется диалог класса CMyDlg. Делаем подсказки для элементов управления (для CStatic контролов не забудьте поставить свойство Notify):
| + | |
− | | + | |
− | <syntaxhighlight lang="cpp">
| + | |
− | class CMyDlg:puplic CDialog
| + | |
− | {
| + | |
− | CToolTipCtrl m_ToolTip; // мембер класса CMyDlg
| + | |
− | };
| + | |
− | | + | |
− | // массив, в котором перечислены идентификаторы
| + | |
− | // контролов и тексты подсказок к ним
| + | |
− | struct
| + | |
− | {
| + | |
− | int ID;
| + | |
− | const char* pch;
| + | |
− | }
| + | |
− | m_a_Tips[] =
| + | |
− | {
| + | |
− | {IDC_BUTTON1, "Кнопка"},
| + | |
− | {IDC_STATIC1, "Текст"},
| + | |
− | {0, 0} // признак конца массива
| + | |
− | };
| + | |
− | | + | |
− | // в инициализации диалога (хотя, в принципе,
| + | |
− | // можно и не тут) создаём и привязываем подсказки
| + | |
− | BOOL CMyDlg::OnInitDialog()
| + | |
− | {
| + | |
− | CDialog::OnInitDialog();
| + | |
− | | + | |
− | // создаём элемент TOOLTIP
| + | |
− | m_ToolTip.Create(this);
| + | |
− | | + | |
− | // Привязка подсказок
| + | |
− | for (int i = 0; m_a_Tips[i].ID; i++)
| + | |
− | {
| + | |
− | m_ToolTip.AddTool(
| + | |
− | GetDlgItem(m_a_Tips[i].ID),
| + | |
− | m_a_Tips[i].pch
| + | |
− | );
| + | |
− | }
| + | |
− |
| + | |
− | // включаем показ подсказок
| + | |
− | m_ToolTip.Activate(1);
| + | |
− | | + | |
− | ...
| + | |
− | }
| + | |
− | | + | |
− | // для того, чтобы подсказки отображались как реакция на движение
| + | |
− | // курсора мыши, транслируем получаемые окнами сообщения в
| + | |
− | // виртуальной PreTranslateMessage()
| + | |
− | BOOL CMyDlg::PreTranslateMessage(MSG* pMsg)
| + | |
− | {
| + | |
− | // транслируем
| + | |
− | if (m_ToolTip.m_hWnd)
| + | |
− | m_ToolTip.RelayEvent(pMsg);
| + | |
− | | + | |
− | return CDialog::PreTranslateMessage(pMsg);
| + | |
− | }
| + | |
− | </syntaxhighlight>
| + | |
− | | + | |
− | ===Как в отладчике Visual С++ просмотреть содержимое std::vector<string>?===
| + | |
− | | + | |
− | Это можно сделать так: к примеру, имеется:
| + | |
− | | + | |
− | <syntaxhighlight lang="cpp">
| + | |
− | std::vector<string> V;
| + | |
− | </syntaxhighlight>
| + | |
− | | + | |
− | В режиме отладки открываем окно "Watch" (ALT+3) и вставляем выражения:
| + | |
− | * "V._Myfirst" - будет показан первый элемент V
| + | |
− | * "V._Myfirst+1" - второй элемент V
| + | |
− | и т.д.
| + | |
− | | + | |
− | ===Как при выводе текста на контекст устройства определить ширину и высоту выведенных символов текста в пикселах?===
| + | |
− | | + | |
− | Для этого нужно использовать процедуру API:
| + | |
− | | + | |
− | <syntaxhighlight lang="cpp">
| + | |
− | BOOL GetTextExtentPoint32(
| + | |
− | HDC hdc, // хендл контекста
| + | |
− | LPCTSTR lpString, // выводимая строка
| + | |
− | int cbString, // длина строки в символах
| + | |
− | LPSIZE lpSize // указатель на структуру SIZE, куда будут помещены размеры
| + | |
− | );
| + | |
− | </syntaxhighlight>
| + | |
− | | + | |
− | Функция вычисляет ширину строки символов, оканчивающейся нулём. Поэтому, чтобы вычислить габариты многострочного текста, текст, в начале, необходимо разбить на строки, разделённые символами '\r' и '\n', и определить ширину и высоту каждой отдельно взятой строки.
| + | |
− | | + | |
− | [[Category:FAQ]]
| + | |