FAQ:WinAPI, VCPP Part 2

Материал из Весельчак У
Версия от 20:01, 2 ноября 2008; RXL (обсуждение | вклад) (Удаление (Как загрузить и показать один из стандартных курсоров?))

Перейти к: навигация, поиск






Содержание

Как запретить пользователю закрыть программу нажатием на кнопку с крестиком?

Для этого нужно добавить обработчик сообщения WM_CLOSE (функция OnClose() в MFC) в главное окно программы. Для диалоговых приложений такое окно - это главный диалог, для одно- и многодокументных приложений - это CMainFrame.

void CMainFrame::OnClose()
{
	if(...)
	{
		// не разрешаем закрыть
		return;
	}
 
	CFrameWnd::OnClose();
}

Как создать на диалоге группу элементов "RadioButton" и как задать порядок их обхода клавишей Tab?

Последовательность действий следующая:

  1. Помещаем на форму нужное количество элементов RadioButton;
  2. У первого элемента из группы ставим свойство Group, у остальных в группе - убираем это свойство;
  3. Порядок обхода (Tab Order) задаётся так: нажимаем Ctrl+D (загораются номера таб-порядка). Затем щёлкаем элементы в группе в таком порядке, который требуется.

Где лучше устанавливать начальные значения элемента CComboBox?

Это можно сделать парой способов:

  1. В редакторе форм - открыть свойства элемента CComboBox, "данные" (для ввода очередной строки данныхых нажать Ctrl+Enter);
  2. В функции OnInitDialog (в случае диалога) или в функции OnInitialUpdate (для CView).

Как перевести RichEdit в режим замены символов?

Имеется несколько способов.

1. Программно, если известен хендл элемента (hWnd), это делается так:

// помещаем сообщение WM_KEYDOWN с нужными параметрами 
//в конец очереди сообщений элемента управления 
::PostMessage(hWnd, WM_KEYDOWN, VK_INSERT, 1);

2. Пользователь во время работы может нажать клавишу Insert.

Как вызвать метод главного окна (если используется класс CMainFrame) из любого места программы?

Это можно сделать так:

AfxGetApp()->m_pMainWnd->...;

или так

extern CMyAppXXX theApp; // здесь CMyAppXXX - название класса вашего приложения
theApp.m_pMainWnd->...;

Как запретить появление полос прокруток на форме класса CFormView, когда пользователь делает размер главного окна меньше размера формы?

Это можно сделать методом SetScaleToFitSize():

void CMyView::OnInitialUpdate()
{
	CFormView::OnInitialUpdate();
 
	// скрываем полосы прокрутки
	GetParentFrame()->RecalcLayout();
 
	// это уберёт полосы прокрутки со вьюхи
	ResizeParentToFit();
 
	SIZE s={0, 0};
	SetScaleToFitSize(s);
	// далее вызовется обработчик OnPaint(), о котором ниже
 
	//...
}

Также обратите внимание на одну особенность: в отладочной версии программы вызов OnPaint после выполнения SetScaleToFitSize() с параметром s={0, 0} происходит с ошибкой (программа при этом завершается). Обходится это так:

void CMyView::OnPaint()
{
	#ifdef _DEBUG
		CPaintDC dc(this);
	#else
		CFormView::OnPaint();
	#endif
}

Или так:

void CMyView::OnPaint()
{
	CPaintDC dc(this);
}

Как нарисовать прямоугольник с вертикальным цветовым градиентом?

Нужно задать граничные значения цветов, затем равномерно нарисовать несколько прямоугольников постепенно меняющегося цвета. Количество прямоугольников, а, значит - плавность градиента, задаётся точностью.

Пример:

// вертикальный градиент
// pdc - указатель на контекст устройства
// pSize - указатель на структуру
// SIZE с размером прямоугольника
// dwdColor1, dwdColor2 - начальный и конечный цвет
// bySteps - количество шагов градиента (1...255)
void sFillVertGradientRect(CDC* pdc, SIZE *pSize, COLORREF dwdColor1,
	COLORREF dwdColor2, BYTE bySteps)
{
	WORD i=0;
	long W=0;
	long H=0;
	long x1=0;
	long x2=0;
 
	BYTE R1=0;
	BYTE G1=0;
	BYTE B1=0;
	BYTE R2=0;
	BYTE G2=0;
	BYTE B2=0;
 
	float dh=0;
	float dR=0;
	float dG=0;
	float dB=0;
	float y1=0;
	float y2=0;
	float Rc=0;
	float Gc=0;
	float Bc=0;
 
	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 - R1) / (float)bySteps;
	dG=(float)(G2 - G1) / (float)bySteps;
	dB=(float)(B2 - 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;
	}
}

Пример вызова:

SIZE Size={100, 100};
sFillGradientRect(&dc, &Size, RGB(200,0,0), RGB(0,200,0), 10);

Как под Windows отслеживать изменение файла?

Нужно использовать функции:

  • FindFirstChangeNotification
  • FindNextChangeNotification
  • FindCloseChangeNotification

(подробности - в MSDN)

Как конвертировать массив char[] в CString?

Это можно сделать методами самого класса CString:

char buf[]="text";
// строка должна обязательно заканчиваться нулём!!!
 
// конвертируем так
CString txt(buf);
 
// или так
CString txt;
txt=buf;

Как зарезервировать в CString буфер нужной длины?

Это можно сделать при помощи методов класса:

   CString::GetBuffer(...);

и

   CString::GetBufferSetLength(...);

Разница между GetBuffer(nLen) и GetBufferSetLength(nLen) в том, что первый возвращает строку не меньше заданной длины, а второй возвращает строку, точно равную заданной длине. Обе могут перераспределять память, если необходимо. В обычных случаях лучше использовать GetBuffer.

Если содержимое буфера менялось, то после этого нужно вызвать CString::ReleaseBuffer с указанием новой длины. Значение -1 в вызове CString::ReleaseBuffer означает, что длина строки будет вычислена автоматом (функцией strlen) в методе. "-1" удобно использовать, если известно, что строка заканчивается нулем.

Как передать больше одного параметр в процедуру потока?

Для этого нужно определить структуру со всеми параметрами (или указателями на них), которые нужно передать. А в процедуру потока передать лишь указатель на заполненную структуру.

Пример:

struct mystr
{
	CEdit* pEd;
	CDialog* pDlg;
	DWORD* pdwd;
	int *pn;
};

запуск потока:

mystr *pparam=new mystr; // экземпляр не должен быть временным!!!
 
memset(pparam, 0, sizeof(*pparam));
pparam->pEd=...;
pparam->pdwd=...;
 
::AfxBeginThread(thread, pparam);
// тут экземпляр *(pparam) уже нельзя использовать в нашем примере, так как
// он у нас удалился в процедуре потока

Процедура потока:

// поток:
UINT threadLoader(LPVOID pParam)
{
	// копируем данные из структуры в локальную структуру
	mystr data=*((mystr*)pParam);
	// освобождаем память временной структуры
	delete ((mystr*)pParam);
	pParam=0;
 
	...
	data.pDlg->...;
	(*data.pdwd)=...;
	...
}

Как убрать главное меню из окна CMainFrame?

Это можно сделать в виртуальной функции PreCreateWindow:

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
	// обнуляем хендл меню до вызова CFrameWnd::PreCreateWindow
	cs.hMenu=0;
 
	if(!CFrameWnd::PreCreateWindow(cs))
	{
		return FALSE;
	}
 
	...
}

Как работающая программа может определить, что пользователь завершает работу Windows?

Когда пользователь завершает работу Windows, всем процессам посылается сообщение WM_QUERYENDSESSION. Его нужно отловить в обработчике OnQueryEndSession(). Если вернуть из обработчика значение 0, то Windows продолжит работу.

Как сделать всплывающую подсказку для класса CWnd и классов, от него производных?

Допустим, имеется диалог класса CMyDlg. Делаем подсказки для элементов управления (для элементов управления CStatic не забудьте поставить свойство Notify):

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);
}

Как в отладчике Visual С++ просмотреть содержимое std::vector<string>?

Это можно сделать так: к примеру, имеется:

std::vector<string> V;

В режиме отладки открываем окно "Watch" (ALT+3) и вставляем выражения:

  • "V._Myfirst" - будет показан первый элемент V
  • "V._Myfirst+1" - второй элемент V
  • и так далее

Как при выводе текста на контекст устройства определить ширину и высоту выведенных символов текста в пикселах?

Для этого нужно использовать процедуру API:

BOOL GetTextExtentPoint32(
	HDC hdc, // хендл контекста
	LPCTSTR lpString, // выводимая строка
	int cbString, // длина строки в символах
	LPSIZE lpSize // указатель на структуру SIZE, куда будут помещены размеры
);

Функция вычисляет ширину строки символов, оканчивающейся нулём. Поэтому, чтобы вычислить габариты многострочного текста, текст, в начале, необходимо разбить на строки, разделённые символами '\r' и '\n', и определить ширину и высоту каждой отдельно взятой строки.